diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 3c7da0b7..eb4e4040 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -2,4 +2,4 @@ trailingComma: "all" tabWidth: 2 semi: true singleQuote: true -printWidth: 80 +printWidth: 120 diff --git a/backend/app.py b/backend/app.py index 6e05e2ad..4f0b0a0c 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,9 +1,15 @@ -from flask import Flask +from flask import Flask, request from flask_cors import CORS from waitress import serve +from http import HTTPStatus + app = Flask(__name__) -cors = CORS(app) + + +def allowed_file(filename): + ALLOWED_EXTENSIONS = {'txt', 'csv'} + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route("/") @@ -11,4 +17,27 @@ def hello(): return "Hello, World!" +@app.route('/analyze_sleep', methods=['POST']) +def analyze_sleep(): + if 'file' not in request.files: + return 'Missing file', HTTPStatus.BAD_REQUEST + file = request.files['file'] + + if file.filename == '': + return 'No selected file', HTTPStatus.BAD_REQUEST + + if not allowed_file(file.filename): + return 'File format not allowed', HTTPStatus.BAD_REQUEST + + file_content = file.read() + form_data = request.form.to_dict() + + return '' + + +CORS(app, + resources={r'/*': {"origins": '*'}}, + allow_headers='Content-Type') +app.config['CORS_HEADERS'] = 'Content-Type' + serve(app, host='0.0.0.0', port=8080) diff --git a/web/package.json b/web/package.json index 60a47fd7..c97e43db 100644 --- a/web/package.json +++ b/web/package.json @@ -11,7 +11,7 @@ "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", - "format": "yarn prettier --config ../.vscode/.prettierrc.yaml --write \"./**/*.{js,jsx,css,scss,json}\"" + "format": "yarn prettier --config ../.prettierrc.yaml --write \"./**/*.{js,jsx,css,scss,json}\"" }, "eslintConfig": { "extends": "react-app" @@ -25,6 +25,7 @@ "dependencies": { "argon-design-system-react": "^1.1.0", "axios": "^0.20.0", + "axios-observable": "^1.1.3", "bootstrap": "^4.5.0", "classnames": "2.2.6", "d3": "^5.16.0", @@ -41,6 +42,7 @@ "react-scripts": "3.4.0", "react-waypoint": "^9.0.2", "reactstrap": "8.4.1", + "rxjs": "^6.6.3", "yarn": "^1.22.4" }, "devDependencies": { diff --git a/web/src/index.js b/web/src/index.js index afa6bd23..960a5894 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -16,10 +16,6 @@ import Performance from 'views/performance'; import AnalyzeSleep from 'views/analyze-sleep'; import ScrollToTop from 'components/scroll_to_top'; import Emoji from 'components/emoji'; -import * as axios from 'axios'; - -// Communication with server (test) -axios.get('http://localhost:8080').then(console.log); const underConstruction = () => { const text = { diff --git a/web/src/requests/analayze-sleep.js b/web/src/requests/analayze-sleep.js new file mode 100644 index 00000000..b223f439 --- /dev/null +++ b/web/src/requests/analayze-sleep.js @@ -0,0 +1,8 @@ +import { SERVER_URL } from './constants'; +import Axios from 'axios-observable'; +import { objectToFormData } from './object-to-formdata'; + +export const analyzeSleep = (formData) => + Axios.post(`${SERVER_URL}/analyze_sleep`, objectToFormData(formData), { + headers: { 'Content-Type': 'multipart/form-data' }, + }); diff --git a/web/src/requests/constants.js b/web/src/requests/constants.js new file mode 100644 index 00000000..e761c194 --- /dev/null +++ b/web/src/requests/constants.js @@ -0,0 +1 @@ +export const SERVER_URL = 'http://localhost:8080'; diff --git a/web/src/requests/object-to-formdata.js b/web/src/requests/object-to-formdata.js new file mode 100644 index 00000000..ed7d6cb7 --- /dev/null +++ b/web/src/requests/object-to-formdata.js @@ -0,0 +1,7 @@ +export const objectToFormData = (obj) => { + const formData = new FormData(); + for (const key in obj) { + formData.append(key, obj[key]); + } + return formData; +}; diff --git a/web/src/requests/ping-server.js b/web/src/requests/ping-server.js new file mode 100644 index 00000000..8b3265b7 --- /dev/null +++ b/web/src/requests/ping-server.js @@ -0,0 +1,12 @@ +import { SERVER_URL } from './constants'; +import Axios from 'axios-observable'; +import { catchError, exhaustMap, map } from 'rxjs/operators'; +import { timer, of } from 'rxjs'; + +const pingServer = () => + Axios.get(SERVER_URL).pipe( + map(() => true), + catchError(() => of(false)), + ); + +export const periodicPingServer = (period) => timer(0, period).pipe(exhaustMap(pingServer)); diff --git a/web/src/views/analyze-sleep/index.js b/web/src/views/analyze-sleep/index.js index d6d14810..429583ff 100644 --- a/web/src/views/analyze-sleep/index.js +++ b/web/src/views/analyze-sleep/index.js @@ -1,15 +1,22 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Container } from 'reactstrap'; - import Header from 'components/header'; import WaitingForServerToBeReady from './waiting-for-server-to-be-ready'; import UploadForm from './upload-form'; - import text from './text.json'; - -const isServerReady = true; +import { periodicPingServer } from 'requests/ping-server'; const AnalyzeSleep = () => { + const [serverReady, setServerReady] = useState(false); + + useEffect(() => { + const subscription = periodicPingServer(200).subscribe( + (ready) => setServerReady(ready), + () => null, + ); + return () => subscription.unsubscribe(); + }); + return (