Initial Bookings additions

pull/1/head
Rob Hedgpeth 2020-01-29 15:47:15 -08:00
rodzic 4a0c3aca4e
commit b28290fb0e
53 zmienionych plików z 17484 dodań i 0 usunięć

186
Bookings/README.md 100644
Wyświetl plik

@ -0,0 +1,186 @@
# Bookings
**Bookings** is a web application written in [ReactJS](https://reactjs.org) and [NodeJS](https://nodejs.org) that, backed by the power of the [MariaDB Node Connector](https://github.com/MariaDB/mariadb-connector-nodejs) and the [MariaDB X4 Platform](https://mariadb.com/resources/blog/deploying-mariadb-platform-x4/#smart), unleashes the power of [smart transactions](https://mariadb.com/resources/blog/introducing-mariadb-platform-x4-smart-transactions-and-cloud-native-storage/)!
<p align="center" spacing="10">
<img src="media/demo.gif" />
</p>
This `README` will walk you through the steps for getting this app up and running (locally) within minutes!
# Table of Contents
1. [Getting started with MariaDB and Hybrid Transactional-Analytical Processing](#overview)
1. [The Basics](#intro-mariadb)
2. [Deploying MariaDB HTAP](#installation)
3. [Create the schema](#create-schema)
4. [Load Flight data](#load-data)
5. [Setting up replication](#replication)
2. [Requirements to run the app](#requirements)
3. [Getting started with the app](#getting-started)
1. [Grab the code](#grab-code)
2. [Build the code](#build-code)
3. [Run the app](#run-app)
4. [Smart Transactions](#smart-transactions)
5. [Cross-Engine Queries](#cross-engine-queries)
6. [Support and Contribution](#support-contribution)
## Overview <a name="overview"></a>
### Introduction to MariaDB <a name="intro-mariadb"></a>
[MariaDB platform](https://mariadb.com/products/mariadb-platform/) unifies [MariaDB TX (transactions)](https://mariadb.com/products/mariadb-platform-transactional/) and [MariaDB AX (analytics)](https://mariadb.com/products/mariadb-platform-analytical/) so transactional applications can retain unlimited historical data and leverage powerful, real-time analytics in order to provide data-driven customers with more information, actionable insight and greater value – and businesses with endless ways to monetize data. It is the enterprise open source database for hybrid transactional/analytical processing at scale.
<p align="center">
<img src="media/platform.png" />
</p>
### Deploying MariaDB Hybrid Transactional-Analytical Processing (HTAP) <a name="installation"></a>
MariaDB Platform supports [Hybrid Transactional-Analytical Processing (HTAP)](https://mariadb.com/docs/deploy/htap/) through a combination of MariaDB Enterprise Server, MariaDB ColumnStore, and MariaDB MaxScale.
Here's a simple architecture diagram of MariaDB X4 Platform.
<p align="center" spacing="10">
<img src="media/x4.png" />
</p>
To deploy MariaDB HTAP check out the instructions [here](https://mariadb.com/docs/deploy/htap/).
### Create the schema <a name="create-schema"></a>
Execute [create.sql](schema/create.sql) within your instance of MariaDB X4.
### Loading Flights data into ColumnStore <a name="load-data"></a>
Instructions on retrieving and importing the flights dataset into a MariaDB ColumnStore database (within HTAP) can be [here](https://github.com/mariadb-corporation/mariadb-columnstore-samples/tree/master/flights). Please note that he scripts provided within that repository only targets data for the year 2019 (~7.5 million records).
**Note** If you'd like to retrieve data spanning from 1990 to 2020 (~182 million records) please use the following scripts in place of the ones provided [here](https://github.com/mariadb-corporation/mariadb-columnstore-samples/tree/master/flights).
* [get_flight_data.sh](data/get_flight_data.sh)
* [load_flights_data.sh](data/load_flights_data.sh)
Of course, you can also modify the scripts to fit your needs.
### Setting up HTAP Replication <a name="htap-replication"></a>
Using MariaDB Replication, MariaDB Enterprise Server replicates writes from InnoDB tables to the MariaDB ColumnStore tables, ensuring that the application can perform analytical processing on current data. Combining MariaDB Replication with MariaDB MaxScale configured as a Binlog Server, MariaDB Enterprise Server can host InnoDB and ColumnStore on the same Server.
This application uses replication on a single table called "Flights", which exists in the InnoDB and ColumnStore instances. In order to set up replication add the following to `/etc/my.cnf` for your HTAP instance.
```
[replication-filter]
type = filter
module = binlogfilter
match = /[.]flights/
rewrite_src = innodb
rewrite_dest = columnstore
```
For more information on configuring MariaDB HTAP please review the official [Enterprise Documentation](https://mariadb.com/docs/deploy/htap/#maxscale-configuration).
## Requirements to run the app <a name="requirements"></a>
This project assumes you have familiarity with building web applications using [ReactJS](https://reactjs.org) and [NodeJS](https://nodejs.org) technologies.
The following is required to run this application:
1. [Download and install MariaDB HTAP](https://mariadb.com/docs/deploy/htap/).
2. [Download and install NodeJS](https://nodejs.org/en/download/).
3. git (Optional) - this is required if you would prefer to pull the source code from GitHub repo.
- Create a [free github account](https://github.com/) if you dont already have one
- git can be downloaded from git-scm.org
## Getting started with the app <a name="getting-started"></a>
### Grab the code <a name="grab-code"></a>
Download this code directly or use [git](git-scm.org) (through CLI or a client) to retrieve the code.
### Configure the code <a name="configure-code"></a>
Configure the MariaDB connection by [adding an .env file to the Node.js project](https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/promise-api.md#security-consideration).
Example implementation:
```
DB_HOST=<host_address>
DB_PORT=<port_number>
DB_USER=<username>
DB_PASS=<password>
DB_NAME=<database>
```
### Build the code <a name="build-code"></a>
Once you have retrieved a copy of the code you're ready to build and run the project! However, before running the code it's important to point out that the application uses several Node Packages.
Executing the CLI command `npm install` within the [src](src) AND [client](src/client) folders will target the the relative `package.json` file and install all dependencies.
For more information on `npm install` check out the [Official Node.js Documentation](https://docs.npmjs.com/downloading-and-installing-packages-locally).
### Run the app <a name="run-app"></a>
Once you've pulled down the code and have verified that all of the required Node packages are installed you're ready to run the application! It's as easy as 1,2,3.
1. Using a command line interface (CLI) navigate to the `src` directory.
<p align="center">
<img src="media/cli_root.png" />
</p>
2. Run the command:
```bash
npm start
```
<p align="center">
<img src="media/npm_start.png" />
</p>
3. Open a browser window and navigate to http://localhost:3000.
<p align="center">
<img src="media/get_started.png" />
</p>
## Smart Transactions <a name="smart-transactions"></a>
At this point you're probably wondering, what are smart transactions?
At their core, smart transactions are the standard transactions that databases have been performing for decades – ultimately powering the online interactions weve become accustomed to. The difference with modern applications is the use of real-time analytics before, during and/or after these transactions.
**Pre-transaction**
This application uses real-time analytics before a flight is booked. Each flight ticket option contains information calculated from the historical records (average delay, average duration, flight score, etc.) within the `flights` table.
<p align="center">
<img src="media/flight_1.png" />
</p>
<p align="center">
<img src="media/flight_2.png" />
</p>
**Post-transaction**
This application also uses real-time analytics after a flight has been booked, and a trip has been created.
<p align="center">
<img src="media/trip_1.png" />
</p>
## Cross-Engine Queries <a name="cross-engine-queries"></a>
This application uses cross-engine queries to maximize the potentials of the MariaDB X4 Platform. Cross-engine querying is the ability to access, via MaxScale, both the transactional and analytics data within a single query.
<p align="center">
<img src="media/cross_engine.png" />
</p>
## Support and Contribution <a name="support-contribution"></a>
Thanks so much for taking a look at the Bookings app! As this is a very simple example, there's plenty of potential for customization. Please feel free to submit PR's to the project to include your modifications!
If you have any questions, comments, or would like to contribute to this or future projects like this please reach out to us directly at developers@mariadb.com or on [Twitter](https://twitter.com/mariadb).

Wyświetl plik

@ -0,0 +1,19 @@
#!/bin/bash
#
# This script will remotely invoke the bureau of transportation statistics web form to retrieve data by month:
# https://www.transtats.bts.gov/DL_SelectFields.asp?Table_ID=236&DB_Short_Name=On-Time
# for the specific columns listed in the SQL and utilized by the sample schema.
mkdir -p data
for y in {1990..2020}; do
for m in {1..12}; do
yyyymm="$y-$(printf %02d $m)"
echo "$yyyymm"
curl -L -o data.zip -d "sqlstr=+SELECT+YEAR%2CMONTH%2CDAY_OF_MONTH%2CDAY_OF_WEEK%2CFL_DATE%2CCARRIER%2CTAIL_NUM%2CFL_NUM%2CORIGIN%2CDEST%2CCRS_DEP_TIME%2CDEP_TIME%2CDEP_DELAY%2CTAXI_OUT%2CWHEELS_OFF%2CWHEELS_ON%2CTAXI_IN%2CCRS_ARR_TIME%2CARR_TIME%2CARR_DELAY%2CCANCELLED%2CCANCELLATION_CODE%2CDIVERTED%2CCRS_ELAPSED_TIME%2CACTUAL_ELAPSED_TIME%2CAIR_TIME%2CDISTANCE%2CCARRIER_DELAY%2CWEATHER_DELAY%2CNAS_DELAY%2CSECURITY_DELAY%2CLATE_AIRCRAFT_DELAY+FROM++T_ONTIME+WHERE+Month+%3D$m+AND+YEAR%3D$y" https://www.transtats.bts.gov/DownLoad_Table.asp?Table_ID=236
rm -f *.csv
unzip data.zip
rm -f data.zip
mv *.csv $yyyymm.csv
tail -n +2 $yyyymm.csv > data/$yyyymm.csv
rm -f $yyyymm.csv
done
done

Wyświetl plik

@ -0,0 +1,15 @@
#!/bin/bash
# check for argument, if so use as wildcard for file load match, otherwise load everything
DATA_DIR=$(readlink -f ./data)
filematch="*"
if [ $# -eq 1 ]
then
filematch="*$1*"
fi
# load the specified files under the data directory with the file pattern match
# here we use cpimport mode 2 to force processing at each PM node which has
# the advantage of this being runnable as a regular user with a root installation.
for f in $DATA_DIR/$filematch.csv; do
echo $f
/usr/bin/cpimport -m2 -s ',' -E '"' flights flights -l $f
done

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 156 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 91 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 622 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 82 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 107 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 249 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 739 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.2 MiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 211 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Wyświetl plik

@ -0,0 +1,113 @@
CREATE DATABASE travel;
-- Create syntax for TABLE 'airlines_idb'
CREATE TABLE `airlines_idb` (
`iata_code` char(2) DEFAULT NULL,
`airline` varchar(30) DEFAULT NULL,
UNIQUE KEY `idx_iata_code` (`iata_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'airports_idb'
CREATE TABLE `airports_idb` (
`iata_code` char(3) DEFAULT NULL,
`airport` varchar(80) DEFAULT NULL,
`city` varchar(30) DEFAULT NULL,
`state` char(2) DEFAULT NULL,
`country` varchar(30) DEFAULT NULL,
`latitude` float DEFAULT NULL,
`longitude` float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'flights_cs'
CREATE TABLE `flights_cs` (
`year` smallint(6) DEFAULT NULL,
`month` tinyint(4) DEFAULT NULL,
`day` tinyint(4) DEFAULT NULL,
`day_of_week` tinyint(4) DEFAULT NULL,
`fl_date` date DEFAULT NULL,
`carrier` char(2) DEFAULT NULL,
`tail_num` char(6) DEFAULT NULL,
`fl_num` smallint(6) DEFAULT NULL,
`origin` varchar(5) DEFAULT NULL,
`dest` varchar(5) DEFAULT NULL,
`crs_dep_time` char(4) DEFAULT NULL,
`dep_time` char(4) DEFAULT NULL,
`dep_delay` smallint(6) DEFAULT NULL,
`taxi_out` smallint(6) DEFAULT NULL,
`wheels_off` char(4) DEFAULT NULL,
`wheels_on` char(4) DEFAULT NULL,
`taxi_in` smallint(6) DEFAULT NULL,
`crs_arr_time` char(4) DEFAULT NULL,
`arr_time` char(4) DEFAULT NULL,
`arr_delay` smallint(6) DEFAULT NULL,
`cancelled` smallint(6) DEFAULT NULL,
`cancellation_code` smallint(6) DEFAULT NULL,
`diverted` smallint(6) DEFAULT NULL,
`crs_elapsed_time` smallint(6) DEFAULT NULL,
`actual_elapsed_time` smallint(6) DEFAULT NULL,
`air_time` smallint(6) DEFAULT NULL,
`distance` smallint(6) DEFAULT NULL,
`carrier_delay` smallint(6) DEFAULT NULL,
`weather_delay` smallint(6) DEFAULT NULL,
`nas_delay` smallint(6) DEFAULT NULL,
`security_delay` smallint(6) DEFAULT NULL,
`late_aircraft_delay` smallint(6) DEFAULT NULL
) ENGINE=Columnstore DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'flights_idb'
CREATE TABLE `flights_idb` (
`year` smallint(6) DEFAULT NULL,
`month` tinyint(4) DEFAULT NULL,
`day` tinyint(4) DEFAULT NULL,
`day_of_week` tinyint(4) DEFAULT NULL,
`fl_date` date DEFAULT NULL,
`carrier` char(2) DEFAULT NULL,
`tail_num` char(6) DEFAULT NULL,
`fl_num` smallint(6) DEFAULT NULL,
`origin` varchar(5) DEFAULT NULL,
`dest` varchar(5) DEFAULT NULL,
`crs_dep_time` char(4) DEFAULT NULL,
`dep_time` char(4) DEFAULT NULL,
`dep_delay` smallint(6) DEFAULT NULL,
`taxi_out` smallint(6) DEFAULT NULL,
`wheels_off` char(4) DEFAULT NULL,
`wheels_on` char(4) DEFAULT NULL,
`taxi_in` smallint(6) DEFAULT NULL,
`crs_arr_time` char(4) DEFAULT NULL,
`arr_time` char(4) DEFAULT NULL,
`arr_delay` smallint(6) DEFAULT NULL,
`cancelled` smallint(6) DEFAULT NULL,
`cancellation_code` char(1) DEFAULT NULL,
`diverted` smallint(6) DEFAULT NULL,
`crs_elapsed_time` smallint(6) DEFAULT NULL,
`actual_elapsed_time` smallint(6) DEFAULT NULL,
`air_time` smallint(6) DEFAULT NULL,
`distance` smallint(6) DEFAULT NULL,
`carrier_delay` smallint(6) DEFAULT NULL,
`weather_delay` smallint(6) DEFAULT NULL,
`nas_delay` smallint(6) DEFAULT NULL,
`security_delay` smallint(6) DEFAULT NULL,
`late_aircraft_delay` smallint(6) DEFAULT NULL,
KEY `idx_fl_num_dep_delay` (`fl_num`,`dep_delay`),
KEY `idx_fl_num_dep_delay_carrier` (`fl_num`,`dep_delay`,`carrier`),
KEY `idx_dep_delay` (`dep_delay`)
) ENGINE=InnoDB;
-- Create syntax for TABLE 'tickets_idb'
CREATE TABLE `tickets_idb` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`fl_date` date NOT NULL,
`fl_num` smallint(6) NOT NULL,
`carrier` char(2) NOT NULL DEFAULT '',
`origin` varchar(5) NOT NULL DEFAULT '',
`dest` varchar(5) NOT NULL DEFAULT '',
`price` decimal(9,2) NOT NULL DEFAULT 0.00,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'trips_idb'
CREATE TABLE `trips_idb` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ticket_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

61
Bookings/src/.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,61 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next

23
Bookings/src/client/.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

Wyświetl plik

@ -0,0 +1,68 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

14447
Bookings/src/client/package-lock.json wygenerowano 100644

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,42 @@
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"bootstrap": "^4.4.1",
"react": "^16.12.0",
"react-bootstrap": "^1.0.0-beta.16",
"react-datepicker": "^2.11.0",
"react-dom": "^16.12.0",
"react-icons-weather": "^1.0.5",
"react-popper": "^1.3.7",
"react-scripts": "3.3.0",
"react-select": "^3.0.8",
"segmented-control": "^0.1.12"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://localhost:8080"
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 15 KiB

Wyświetl plik

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Bookings</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 5.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 9.4 KiB

Wyświetl plik

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

Wyświetl plik

@ -0,0 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

Wyświetl plik

@ -0,0 +1,351 @@
.app {
text-align: center;
}
.app-logo {
height: 40vmin;
}
.app-header {
background-color: #003545;
min-height: 125px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
}
.title-text {
padding-top: 10px;
margin: 0px;
font-size: 30px;
font-weight: bold;
}
.sub-title-text {
margin: 0px;
font-size: small;
margin-left: 10px;
}
.float-left {
float: left;
}
.float-right {
float: right;
}
.center {
margin-top: auto;
margin-bottom: auto;
text-align: center;
padding: 10px;
}
.width-50 {
width: 50%;
}
.header-link {
background-color: #E5E1E5;
color: white;
cursor: pointer;
height: 65px;
display: table;
}
.header-link h3 {
display:table-cell;
vertical-align:middle
}
.selected {
background-color: #96DDCF;
}
.form-nav-panel {
border-bottom: #c0c0c0 1px solid;
}
.form-main {
margin-left: auto;
margin-right: auto;
}
.form-main table {
width: 50%;
margin-left: auto;
margin-right: auto;
}
.form-content {
padding: 10px;
}
.form-section {
text-align: left;
}
.filter-content {
background: #EEEEEE;
border-radius: 5px;
text-align: left;
padding: 15px;
}
.filter-content h2 {
margin-top: 0px;
margin-left: 5px;
color: #003545;
}
.margin-top-10 {
margin-top: 10px;
}
.margin-20 {
margin: 20px;
}
button {
border-radius: 5px;
background: #003545;
border-color: #E5E1E5;
color: #ffffff;
font-weight: bold;
width: 350px;
height: 50px;
font-size: large;
}
button:hover {
cursor: pointer;
background: #96DDCF;
}
.datepicker {
width: 100px;
height: 25px;
border-radius: 5px;
border: #C0C0C0 1px solid;
}
.datepicker {
width: 100px;
height: 25px;
border-radius: 5px;
border: #C0C0C0 1px solid;
}
.width-500 {
width: 500px;
}
.width-125 {
width: 125px;
}
.margin-top-25 {
margin-top: 25px;
}
.margin-left-10 {
margin-left: 10px;
}
.width-33 {
width: 33%;
display: inline-block;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.valign-top {
vertical-align: top;
}
.flight-content button {
width: 125px;
}
.below-0 {
margin-bottom: 0px;
padding-bottom: 0px;
}
.item-content {
width: 90%;
margin-left: auto;
margin-right: auto;
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: #C0C0C0 1px solid;
padding: 10px;
}
.item-content table {
width: 100%;
}
.item-content table th {
background-color: #003545;
color: white;
height: 35px;
font-weight: normal;
}
.item-content table td {
padding: 15px;
}
.item-content p.subtext {
color: red;
margin-bottom: 20px;
font-size: 12px;
}
.item-content .header {
font-size: 26px;
margin-top: 5px;
margin-bottom: 15px;
}
.item-content .sub-header {
font-size: 16px;
text-align: left;
}
.score-very-good {
color: green;
}
.score-good {
color: lightgreen;
}
.score-average {
color: black;
}
.score-poor {
color: orange;
}
.score-very-poor {
color: red;
}
.font-size-20 {
font-size: 20px;
}
.hidden {
display: none;
}
.margin-right-25 {
margin-right: 25px;
}
.margin-left-10 {
margin-left: 10px;
}
.forecast-title {
font-size: 20px;
font-weight: bold;
margin-right: 10px;
}
.forecast-details {
margin-top: 5px;
font-size: 11px;
}
.weather-summary-content {
background-color: #E5E1E5;
padding: 8px;
margin: 5px;
}
.bold {
font-weight: bold;
}
.margin-left-5 {
margin-left: 5px;
}
.section-label {
font-weight: bold;
margin: 0px;
}
.section-description {
font-style: italic;
}
.estimation-content {
font-size: 12px;
color: red;
}
.estimation-content p {
margin: 0px;
font-weight: bold;
color: black;
}
.item-content .trip-content:last-child {
margin-top: 25px;
}
.trip-content table {
margin-top: 10px;
}
.trip-content table tr td {
width: 25%;
}
.trip-score {
width: 100px;
height: 100px;
border-radius: 50px;
margin-right: 30px;
margin-top: 10px;
}
.trip-score p {
color: white;
height: 100px;
line-height: 100px;
text-align: center;
font-size: 38px;
}
.background-green {
background-color: green;
}
.background-gray {
background-color: gray;
}
.background-red {
background-color: red;
}
#popover-basic.popover {
max-width: 500px;
}

Wyświetl plik

@ -0,0 +1,24 @@
import React from 'react';
import './App.css';
import Dashboard from './components/Dashboard';
import logo from './plane.png';
function App() {
return (
<div className="app">
<header className="app-header">
<div>
<img className="float-left" src={logo} alt="logo" />
<div className="float-right center">
<p className="title-text align-left">Bookings</p>
<p className="sub-title-text align-left">A MariaDB Smart Transactions Demo</p>
</div>
<div style={{clear: "both"}} />
</div>
</header>
<Dashboard />
</div>
);
}
export default App;

Wyświetl plik

@ -0,0 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

Wyświetl plik

@ -0,0 +1,17 @@
import React, {Component} from 'react';
const images = require.context('../images', true);
export default class AirlineIcon extends Component {
getIcon(code) {
return images('./' + code + '.png');
}
render() {
return (
<div>
<img src={this.getIcon(this.props.code)} alt={this.props.name} />
{this.props.name}
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,103 @@
import React, {Component} from 'react';
import FlightSearch from './FlightSearch';
import Flights from './Flights';
import Trips from './Trips';
export default class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
flights: [],
trips: [],
selectedTabIndex: 0
};
}
componentDidMount() {
this.loadTrips();
}
async loadTrips() {
this.setState({
trips: await this.getTrips()
})
}
async getTrips() {
const response = await fetch('/api/trips');
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message)
}
return body;
}
async executeSearch(params) {
this.setState({
flights: await this.getFlights(params.origin.code,
params.destination.code,
params.originDepartureDate)
});
}
async getFlights(origin, destination, originDepartureDate) {
const response = await fetch('/api/flights?o=' + origin + '&d=' + destination + '&dt=' + originDepartureDate);
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message)
}
return body;
};
toggleSection(selectedTabIndex) {
this.setState({selectedTabIndex});
}
getNavClasses(index) {
if (this.state.selectedTabIndex === index) {
return "header-link selected width-50 float-left";
}
else {
return "header-link width-50 float-left";
}
}
renderSection() {
if (this.state.selectedTabIndex === 0) {
return (
<div className="form-content">
<FlightSearch executeSearch={(params) => this.executeSearch(params)} />
<Flights flights={this.state.flights} />
</div>
);
}
else {
return (
<div>
<Trips trips={this.state.trips} />
</div>
);
}
}
render() {
return (
<div >
<div className="form-nav-panel">
<div onClick={() => this.toggleSection(0)} className={this.getNavClasses(0)}>
<h3>Book a Trip!</h3>
</div>
<div onClick={() => this.toggleSection(1)} className={this.getNavClasses(1)}>
<h3>Upcoming Trips (2)</h3>
</div>
<div style={{clear: "both"}} />
</div>
<div className="form-main">
{this.renderSection()}
</div>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,135 @@
import React, {Component} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import AirlineIcon from './AirlineIcon';
import info from '../images/info.png';
export default class Flight extends Component {
renderFlightScore(score) {
let cssClass = "float-left margin-left-10 bold ";
if (score >= 4.5) {
cssClass += "score-very-good";
}
else if (score >= 4.0) {
cssClass += "score-good";
}
else if (score >= 3.0) {
cssClass += "score-average";
}
else if (score >= 2.0) {
cssClass += "score-poor";
}
else {
cssClass += "score-very-poor";
}
return(
<div className="float-left font-size-20">
<p className="float-left">
Flight score:
</p>
<p className={cssClass}>
{score}
</p>
<div style={{clear: "both"}} />
</div>
);
}
getPopover(props,assessment) {
return(
<Popover id="popover-basic" {...props}>
<Popover.Title as="h3">Flight score ({assessment.overall_score}/5.0) </Popover.Title>
<Popover.Content>
<table cellSpacing="10" cellPadding="10">
<tr>
<td><p className="section-label">Price score</p></td>
<td className="assessment-score" align="right" valign="top">{assessment.price_score}</td>
</tr>
<tr>
<td>
<p className="section-label">Delay score</p>
<p className="section-description">This flight is delayed {assessment.delay_percentage}% of the time.</p>
</td>
<td className="assessment-score" align="right" valign="top">{assessment.delay_score}</td>
</tr>
<tr>
<td>
<p className="section-label">Cancellation score</p>
<p className="section-description">This flight is canceled {assessment.cancel_percentage}% of the time.</p>
</td>
<td className="assessment-score" align="right" valign="top">{assessment.cancel_score}</td>
</tr>
</table>
</Popover.Content>
</Popover>
);
}
getFormattedTime(time) {
return time.match(/.{1,2}/g).join(':');
}
getDuration(departure,arrival) {
var delta = parseInt(arrival) - parseInt(departure);
return this.getFormattedTimeFromInt(delta);
}
getFormattedTimeFromInt(num) {
var hours = Math.floor(num / 60);
var minutes = parseInt(num % 60);
return hours + "h " + minutes + "m";
}
render() {
const flight = this.props.flight;
return (
<div className="item-content">
<div>
<div className="width-33 align-left">
<h3>{this.getFormattedTime(flight.dep_time)} - {this.getFormattedTime(flight.arr_time)}</h3>
<p className="subtext">Avg. delay: {flight.avg_delay !== null ? Math.round(flight.avg_delay) : 0} minutes</p>
</div>
<div className="width-33 align-left">
<p>Flight duration: {this.getDuration(flight.dep_time,flight.arr_time)}</p>
<p className="subtext">Avg. flight duration: {this.getFormattedTimeFromInt(flight.avg_duration)}</p>
</div>
<div className="width-33 align-right valign-top">
<h3>${flight.price}</h3>
</div>
</div>
<div>
<div className="width-33 align-left">
<AirlineIcon code={flight.airline_code} name={flight.airline} />
</div>
<div className="width-33 align-left">
<p>{flight.origin} - {flight.dest}</p>
</div>
<div className="width-33 align-left" />
</div>
<div>
<div className="float-left valign-middle">
{this.renderFlightScore(flight.assessment.overall_score)}
<div className="float-left valign-middle margin-left-5">
<OverlayTrigger
trigger="hover"
placement="right"
overlay={this.getPopover(this.props,flight.assessment)}>
<img src={info} alt="?" />
</OverlayTrigger>
</div>
<div style={{clear: "both"}} />
</div>
<div className="align-right margin-top-25">
<button>Select</button>
</div>
<div style={{clear: "both"}} />
</div>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,230 @@
import React, {Component} from 'react';
import Select from 'react-select';
import { SegmentedControl } from 'segmented-control'
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import calendar from '../images/calendar.png';
export default class FlightSearch extends Component {
state = {
type: 0,
airlines: [],
airports: [],
selectedOriginOption: null,
selectedDestinationOption: null,
count_options: [{ value: 0, label: "0" }, { value: 1, label: "1" }, { value: 2, label: "2" }],
selectedAdultOption: { value: 1, label: "1" },
selectedChildrenOption: { value: 0, label: "0" }
};
constructor(props) {
super(props);
this.handleOriginDepartureDateChange = this.handleOriginDepartureDateChange.bind(this);
this.handleDestinationDepartureDateChange = this.handleDestinationDepartureDateChange.bind(this);
this.executeSearch = this.executeSearch.bind(this);
}
componentDidMount() {
this.loadAirports();
this.loadAirlines();
}
async loadAirlines() {
await this.getAirlines()
.then(res => {
const airlineOptions = res.map(airline => ({
value: airline.iata_code,
label: airline.airline
}));
this.setState({ airlines: airlineOptions })
})
.catch(err => console.log(err));
}
async loadAirports() {
await this.getAirports()
.then(res => {
const airportOptions = res.map(airport => ({
value: airport.iata_code,
label: airport.airport
}));
this.setState({ airports: airportOptions })
})
.catch(err => console.log(err));
}
async getAirlines() {
const response = await fetch('/api/airlines');
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message)
}
return body;
};
async getAirports() {
const response = await fetch('/api/airports');
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message)
}
return body;
};
handleTypeChange(type) {
this.setState({type});
}
handleOriginChange = selectedOriginOption => {
this.setState({ selectedOriginOption });
};
handleDestinationChange = selectedDestinationOption => {
this.setState({ selectedDestinationOption });
};
handleOriginDepartureDateChange(date) {
this.setState({
originDepartureDate: date
});
}
handleDestinationDepartureDateChange(date) {
this.setState({
destinationDepartureDate: date
});
}
getReturnDateCss() {
let classes = "form-section float-left margin-left-10";
if (this.state.type === 0) {
classes += " hidden";
}
return classes;
}
async executeSearch() {
const { selectedOriginOption, selectedDestinationOption} = this.state;
var params = {
origin: {
code: selectedOriginOption.value,
name: selectedOriginOption.label
},
destination: {
code: selectedDestinationOption.value,
name: selectedDestinationOption.label
},
originDepartureDate: this.state.originDepartureDate
};
if (this.state.type === 1) {
params.destinationDepartureDate = this.state.destinationDepartureDate
}
this.props.executeSearch(params);
}
renderAirportOptions(selectedOption, handleChange) {
return (
<Select
className="width-500"
value={selectedOption}
onChange={handleChange}
options={this.state.airports}
isClearable="true"
/>
);
}
render() {
return (
<div className="filter-content">
<h2>Search Flights</h2>
<table>
<tr colSpan="2">
<SegmentedControl
name="tripType"
options={[
{ label: "One-way", value: 0, default: true },
{ label: "Round trip", value: 1 }
]}
setValue={val => this.handleTypeChange(val)}
style={{ width: 300, color: '#003545' }}
/>
</tr>
<tr>
<td>
<div className="form-section">
<p>Origin</p>
{this.renderAirportOptions(this.state.selectedOriginOption,this.handleOriginChange)}
</div>
</td>
<td >
<div className="form-section">
<p>Destination</p>
{this.renderAirportOptions(this.state.selectedDestinationOption,this.handleDestinationChange)}
</div>
</td>
</tr>
<tr>
<td>
<div>
<div className="form-section float-left">
<p>Departing</p>
<div>
<img className="float-left" src={calendar} alt="cal" />
<DatePicker className="datepicker float-left" selected={this.state.originDepartureDate} onChange={this.handleOriginDepartureDateChange} />
<div style={{clear: "both"}} />
</div>
</div>
<div className={this.getReturnDateCss()}>
<p>Returning</p>
<div>
<img className="float-left" src={calendar} alt="cal" />
<DatePicker className="datepicker float-left" selected={this.state.destinationDepartureDate} onChange={this.handleDestinationDepartureDateChange} />
<div style={{clear: "both"}} />
</div>
</div>
<div style={{clear: "both"}} />
</div>
</td>
<td>
<div>
<div className="form-section float-left">
<p>Adults (18+)</p>
<Select
className="width-125"
options={this.state.count_options}
value={this.state.selectedAdultOption}
/>
</div>
<div className="form-section float-left margin-left-10">
<p>Children (0-17)</p>
<Select
className="width-125"
options={this.state.count_options}
value={this.state.selectedChildrenOption}
/>
</div>
<div style={{clear: "both"}} />
</div>
</td>
</tr>
<tr>
<td colSpan="4">
<div className="margin-20 center">
<button onClick={this.executeSearch}>Search</button>
</div>
</td>
</tr>
</table>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,21 @@
import React, {Component} from 'react';
import Flight from './Flight';
export default class Flights extends Component {
renderFlights() {
return this.props.flights.map(flight => (
<Flight flight={flight} />
))
}
render() {
return (
<div className="margin-top-10">
<div>
</div>
{this.renderFlights()}
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,196 @@
import React, {Component} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { OverlayTrigger, Popover } from 'react-bootstrap';
import WeatherIcon from 'react-icons-weather';
import AirlineIcon from './AirlineIcon';
export default class Trip extends Component {
getPopover(props,assessment) {
return(
<Popover id="popover-basic" {...props}>
<Popover.Title as="h3">Flight score ({assessment.overall_score}/5.0) </Popover.Title>
<Popover.Content>
<table cellSpacing="10" cellPadding="10">
<tr>
<td>
<p className="section-label">Historical score</p>
<p className="section-description">
Based on historical analytics this flight has a <b>{assessment.historical_delay_percentage}%</b>&nbsp;
chance of being delayed.
</p>
</td>
<td className="assessment-score" align="right" valign="top">{assessment.historical_score}</td>
</tr>
<tr>
<td>
<p className="section-label">Weather score</p>
</td>
<td className="assessment-score" align="right" valign="top">{assessment.weather_score}</td>
</tr>
<tr>
<td>
<p className="section-label">Delay projection (minutes)</p>
<p className="section-description">
This flight is delay probabilty has a multiplier of <b>{assessment.weather_delay_multiplier}</b>&nbsp;
due to current weather projections.
</p>
</td>
<td className="assessment-score" align="right" valign="top">{assessment.projected_delay}</td>
</tr>
</table>
</Popover.Content>
</Popover>
);
}
getFormattedDate(dateStr) {
let date = new Date(dateStr);
return date.toDateString();
}
getFormattedTime(time) {
return time.match(/.{1,2}/g).join(':');
}
getFormattedOffsetTime(time,offset) {
let offsetTime = (parseInt(time) + offset).toString();
if (offsetTime.length === 3) {
return this.getFormattedTime("0" + offsetTime);
}
return this.getFormattedTime(offsetTime);
}
getDuration(departure,arrival) {
let delta = (parseInt(arrival) - parseInt(departure)).toString();
return this.getFormattedDuration(delta);
}
getFormattedDuration(delta) {
if (delta.length === 4) {
return delta.substring(0,1) + 'h ' + delta.substring(1,3) + 'm';
}
else {
return delta[0] + 'h ' + delta.substring(1,3) + 'm';
}
}
renderFlightScore(assessment) {
let cssClass = "assessment-content float-right trip-score ";
if (assessment.overall_score >= 4.0) {
cssClass += "background-green";
}
else if (assessment.overall_score >= 2.5) {
cssClass += "background-gray";
}
else {
cssClass += "background-red";
}
return(
<div className={cssClass}>
<OverlayTrigger
trigger="hover"
placement="left"
overlay={this.getPopover(this.props, assessment)}>
<p>{assessment.overall_score}</p>
</OverlayTrigger>
</div>
);
}
renderTrips() {
// This will handle round-trip rendering
//return this.props.trip.map(trip => (
let trip = this.props.trip;
return(
<div class="trip-content">
<div>
<div className="header float-left">
Depart {trip.origin} - {trip.dest}
<p className="sub-header">{this.getFormattedDate(trip.fl_date)}</p>
</div>
<div className="float-right align-right margin-right-25 weather-summary-content">
<div>
<div className="float-left forecast-title">{trip.forecast.description}</div>
<div className="float-left"><WeatherIcon name="darksky" iconId={trip.forecast.icon} flip="horizontal" rotate="90" /></div>
<div style={{clear: "both"}} />
</div>
<div className="align-left forecast-details">
<div className="float-left">
<p>Low: {trip.forecast.temp_low}</p>
<p>High: {trip.forecast.temp_high}</p>
</div>
<div className="float-left margin-left-10">
<p>Precip: {trip.forecast.precip_probability}</p>
<p>Wind: {trip.forecast.wind_speed}</p>
</div>
<div style={{clear: "both"}} />
</div>
</div>
{this.renderFlightScore(trip.assessment)}
<div style={{clear: "both"}} />
</div>
<table cellspacing="0" cellpadding="0">
<tr>
<th>Flight</th>
<th>Departs</th>
<th>Arrives</th>
<th>Duration</th>
</tr>
<tr>
<td>
{trip.airline_code} {trip.fl_num}
</td>
<td>
{this.getFormattedTime(trip.dep_time)}
</td>
<td>
{this.getFormattedTime(trip.arr_time)}
</td>
<td>
{this.getDuration(trip.dep_time,trip.arr_time)}
</td>
</tr>
<tr>
<td>
<AirlineIcon code={trip.airline_code} name={trip.airline} />
</td>
<td>
<div className="estimation-content">
<p>Estimated</p>
{this.getFormattedOffsetTime(trip.dep_time,trip.assessment.projected_delay)}
</div>
</td>
<td>
<div className="estimation-content">
<p>Estimated</p>
{this.getFormattedOffsetTime(trip.arr_time,trip.assessment.projected_delay)}
</div>
</td>
<td>
</td>
</tr>
</table>
</div>
);
//))
}
render() {
return (
<div className="item-content">
{this.renderTrips()}
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,19 @@
import React, {Component} from 'react';
import Trip from './Trip';
export default class Trips extends Component {
renderTrips() {
return this.props.trips.map(trip => (
<Trip trip={trip} />
))
}
render() {
return (
<div className="margin-top-10">
{this.renderTrips()}
</div>
);
}
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.6 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.5 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.8 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.2 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.2 KiB

Wyświetl plik

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

Wyświetl plik

@ -0,0 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 6.2 KiB

Wyświetl plik

@ -0,0 +1,137 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

Wyświetl plik

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

24
Bookings/src/db.js 100644
Wyświetl plik

@ -0,0 +1,24 @@
var mariadb = require('mariadb');
require('dotenv').config();
var pool = mariadb.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
multipleStatements: true,
connectionLimit: 5
});
module.exports={
getConnection: function(){
return new Promise(function(resolve,reject){
pool.getConnection().then(function(connection){
resolve(connection);
}).catch(function(error){
reject(error);
});
});
}
}

818
Bookings/src/package-lock.json wygenerowano 100644
Wyświetl plik

@ -0,0 +1,818 @@
{
"name": "bookings",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/geojson": {
"version": "7946.0.7",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
"integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ=="
},
"@types/node": {
"version": "12.12.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.24.tgz",
"integrity": "sha512-1Ciqv9pqwVtW6FsIUKSZNB82E5Cu1I2bBTj1xuIHXLe/1zYLl3956Nbhg2MzSYHVfl9/rmanjbQIb7LibfCnug=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"concurrently": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.0.2.tgz",
"integrity": "sha512-iUNVI6PzKO0RVXV9pHWM0khvEbELxf3XLIoChaV6hHyoIaJuxQWZiOwlNysnJX5khsfvIK66+OJqRdbYrdsR1g==",
"requires": {
"chalk": "^2.4.2",
"date-fns": "^2.0.1",
"lodash": "^4.17.15",
"read-pkg": "^4.0.1",
"rxjs": "^6.5.2",
"spawn-command": "^0.0.2-1",
"supports-color": "^6.1.0",
"tree-kill": "^1.2.2",
"yargs": "^13.3.0"
}
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"date-fns": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.8.1.tgz",
"integrity": "sha512-EL/C8IHvYRwAHYgFRse4MGAPSqlJVlOrhVYZ75iQBKrnv+ZedmYsgwH3t+BCDuZDXpoo07+q9j4qgSSOa7irJg=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"requires": {
"is-arrayish": "^0.2.1"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"hosted-git-info": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
"integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg=="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"mariadb": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-2.1.4.tgz",
"integrity": "sha512-CMLbIKZCzxero94luo25IKpboEuUCvA2g2+NMUBdRYpBCDmnsiQ8kwdgqvuVQ13a4/ZKr87BC1nbe4b3UZXXmQ==",
"requires": {
"@types/geojson": "^7946.0.7",
"@types/node": "^12.12.11",
"denque": "^1.4.1",
"iconv-lite": "^0.5.0",
"long": "^4.0.0",
"moment-timezone": "^0.5.27"
},
"dependencies": {
"iconv-lite": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
"integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
}
}
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"moment-timezone": {
"version": "0.5.27",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
"integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==",
"requires": {
"moment": ">= 2.9.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"requires": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
}
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"p-limit": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
"integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"path-parse": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"proxy-addr": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
"integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.0"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"read-pkg": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
"integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
"requires": {
"normalize-package-data": "^2.3.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"resolve": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.1.tgz",
"integrity": "sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg==",
"requires": {
"path-parse": "^1.0.6"
}
},
"rxjs": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
"integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"spawn-command": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
"integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A="
},
"spdx-correct": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"requires": {
"has-flag": "^3.0.0"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
},
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yargs": {
"version": "13.3.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
"integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
"requires": {
"cliui": "^5.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.1"
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}

Wyświetl plik

@ -0,0 +1,20 @@
{
"name": "bookings",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "concurrently \"npm run server\" \"npm run client\"",
"server": "node server.js",
"client": "npm start --prefix client"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"concurrently": "^5.0.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"mariadb": "^2.1.4"
}
}

Wyświetl plik

@ -0,0 +1,21 @@
"use strict";
let express = require("express"),
router = express.Router(),
pool = require('../db');
// GET all airlines
router.get("/", async (req, res, next) => {
let conn;
try {
conn = await pool.getConnection();
var query = "select * from airlines_idb order by airline";
var rows = await conn.query(query);
res.send(rows);
throw err;
} finally {
if (conn) return conn.release();
}
});
module.exports = router;

Wyświetl plik

@ -0,0 +1,22 @@
"use strict";
let express = require("express"),
router = express.Router(),
pool = require('../db');
// GET all airports
router.get("/", async (req, res, next) => {
let conn;
try {
conn = await pool.getConnection();
var query = "select iata_code, airport from airports_idb group by airport, iata_code order by airport";
var rows = await conn.query(query);
res.send(rows);
} catch (err) {
throw err;
} finally {
if (conn) return conn.release();
}
});
module.exports = router;

Wyświetl plik

@ -0,0 +1,107 @@
"use strict";
let express = require("express"),
router = express.Router(),
pool = require('../db');
router.get("/", async (req, res, next) => {
let date = new Date(req.query.dt);
let origin = req.query.o;
let dest = req.query.d;
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
let conn;
try {
conn = await pool.getConnection();
var query = "select \
a.airline, \
t.carrier airline_code, \
t.origin, \
t.dest, \
t.price, \
a.`airline`, \
f.dep_time, \
f.arr_time, \
fh.avg_delay, \
(select avg(arr_time - dep_time) from flights_cs \
where month = ? and day = ? and origin = ? and dest = ? and carrier = t.carrier) avg_duration, \
fh.delayed_pct, \
fh.cancelled_pct \
from \
tickets_idb t, \
(select * from flights_idb where year = ? and month = ? and day = ?) f, \
(select \
a.avg_delay, \
round(100 * (a.`delayed` / a.volume), 2) delayed_pct, \
round(100 * (a.cancelled / a.volume), 2) cancelled_pct, \
a.carrier \
from \
(select \
count(*) volume, \
sum(case when dep_delay > 0 then 1 else 0 end) `delayed`, \
sum(cancelled) cancelled, \
avg(dep_delay) avg_delay, \
carrier \
from \
flights_cs \
where \
month = ? and day = ? and origin = ? and dest = ? group by carrier) a) fh, \
airlines_idb a \
where \
t.carrier = f.carrier and \
t.fl_date = f.fl_date and \
t.fl_num = f.fl_num and \
t.carrier = fh.carrier and \
f.carrier = a.iata_code and \
t.fl_date = ? and \
t.origin = ? and \
t.dest = ?";
var results = await conn.query(query, [month,day,origin,dest,year,month,day,month,day,origin,dest,date,origin,dest]);
var analyzedResults = analyzeResults(results);
res.send(analyzedResults);
} catch (err) {
throw err;
} finally {
if (conn) return conn.release();
}
});
// secret (scoring) sauce
function analyzeResults(items) {
let prices = items.map(item => item.price)
let average_price = getAverage(prices);
items.forEach(item => {
let price_score = round(3.5 * (average_price / item.price), 1);
let delay_score = round(5 * ((100 - item.delayed_pct)/100), 1);
let cancel_score = round(5 * ((100 - item.cancelled_pct)/100), 1);
let overall_score = round((price_score + delay_score + cancel_score) / 3, 1);
item.assessment = {
overall_score: overall_score,
price_score: price_score,
delay_score: delay_score,
delay_percentage: item.delayed_pct,
cancel_score: cancel_score,
cancel_percentage: item.cancelled_pct
};
});
return items;
}
function getAverage(arr) {
let reducer = (total, currentValue) => total + currentValue;
let sum = arr.reduce(reducer)
return sum / arr.length;
}
function round(value, precision) {
var multiplier = Math.pow(10, precision || 0);
return Math.round(value * multiplier) / multiplier;
}
module.exports = router;

Wyświetl plik

@ -0,0 +1,122 @@
"use strict";
let express = require("express"),
router = express.Router(),
pool = require('../db');
router.get("/", async (req, res, next) => {
let currentDate = new Date();
let year = currentDate.getFullYear();
let conn;
try {
conn = await pool.getConnection();
var query = "select \
t.fl_num, \
a.airline, \
t.carrier airline_code, \
t.fl_date, \
t.origin, \
t.dest, \
f.dep_time, \
f.arr_time, \
fh.delayed_pct, \
fh.avg_delay \
from \
trips_idb tr inner join \
tickets_idb t on tr.ticket_id = t.id inner join \
airlines_idb a on t.carrier = a.iata_code, \
(select * from flights_idb where year >= ?) f, \
(select \
a.avg_delay, \
round(100 * (a.`delayed` / a.volume), 2) delayed_pct, \
round(100 * (a.cancelled / a.volume), 2) cancelled_pct, \
a.carrier, \
a.day, \
a.month \
from \
(select \
count(*) volume, \
sum(case when dep_delay > 0 then 1 else 0 end) `delayed`, \
sum(cancelled) cancelled, \
avg(dep_delay) avg_delay, \
carrier, \
month, \
day \
from \
flights_cs \
where \
month in (select month(fl_date) from trips_idb tr inner join tickets_idb t on tr.ticket_id = t.id) and \
day in (select day(fl_date) from trips_idb tr inner join tickets_idb t on tr.ticket_id = t.id) \
group by \
day, \
month, \
carrier) a) fh \
where \
t.carrier = f.carrier and \
t.fl_date = f.fl_date and \
t.fl_num = f.fl_num and \
t.carrier = fh.carrier and \
fh.month = month(t.fl_date) and \
fh.day = day(t.fl_date)";
var results = await conn.query(query, [year]);
var analyzedResults = analyzeResults(results);
res.send(analyzedResults);
} catch (err) {
throw err;
} finally {
if (conn) return conn.release();
}
});
// secret (scoring) sauce
function analyzeResults(items) {
items.forEach(item => {
let forecast = forecasts[item.origin + "_" + item.fl_date.toISOString().substring(0, 10)];
let weather_score = 5 - 5*(forecast.precip_probability + (forecast.wind_speed/100));
let historical_score = round(5 * ((100 - item.delayed_pct)/100), 1);
let overall_score = round((weather_score + historical_score) / 2, 1);
let weather_delay_multiplier = round((forecast.precip_probability + (forecast.wind_speed/100)) * 5, 3);
let projected_delay = round(weather_delay_multiplier * item.avg_delay, 0);
item.assessment = {
overall_score: overall_score,
historical_score: historical_score,
historical_delay_percentage: item.delayed_pct,
weather_score: weather_score,
weather_delay_multiplier: weather_delay_multiplier,
projected_delay: projected_delay
};
item.forecast = forecast;
});
return items;
}
function round(value, precision) {
var multiplier = Math.pow(10, precision || 0);
return Math.round(value * multiplier) / multiplier;
}
var forecasts = {
"ORD_2020-02-04": {
description: "Snow",
icon: "snow",
temp_low: "28°F",
temp_high: "29°F",
precip_probability: 0.6,
wind_speed: 15
},
"LAX_2020-02-06": {
description: "Clear",
icon: "clear-day",
temp_low: "56°F",
temp_high: "65°F",
precip_probability: 0,
wind_speed: 5
}
};
module.exports = router;

Wyświetl plik

@ -0,0 +1,34 @@
const express = require('express');
const app = express();
const port = 8080;
const path = require('path');
const bodyParser = require("body-parser");
const airportsRoutes = require("./routes/airportsRoutes");
const airlinesRoutes = require("./routes/airlinesRoutes");
const flightsRoutes = require("./routes/flightsRoutes");
const tripsRoutes = require("./routes/tripsRoutes");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
}
app.use("/api/airports", airportsRoutes);
app.use("/api/airlines", airlinesRoutes);
app.use("/api/flights", flightsRoutes);
app.use("/api/trips", tripsRoutes);
app.get("/*", (req, res) => {
res.sendFile(path.join(__dirname, "/client/build/index.html"));
});
app.use((err, req, res, next) => {
res.status(422).send({ error: err._message });
});
// console.log that your server is up and running
app.listen(port, () => console.log(`Listening on port ${port}`));