diff --git a/assets/.DS_Store b/assets/.DS_Store deleted file mode 100644 index 22ff2d9e9..000000000 Binary files a/assets/.DS_Store and /dev/null differ diff --git a/backend/Authentication/AuthenticationAPI.py b/backend/Authentication/AuthenticationAPI.py index 64d05ec5f..d0874ac1d 100644 --- a/backend/Authentication/AuthenticationAPI.py +++ b/backend/Authentication/AuthenticationAPI.py @@ -128,7 +128,7 @@ def register(): if telegramHandle.startswith('@'): telegramHandle = telegramHandle[1:] - if db.User.find({'userID': userID}).count(): # entry exists + if db.User.count_documents({'userID': userID}): # entry exists return jsonify({'message': 'User already exists'}), 401 # add to User table # note: if the user data does not adhere to defined validation standards, an error will be thrown here @@ -140,7 +140,7 @@ def register(): "bio": bio, "block": block, "telegramHandle": telegramHandle, - "profilePictureURI": "https://www.gravatar.com/avatar/00000000000000000000000000000000?d=identicon" + "imageKey": "default/profile_pic.png" }) except Exception as e: print(e) diff --git a/backend/CrowdAnalyser/CrowdAPI.py b/backend/CrowdAnalyser/CrowdAPI.py new file mode 100644 index 000000000..9df0fe4ec --- /dev/null +++ b/backend/CrowdAnalyser/CrowdAPI.py @@ -0,0 +1,59 @@ +from AuthFunction import authenticate +from db import * +from flask import jsonify, request, make_response +from AuthFunction import authenticate +from datetime import datetime, timedelta +import time +from flask_cors import cross_origin +from flask import Blueprint +import sys + +sys.path.append("../") + +crowd_api = Blueprint("crowd", __name__) + +@crowd_api.route('/') +@cross_origin() +def root_route(): + return 'hey' + +@crowd_api.route('/getweekly', methods = ["GET"]) +@cross_origin(supports_credentials=True) +def get_weekly_crowd(): + try: + # Get today's date and facilityID + today = request.args.get('endDate') + facilityID = request.args.get('facilityID') + + # Calculate the date of the start of the current week (Monday) + start_of_week = today - timedelta(days = today.weekday()) + + # Create a datetime object representing the start of the week + start_of_week_datetime = datetime.combine(start_of_week, datetime.min.time()) + + # Convert the datetime object to Unix timestamp + start_of_week_unix = int(time.mktime(start_of_week_datetime.timetuple())) + + # Initialise counter for loop to find avg crowd on each day of the week + counter = today.weekday() + 1 + start_of_day = start_of_week_unix; + crowd_result = [] + for _ in range(0, counter): + # Find all the data for the current day + data = list(db.Crowd.find({"facilityID": int(facilityID), + "time": {"$gte": start_of_day, "$lte": start_of_day + 86400}})) + + # Calculate average of the crowd level for the current day + level_sum = 0 + for item in data: + level_sum += item['level'] + avg_level = level_sum / len(data) if level_sum != 0 else 0 + crowd_result.append(avg_level) + + # Increment the start of the day by 1 day + start_of_day = start_of_day + 86400 + response = {"facilityID": facilityID, "level": crowd_result, "status": "success"} + except Exception as e: + print(e) + return {"err": "An error has occured", "status": "failed"}, 500 + return make_response(response) \ No newline at end of file diff --git a/backend/FacilityBooking/FacilitiesAPI.py b/backend/FacilityBooking/FacilitiesAPI.py index 117734fd8..5235d16a9 100644 --- a/backend/FacilityBooking/FacilitiesAPI.py +++ b/backend/FacilityBooking/FacilitiesAPI.py @@ -12,9 +12,9 @@ facilities_api = Blueprint("facilities", __name__) -# Used to convert CCA ID into CCA Name +# Used to convert CCA ID into CCA Name def conversion(ccaID): return str(db.CCA.find_one({"ccaID": ccaID})["ccaName"]) @@ -38,6 +38,58 @@ def all_facilities(): return make_response(response) +@ facilities_api.route('/available', methods=["GET"]) +@ cross_origin(supports_credentials=True) +def available_facilities_within_time(): + try: + all_facilities = list(db.Facilities.find({}, {"_id": 0}).sort("facilityID", 1)) + startTime = int(request.args.get('startTime')) if request.args.get('startTime') else 0 + endTime = int(request.args.get('endTime')) if request.args.get('endTime') else -1 + if endTime <= startTime: + return {"err": "Invalid start and end time", "status": "failed"}, 400 + + condition = { + "$and": [ + { + "startTime": { + "$lt": endTime + } + }, + { + "endTime": { + "$gt": startTime + } + } + ] + } + + pipeline = [ + {'$match': + condition + }, + {'$lookup': { + 'from': 'Facilities', + 'localField': 'facilityID', + 'foreignField': 'facilityID', + 'as': 'facility' + }}, + {'$unwind': {'path': '$facility', 'preserveNullAndEmptyArrays': True}}, + {'$addFields': { + 'facilityName': '$facility.facilityName', + 'facilityLocation': '$facility.facilityLocation' + }}, + {'$project': {'facilityID': 1, 'facilityName': 1, 'facilityLocation': 1, '_id': 0}}, + ] + + occupied_facilities = list(db.Bookings.aggregate(pipeline)) + available_facilities = list(filter(lambda fac: fac not in occupied_facilities, all_facilities)) + response = {"available_facilities": available_facilities, "status": "success"}, 200 + except Exception as e: + print(e) + return {"err": "An error has occurred", "status": "failed"}, 500 + return make_response(response) + + @ facilities_api.route('/facilities/', methods=["GET"]) @ cross_origin(supports_credentials=True) def get_facility_name(facilityID): @@ -248,6 +300,76 @@ def check_bookings(facilityID): return make_response(response) +@ facilities_api.route('/bookings', methods=["GET"]) +@ cross_origin(supports_credentials=True) +def check_bookings_within_time(): + try: + startTime = int(request.args.get('startTime')) if request.args.get('startTime') else 0 + endTime = int(request.args.get('endTime')) if request.args.get('endTime') else -1 + if endTime <= startTime: + return {"err": "Invalid start and end time", "status": "failed"}, 400 + + condition = { + "$and": [ + { + "startTime": { + "$lt": endTime + } + }, + { + "endTime": { + "$gt": startTime + } + } + ] + } + + pipeline = [ + {'$match': + condition + }, + {'$lookup': { + 'from': 'User', + 'localField': 'userID', + 'foreignField': 'userID', + 'as': 'profile' + }}, + {'$unwind': {'path': '$profile', 'preserveNullAndEmptyArrays': True}}, + {'$addFields': { + 'displayName': '$profile.displayName' + }}, + {'$lookup': { + 'from': 'CCA', + 'localField': 'ccaID', + 'foreignField': 'ccaID', + 'as': 'cca' + }}, + {'$unwind': {'path': '$cca', 'preserveNullAndEmptyArrays': True}}, + {'$addFields': { + 'ccaName': '$cca.ccaName' + }}, + {'$lookup': { + 'from': 'Facilities', + 'localField': 'facilityID', + 'foreignField': 'facilityID', + 'as': 'facility' + }}, + {'$unwind': {'path': '$facility', 'preserveNullAndEmptyArrays': True}}, + {'$addFields': { + 'facilityName': '$facility.facilityName' + }}, + {'$project': {'profile': 0, 'cca': 0, 'facility': 0, '_id': 0}} + ] + + data = list(db.Bookings.aggregate(pipeline)) + data.sort(key=lambda x: x.get('startTime')) + response = {"startTime": startTime, "endTime": endTime, "bookings_placed": data, "status": "success"}, 200 + except Exception as e: + print(e) + return {"err": "An error has occurred", "status": "failed"}, 500 + return make_response(response) + + @ facilities_api.route('/bookings', methods=['POST']) @ cross_origin(supports_credentials=True) def add_booking(): diff --git a/backend/GymFeatures/GymAPI.py b/backend/GymFeatures/GymAPI.py index 549a5588c..8d562bcd1 100644 --- a/backend/GymFeatures/GymAPI.py +++ b/backend/GymFeatures/GymAPI.py @@ -1,4 +1,3 @@ -from curses import keyname from operator import is_ from numpy import sort from db import * diff --git a/backend/main.py b/backend/main.py index e608a1e1e..d1b3bde24 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,6 +9,7 @@ from Supper.SupperAPI import supper_api from Authentication.AuthenticationAPI import authentication_api from GymFeatures.GymAPI import gym_api +from CrowdAnalyser.CrowdAPI import crowd_api; from db import * from gevent.pywsgi import WSGIServer from prometheus_flask_exporter import PrometheusMetrics @@ -26,6 +27,7 @@ app.register_blueprint(supper_api, url_prefix="/supper") app.register_blueprint(authentication_api, url_prefix="/auth") app.register_blueprint(gym_api, url_prefix="/gym") +app.register_blueprint(crowd_api, url_prefix="/crowd") @app.route("/") def hello(): diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index f77c9f61f..25bb5252d 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -9,6 +9,8 @@ services: expose: - 8080 restart: on-failure + env_file: + - ./secrets/rhapp_env networks: - intranet labels: diff --git a/docker-compose.yml b/docker-compose.yml index 89a012d8f..c881a71f3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,8 @@ services: expose: - 8080 restart: on-failure + env_file: + - ./secrets/rhapp_env networks: - intranet labels: diff --git a/frontend/package.json b/frontend/package.json index a0a7f392a..9ac904d55 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "0.1.0", + "version": "2.1.0", "private": true, "dependencies": { "@ant-design/icons": "^4.3.0", @@ -22,10 +22,11 @@ "antd": "^4.16.6", "antd-mobile": "^2.3.4", "axios": "^0.21.0", - "babel-plugin-import": "^1.13.3", + "babel-plugin-import": "^1.13.5", "bcrypt": "^5.0.0", "crypto-js": "^4.0.0", "customize-cra": "^1.0.0", + "date-fns": "^2.28.0", "dayjs": "^1.9.7", "dotenv": "^8.2.0", "dotenv-webpack": "^7.0.1", @@ -61,10 +62,12 @@ "web-vitals": "^0.2.4" }, "scripts": { + "analyze": "source-map-explorer 'build/static/js/*.js'", "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", + "docs": "typedoc", "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix" }, "eslintConfig": { @@ -101,6 +104,9 @@ "eslint": "^7.29.0", "eslint-config-prettier": "^6.15.0", "eslint-plugin-prettier": "^3.1.4", - "prettier": "^2.2.1" + "eslint-plugin-react": "^7.30.0", + "prettier": "^2.2.1", + "react-test-renderer": "^18.1.0", + "source-map-explorer": "^2.5.3" } } diff --git a/frontend/public/index.html b/frontend/public/index.html index c87b94ff6..2dc9cd059 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -9,13 +9,13 @@ /> - - - - - - - + + + + + + +