diff --git a/.gitignore b/.gitignore index de64807..549b6f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# # Miscellaneous *.class *.lock @@ -124,3 +125,157 @@ app.*.symbols !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock + +## Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ Flutter diff --git a/API/README.md b/API/README.md new file mode 100644 index 0000000..a7ab7a6 --- /dev/null +++ b/API/README.md @@ -0,0 +1,9 @@ +# Communication + +### How to run +```bash +docker-compose up --build +``` + +### Webapp +[Url](http://localhost/) diff --git a/API/webapp/.gitignore b/API/webapp/.gitignore new file mode 100644 index 0000000..111de12 --- /dev/null +++ b/API/webapp/.gitignore @@ -0,0 +1,2 @@ +# Docker volumes +/volumes/mongo \ No newline at end of file diff --git a/API/webapp/docker-compose.yml b/API/webapp/docker-compose.yml new file mode 100644 index 0000000..c098f93 --- /dev/null +++ b/API/webapp/docker-compose.yml @@ -0,0 +1,33 @@ +version: "3.7" + +services: + flask: + build: ./flask + container_name: flask + restart: always + environment: + - APP_NAME=SmartGainsDatabase + expose: + - 8080 + + nginx: + build: ./nginx + container_name: nginx + restart: always + ports: + - "80:80" + + mongo: + image: mongo:4.4.3 + env_file: ./.env + container_name: mongo + environment: + - MONGO_INITDB_ROOT_USERNAME=$MONGO_ROOT_USERNAME + - MONGO_INITDB_ROOT_PASSWORD=$MONGO_ROOT_PASSWORD + - MONGO_INITDB_DATABASE=admin + volumes: + - ./volumes/mongo/data/db/:/data/db/ + - ./volumes/mongo/log/:/var/log/mongodb/ + expose: + - 27017 + command: ["--bind_ip", "0.0.0.0"] \ No newline at end of file diff --git a/API/webapp/flask/Dockerfile b/API/webapp/flask/Dockerfile new file mode 100644 index 0000000..76a76dc --- /dev/null +++ b/API/webapp/flask/Dockerfile @@ -0,0 +1,14 @@ +# Use the Python3.7.2 image +FROM python:3.7.2-stretch + +# Set the working directory to /app +WORKDIR /webapp + +# Copy the current directory contents into the container at /challenge +ADD . /webapp + +# Install the dependencies +RUN pip3 install -r requirements.txt + +# run the command to start uWSGI +CMD ["uwsgi", "app.ini"] \ No newline at end of file diff --git a/API/webapp/flask/app.ini b/API/webapp/flask/app.ini new file mode 100644 index 0000000..bdf5a7d --- /dev/null +++ b/API/webapp/flask/app.ini @@ -0,0 +1,10 @@ +[uwsgi] +wsgi-file = run.py +callable = app +socket = :8080 +processes = 4 +threads = 2 +master = true +chmod-socket = 660 +vacuum = true +die-on-term = true \ No newline at end of file diff --git a/API/webapp/flask/app/__init__.py b/API/webapp/flask/app/__init__.py new file mode 100644 index 0000000..ad3de73 --- /dev/null +++ b/API/webapp/flask/app/__init__.py @@ -0,0 +1,5 @@ +from flask import Flask, render_template + +app = Flask(__name__, static_folder='static', static_url_path='') + +from app import views \ No newline at end of file diff --git a/API/webapp/flask/app/static/robots.txt b/API/webapp/flask/app/static/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/API/webapp/flask/app/static/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/API/webapp/flask/app/templates/404.html b/API/webapp/flask/app/templates/404.html new file mode 100644 index 0000000..d89a149 --- /dev/null +++ b/API/webapp/flask/app/templates/404.html @@ -0,0 +1,14 @@ + + + + +404 - this page does not exist + + + +

+ 404 Not Found +

+ + \ No newline at end of file diff --git a/API/webapp/flask/app/views.py b/API/webapp/flask/app/views.py new file mode 100644 index 0000000..5a15e2d --- /dev/null +++ b/API/webapp/flask/app/views.py @@ -0,0 +1,318 @@ +from flask import request, jsonify +from app import app +from flask_cors import cross_origin +import pymongo +from bson.json_util import dumps + + +# mock database created with pymongo +myclient = pymongo.MongoClient("mongodb://mongo:27017/", username='admin', password='admin', authSource='admin', authMechanism='SCRAM-SHA-256') +# create or get db +mydb = myclient["SmartGainsTest"] +# create or get collection +mycol = mydb["CollectionTest"] + +mydict = {"init": "yes"} +x = mycol.insert_one(mydict) + + +# debug material +@app.route("/") +@cross_origin() +def index(): + return {} + + +@app.route('/user/inf', methods=['PUT']) +@cross_origin() +def UserInf(): + + username = request.args.get("username") + + # curl http://localhost:8393/user/inf?username= + # -d "weight=" -d "height=" -d "gender=" -d "birth=" + # -X PUT + + if request.method == 'PUT': + + weight = request.form['weight'] + height = request.form['height'] + gender = request.form['gender'] + birth = request.form['dateOfBirth'] + objective = request.form["dailyGoal"] + + result = [] + for x in mycol.find({"username": username}): + result.append(x) + + if result != []: + + mydict = {"username": username} + update_to = {"$set" : {'weight' : weight}} + x = mycol.update_one(mydict, update_to) + update_to = {"$set" : {'height' : height}} + x = mycol.update_one(mydict, update_to) + update_to = {"$set" : {'gender' : gender}} + x = mycol.update_one(mydict, update_to) + update_to = {"$set" : {'dateOfBirth' : birth}} + x = mycol.update_one(mydict, update_to) + update_to = {"$set" : {'dailyGoal' : objective}} + x = mycol.update_one(mydict, update_to) + + return dumps(mycol.find({"username": username})) + + return jsonify({"command": "Failed", "error": "Could not update the user"}) + + return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +# @app.route('/user/activity', methods=['PUT']) +# @cross_origin() +# def UserActivity(): + +# username = request.args.get("username") + +# # curl http://localhost:8393/user/activity?username= +# # -d "activity=" +# # -X PUT + +# if request.method == 'PUT': + +# activity = request.form['activity'] + +# result = [] +# for x in mycol.find({"username": username}): +# result.append(x) + +# if result != []: + +# mydict = {"username": username} +# update_to = {"$set" : {'activity' : activity}} +# x = mycol.update_one(mydict, update_to) + +# return dumps(mycol.find({"username": username})) + +# return jsonify({"command": "Failed", "error": "Could not update the user"}) + +# return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +# @app.route('/user/goals', methods=['PUT']) +# @cross_origin() +# def UserGoals(): + +# username = request.args.get("username") + +# # curl http://localhost:8393/user/activity?username= +# # -d "goals=" -d "dailyGoal=" +# # -X PUT + +# if request.method == 'PUT': + +# goals = request.form['goals'] +# dailyGoal = request.form['dailyGoal'] + +# result = [] +# for x in mycol.find({"username": username}): +# result.append(x) + +# if result != []: + +# mydict = {"username": username} +# update_to = {"$set" : {'goals' : goals}} +# x = mycol.update_one(mydict, update_to) +# update_to = {"$set" : {'dailyGoal' : dailyGoal}} +# x = mycol.update_one(mydict, update_to) + +# return dumps(mycol.find({"username": username})) + +# return jsonify({"command": "Failed", "error": "Could not update the user"}) + +# return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +@app.route('/user', methods=['GET', 'POST', 'PUT']) +@cross_origin() +def User(): + + username = request.args.get("username") + + # curl http://localhost:8393/user?username= + # -d "password=" -d "email="" + # -X POST + if request.method == 'POST': + + password = request.form['password'] + email = request.form['email'] + + result = [] + for x in mycol.find({"username": username}): + result.append(x) + + if result == []: + mydict = { "username": username, "password": password, "email": email} + x = mycol.insert_one(mydict) + + # return identification and photo + return dumps(mycol.find({"username": username})) + + return jsonify({"command": "Failed", "error": "Username already exists"}) + + + # curl http://localhost:8393/user?username= + # -X GET + elif request.method == 'GET': + + result = [] + for x in mycol.find({"username": username}): + result.append(x) + + return dumps(result) + + # curl http://localhost:8393/user?username= + # -d "weight=" + # -X GET + elif request.method == 'PUT': + + weight = request.form['weight'] + exercise = request.form['exercise'] + + mydict = {"username": username} + update_to = {"$set" : {exercise : weight}} + x = mycol.update_one(mydict, update_to) + + return dumps(mycol.find({"username": username})) + + + return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +# @app.route('/databackend', methods=['PUT']) +# @cross_origin() +# def DataBackend(): +# # curl http://localhost:8393/databackend +# # -d "repCount=" -d "goodRepCount=" -d "username=" +# # -X PUT + +# if request.method == 'PUT': + +# repCount = request.form['repCount'] +# goodRepCount = request.form['goodRepCount'] +# badRepCount = request.form['badRepCount'] +# username = request.form["username"] + +# result = [] +# for x in mycol.find({"username": username}): +# result.append(x) + +# if result != []: +# mydict = {"username": username} +# update_to = {"$set" : {'repCount' : repCount}} +# x = mycol.update_one(mydict, update_to) +# update_to = {"$set" : {'goodRepCount' : goodRepCount}} +# x = mycol.update_one(mydict, update_to) +# update_to = {"$set" : {'badRepCount' : badRepCount}} +# x = mycol.update_one(mydict, update_to) + +# return dumps(mycol.find({"username": username})) + +# return jsonify({"command": "Failed", "error": "Could not update the user"}) + +# return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +# @app.route('/datafrontend', methods=['PUT']) +# @cross_origin() +# def DataFrontend(): +# # curl http://localhost:8393/datafrontend +# # -d "hours:" -d "day:" -d "username=" +# # -X PUT + +# if request.method == 'PUT': + +# hours = request.form['hours'] +# days = request.form['day'] +# username = request.form["username"] + +# result = [] +# for x in mycol.find({"username": username}): +# result.append(x) + +# if result != []: +# mydict = {"username": username} +# update_to = {"$set" : {'hoursDay' : {days: hours}}} +# x = mycol.update_one(mydict, update_to) + +# return dumps(mycol.find({"username": username})) + +# return jsonify({"command": "Failed", "error": "Could not update the user"}) + +# return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + +@app.route('/potencial', methods=['PUT']) +@cross_origin() +def Potencial(): + # curl http://localhost:8393/datafrontend + # -d "potencial:" -d "exercise:" -d "username=" + # -X PUT + + if request.method == 'PUT': + + exercise = request.form['exercise'] + potencial = request.form['class'] + percetClass = request.form['rate'] + username = request.form["user"] + + result = [] + for x in mycol.find({"username": username}): + result.append(x) + + if result != []: + + pot = {exercise: {"potencialEx": potencial, "percetClass": percetClass}} + + mydict = {"username": username} + update_to = {"$set" : {"potencial" : pot}} + x = mycol.update_one(mydict, update_to) + + return dumps(mycol.find({"username": username})) + + return jsonify({"command": "Failed", "error": "Could not update the user"}) + + return jsonify({"command": "Failed", "error": "Request method incorrect"}) + + + + +# @app.route('/gifs', methods=['PUT']) +# @cross_origin() +# def Gifs(): +# # curl http://localhost:8393/gifs +# # -d "gifs:" "username=" +# # -X PUT + +# username = request.args.get("username") + +# if request.method == 'PUT': + +# gifs = request.form['gifs'] + +# result = [] +# for x in mycol.find({"username": username}): +# result.append(x) + +# if result != []: +# mydict = {"username": username} +# update_to = {"$set" : {'gifs' : gifs}} +# x = mycol.update_one(mydict, update_to) + +# return dumps(mycol.find({"username": username})) + +# return jsonify({"command": "Failed", "error": "Could not update the user"}) + +# return jsonify({"command": "Failed", "error": "Request method incorrect"}) + +@app.errorhandler(404) +def page_not_found(e): + return jsonify({"command": "Failed", "error": "Error handling the request"}) \ No newline at end of file diff --git a/API/webapp/flask/requirements.txt b/API/webapp/flask/requirements.txt new file mode 100644 index 0000000..8c51908 --- /dev/null +++ b/API/webapp/flask/requirements.txt @@ -0,0 +1,4 @@ +Flask==2.1.1 +uWSGI +flask_cors +pymongo \ No newline at end of file diff --git a/API/webapp/flask/run.py b/API/webapp/flask/run.py new file mode 100644 index 0000000..c81cb8d --- /dev/null +++ b/API/webapp/flask/run.py @@ -0,0 +1,5 @@ +from app import app + +if __name__ == "__main__": + app.run(host='0.0.0.0') + \ No newline at end of file diff --git a/API/webapp/nginx/Dockerfile b/API/webapp/nginx/Dockerfile new file mode 100644 index 0000000..137f097 --- /dev/null +++ b/API/webapp/nginx/Dockerfile @@ -0,0 +1,8 @@ +# Use the Nginx image +FROM nginx + +# Remove the default nginx.conf +RUN rm /etc/nginx/conf.d/default.conf + +# Replace with our own nginx.conf +COPY nginx.conf /etc/nginx/conf.d/ \ No newline at end of file diff --git a/API/webapp/nginx/nginx.conf b/API/webapp/nginx/nginx.conf new file mode 100644 index 0000000..1ffac49 --- /dev/null +++ b/API/webapp/nginx/nginx.conf @@ -0,0 +1,10 @@ +server { + + listen 80; + + location / { + include uwsgi_params; + uwsgi_pass flask:8080; + } + +} \ No newline at end of file diff --git a/API/webapp/volumes/.gitkeep b/API/webapp/volumes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/ExerciseData/curl_age_f.csv b/Backend/ExerciseData/curl_age_f.csv new file mode 100644 index 0000000..4258322 --- /dev/null +++ b/Backend/ExerciseData/curl_age_f.csv @@ -0,0 +1,17 @@ +Age,Beginner,Novice,Intermediate,Advanced,Elite +15,6,12,21,33,47 +20,6,14,24,38,53 +25,6,14,25,39,55 +30,6,14,25,39,55 +35,6,14,25,39,55 +40,6,14,25,39,55 +45,6,13,23,37,52 +50,6,12,22,34,49 +55,5,11,20,32,45 +60,5,10,19,29,41 +65,4,9,17,26,37 +70,4,8,15,23,33 +75,4,8,13,21,30 +80,3,7,12,19,27 +85,3,6,11,17,24 +90,3,5,10,15,22 diff --git a/Backend/ExerciseData/curl_age_m.csv b/Backend/ExerciseData/curl_age_m.csv new file mode 100644 index 0000000..a98816f --- /dev/null +++ b/Backend/ExerciseData/curl_age_m.csv @@ -0,0 +1,17 @@ +Age,Beginner,Novice,Intermediate,Advanced,Elite +15,15,25,40,58,77 +20,17,29,46,66,89 +25,17,30,47,68,91 +30,17,30,47,68,91 +35,17,30,47,68,91 +40,17,30,47,68,91 +45,16,28,44,64,86 +50,15,27,42,60,81 +55,14,25,39,56,75 +60,13,22,35,51,68 +65,12,20,32,46,62 +70,11,18,29,41,55 +75,9,16,26,37,50 +80,8,15,23,33,44 +85,8,13,20,30,40 +90,7,12,18,27,36 diff --git a/Backend/ExerciseData/curl_bw_f.csv b/Backend/ExerciseData/curl_bw_f.csv new file mode 100644 index 0000000..d6e3806 --- /dev/null +++ b/Backend/ExerciseData/curl_bw_f.csv @@ -0,0 +1,18 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +40,3,8,17,28,41 +45,4,10,19,30,44 +50,5,11,20,32,47 +55,5,12,22,34,49 +60,6,13,23,36,51 +65,7,14,25,38,54 +70,8,15,26,40,56 +75,8,16,27,41,57 +80,9,17,29,43,59 +85,10,18,30,44,61 +90,10,19,31,46,63 +95,11,20,32,47,64 +100,12,21,33,49,66 +105,12,22,34,50,67 +110,13,23,35,51,69 +115,14,23,36,52,70 +120,14,24,37,53,71 diff --git a/Backend/ExerciseData/curl_bw_m.csv b/Backend/ExerciseData/curl_bw_m.csv new file mode 100644 index 0000000..1ca5808 --- /dev/null +++ b/Backend/ExerciseData/curl_bw_m.csv @@ -0,0 +1,20 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +50,9,18,30,46,64 +55,11,20,34,50,69 +60,13,23,37,54,73 +65,14,25,40,58,78 +70,16,27,43,61,82 +75,18,30,45,64,85 +80,19,32,48,67,89 +85,21,34,50,70,93 +90,23,36,53,73,96 +95,24,38,55,76,99 +100,26,40,58,79,102 +105,27,42,60,82,105 +110,29,43,62,84,108 +115,30,45,64,87,111 +120,32,47,66,89,114 +125,33,49,68,91,116 +130,34,50,70,94,119 +135,36,52,72,96,121 +140,37,54,74,98,124 \ No newline at end of file diff --git a/Backend/ExerciseData/curl_rates_f.csv b/Backend/ExerciseData/curl_rates_f.csv new file mode 100644 index 0000000..e0433c3 --- /dev/null +++ b/Backend/ExerciseData/curl_rates_f.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +5,22,52,80,92 diff --git a/Backend/ExerciseData/curl_rates_m.csv b/Backend/ExerciseData/curl_rates_m.csv new file mode 100644 index 0000000..5e7f2a6 --- /dev/null +++ b/Backend/ExerciseData/curl_rates_m.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +6,21,51,80,95 diff --git a/Backend/ExerciseData/pushup_age_f.csv b/Backend/ExerciseData/pushup_age_f.csv new file mode 100644 index 0000000..e18144d --- /dev/null +++ b/Backend/ExerciseData/pushup_age_f.csv @@ -0,0 +1,17 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +15, 1, 1,12,27,43 +20, 1,5,18,35,54 +25, 1,5,19,37,56 +30, 1,5,19,37,56 +35, 1,5,19,37,56 +40, 1,5,19,37,56 +45, 1,4,16,33,51 +50, 1,1,14,29,46 +55, 1, 1,10,25,41 +60, 1, 1,8,20,35 +65, 1, 1,5,15,28 +70, 1, 1,1,11,22 +75, 1, 1, 1,7,17 +80, 1, 1, 1,4,12 +85, 1, 1, 1, 1,9 +90, 1, 1, 1, 1,5 diff --git a/Backend/ExerciseData/pushup_age_m.csv b/Backend/ExerciseData/pushup_age_m.csv new file mode 100644 index 0000000..df7822e --- /dev/null +++ b/Backend/ExerciseData/pushup_age_m.csv @@ -0,0 +1,17 @@ +Age,Beginner,Novice,Intermediate,Advanced,Elite +15, 1,11,30,54,80 +20, 1,16,39,66,95 +25,1,18,41,68,99 +30,1,18,41,68,99 +35,1,18,41,68,99 +40,1,18,41,68,99 +45, 1,15,37,63,92 +50, 1,12,33,57,85 +55, 1,10,28,51,76 +60, 1,7,23,44,67 +65, 1,4,18,37,58 +70, 1, 1,13,30,49 +75, 1, 1,9,24,40 +80, 1, 1,6,18,33 +85, 1, 1,2,13,27 +90, 1, 1, 1,9,21 diff --git a/Backend/ExerciseData/pushup_bw_f.csv b/Backend/ExerciseData/pushup_bw_f.csv new file mode 100644 index 0000000..1c0ccf6 --- /dev/null +++ b/Backend/ExerciseData/pushup_bw_f.csv @@ -0,0 +1,18 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +40, 1,4,19,39,61 +45, 1,5,20,38,59 +50, 1,6,20,37,57 +55, 1,6,20,36,54 +60, 1,7,19,35,52 +65, 1,7,19,34,50 +70, 1,7,18,33,48 +75, 1,7,18,31,46 +80, 1,6,17,30,45 +85, 1,6,16,29,43 +90, 1,6,16,28,41 +95, 1,6,15,27,40 +100, 1,6,15,26,38 +105, 1,5,14,25,37 +110, 1,5,13,24,36 +115, 1,5,13,23,35 +120, 1,4,12,22,33 diff --git a/Backend/ExerciseData/pushup_bw_m.csv b/Backend/ExerciseData/pushup_bw_m.csv new file mode 100644 index 0000000..1e1ca59 --- /dev/null +++ b/Backend/ExerciseData/pushup_bw_m.csv @@ -0,0 +1,20 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +50,1,16,42,73,108 +55,1,17,42,72,105 +60,1,18,42,70,102 +65,2,19,42,69,99 +70,3,19,41,67,96 +75,4,19,41,66,93 +80,5,20,40,64,91 +85,5,20,39,63,88 +90,5,19,39,61,86 +95,6,19,38,60,83 +100,6,19,37,58,81 +105,6,19,37,57,79 +110,6,19,36,56,77 +115,6,18,35,54,75 +120,6,18,34,53,73 +125,6,18,34,52,71 +130,6,17,33,51,70 +135,6,17,32,49,68 +140,6,17,32,48,66 \ No newline at end of file diff --git a/Backend/ExerciseData/pushup_rates_f.csv b/Backend/ExerciseData/pushup_rates_f.csv new file mode 100644 index 0000000..9346713 --- /dev/null +++ b/Backend/ExerciseData/pushup_rates_f.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +11,22,51,81,95 diff --git a/Backend/ExerciseData/pushup_rates_m.csv b/Backend/ExerciseData/pushup_rates_m.csv new file mode 100644 index 0000000..38008cc --- /dev/null +++ b/Backend/ExerciseData/pushup_rates_m.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +5,20,50,80,95 diff --git a/Backend/ExerciseData/squat_age_f.csv b/Backend/ExerciseData/squat_age_f.csv new file mode 100644 index 0000000..993bc62 --- /dev/null +++ b/Backend/ExerciseData/squat_age_f.csv @@ -0,0 +1,17 @@ +Age,Beginner,Novice,Intermediate,Advanced,Elite +15,25,41,62,88,116 +20,29,47,71,100,132 +25,30,48,73,103,136 +30,30,48,73,103,136 +35,30,48,73,103,136 +40,30,48,73,103,136 +45,28,46,69,97,129 +50,26,43,65,92,121 +55,24,40,60,85,112 +60,22,36,55,77,102 +65,20,33,50,70,92 +70,18,29,44,63,83 +75,16,26,40,56,74 +80,14,24,36,50,66 +85,13,21,32,45,59 +90,12,19,29,40,54 diff --git a/Backend/ExerciseData/squat_age_m.csv b/Backend/ExerciseData/squat_age_m.csv new file mode 100644 index 0000000..e543536 --- /dev/null +++ b/Backend/ExerciseData/squat_age_m.csv @@ -0,0 +1,17 @@ +Age,Beginner,Novice,Intermediate,Advanced,Elite +15,55,80,111,147,187 +20,62,91,127,168,214 +25,64,93,130,173,219 +30,64,93,130,173,219 +35,64,93,130,173,219 +40,64,93,130,173,219 +45,61,89,123,164,208 +50,57,83,116,154,195 +55,53,77,107,142,180 +60,48,70,98,130,165 +65,44,63,88,117,149 +70,39,57,79,105,134 +75,35,51,71,94,119 +80,31,46,63,84,107 +85,28,41,57,75,96 +90,25,37,51,68,86 diff --git a/Backend/ExerciseData/squat_bw_f.csv b/Backend/ExerciseData/squat_bw_f.csv new file mode 100644 index 0000000..ab24afd --- /dev/null +++ b/Backend/ExerciseData/squat_bw_f.csv @@ -0,0 +1,18 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +40,17,31,51,75,101 +45,20,36,56,81,109 +50,23,39,61,87,115 +55,26,43,65,92,122 +60,29,47,70,97,128 +65,32,50,74,102,133 +70,34,53,78,106,138 +75,37,56,81,111,143 +80,39,59,85,115,148 +85,41,62,88,119,152 +90,44,65,91,123,157 +95,46,68,95,126,161 +100,48,70,98,130,165 +105,50,73,101,133,169 +110,52,75,103,136,172 +115,54,77,106,140,176 +120,56,80,109,143,179 diff --git a/Backend/ExerciseData/squat_bw_m.csv b/Backend/ExerciseData/squat_bw_m.csv new file mode 100644 index 0000000..4b9a998 --- /dev/null +++ b/Backend/ExerciseData/squat_bw_m.csv @@ -0,0 +1,20 @@ +Bodyweight,Beginner,Novice,Intermediate,Advanced,Elite +50,33,52,76,104,136 +55,40,60,86,116,149 +60,47,68,95,127,161 +65,53,76,104,137,173 +70,59,83,113,147,184 +75,66,91,122,157,195 +80,72,98,130,166,205 +85,78,105,138,175,215 +90,83,112,146,184,225 +95,89,118,153,192,234 +100,95,125,160,201,243 +105,100,131,168,209,252 +110,106,137,174,216,260 +115,111,143,181,224,269 +120,116,149,188,231,277 +125,121,155,194,238,284 +130,126,160,201,245,292 +135,131,166,207,252,299 +140,136,171,213,259,307 diff --git a/Backend/ExerciseData/squat_rates_f.csv b/Backend/ExerciseData/squat_rates_f.csv new file mode 100644 index 0000000..0f3c74d --- /dev/null +++ b/Backend/ExerciseData/squat_rates_f.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +5,21,51,80,95 diff --git a/Backend/ExerciseData/squat_rates_m.csv b/Backend/ExerciseData/squat_rates_m.csv new file mode 100644 index 0000000..49c1568 --- /dev/null +++ b/Backend/ExerciseData/squat_rates_m.csv @@ -0,0 +1,2 @@ +Beginner,Novice,Intermediate,Advanced,Elite +5,20,51,81,95 diff --git a/Backend/README.txt b/Backend/README.txt new file mode 100644 index 0000000..540eeeb --- /dev/null +++ b/Backend/README.txt @@ -0,0 +1,11 @@ +# Backend + +### How to run +For receiving frames through websocket: +```bash +python main.py +``` +For debugging: +```bash +python app.py +``` diff --git a/Backend/RepetitionGifs/rep_1.gif b/Backend/RepetitionGifs/rep_1.gif deleted file mode 100644 index a330367..0000000 Binary files a/Backend/RepetitionGifs/rep_1.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_10.gif b/Backend/RepetitionGifs/rep_10.gif deleted file mode 100644 index 2efba32..0000000 Binary files a/Backend/RepetitionGifs/rep_10.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_11.gif b/Backend/RepetitionGifs/rep_11.gif deleted file mode 100644 index 04aac52..0000000 Binary files a/Backend/RepetitionGifs/rep_11.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_12.gif b/Backend/RepetitionGifs/rep_12.gif deleted file mode 100644 index f5e3478..0000000 Binary files a/Backend/RepetitionGifs/rep_12.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_13.gif b/Backend/RepetitionGifs/rep_13.gif deleted file mode 100644 index e603fd9..0000000 Binary files a/Backend/RepetitionGifs/rep_13.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_14.gif b/Backend/RepetitionGifs/rep_14.gif deleted file mode 100644 index 9e41ad3..0000000 Binary files a/Backend/RepetitionGifs/rep_14.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_15.gif b/Backend/RepetitionGifs/rep_15.gif deleted file mode 100644 index 54ed6ab..0000000 Binary files a/Backend/RepetitionGifs/rep_15.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_16.gif b/Backend/RepetitionGifs/rep_16.gif deleted file mode 100644 index abdfaf7..0000000 Binary files a/Backend/RepetitionGifs/rep_16.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_2.gif b/Backend/RepetitionGifs/rep_2.gif deleted file mode 100644 index 34c9e69..0000000 Binary files a/Backend/RepetitionGifs/rep_2.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_3.gif b/Backend/RepetitionGifs/rep_3.gif deleted file mode 100644 index 84441a0..0000000 Binary files a/Backend/RepetitionGifs/rep_3.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_4.gif b/Backend/RepetitionGifs/rep_4.gif deleted file mode 100644 index 5e42c0b..0000000 Binary files a/Backend/RepetitionGifs/rep_4.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_5.gif b/Backend/RepetitionGifs/rep_5.gif deleted file mode 100644 index acb853a..0000000 Binary files a/Backend/RepetitionGifs/rep_5.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_6.gif b/Backend/RepetitionGifs/rep_6.gif deleted file mode 100644 index 7b1496e..0000000 Binary files a/Backend/RepetitionGifs/rep_6.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_7.gif b/Backend/RepetitionGifs/rep_7.gif deleted file mode 100644 index 9df3530..0000000 Binary files a/Backend/RepetitionGifs/rep_7.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_8.gif b/Backend/RepetitionGifs/rep_8.gif deleted file mode 100644 index ab54bda..0000000 Binary files a/Backend/RepetitionGifs/rep_8.gif and /dev/null differ diff --git a/Backend/RepetitionGifs/rep_9.gif b/Backend/RepetitionGifs/rep_9.gif deleted file mode 100644 index fd14e91..0000000 Binary files a/Backend/RepetitionGifs/rep_9.gif and /dev/null differ diff --git a/Backend/curl.py b/Backend/curl.py index 53ae2e5..64fd9f6 100644 --- a/Backend/curl.py +++ b/Backend/curl.py @@ -122,9 +122,10 @@ def create_draw(self, joints, angles): return drawing def check_hip(self, angle): + return True """ Form Checking method """ # Check if hip posture is good - if angle < 145 and self.stage != 'idle': + if angle < 160 and self.stage != 'idle': self.hip_fail = True return False @@ -192,15 +193,15 @@ def count(self, angle): #Check all form errors if self.hip_fail: - self.framework.add_feedback("curl_back") + self.framework.add_feedback("Dont bend forward") print("[FeedBack] Dont bend forward") if self.knee_fail: - self.framework.add_feedback("curl_knee") + self.framework.add_feedback("Dont bend your knees") print("[FeedBack] Dont bend your knees") if not self.perfect_tag: - self.framework.add_feedback("curl_rom") + self.framework.add_feedback("Not full range motion") print("[FeedBack] Not full motion rep >:(\n\n") if self.perfect_tag and not self.knee_fail and not self.hip_fail: diff --git a/Backend/deadlift.py b/Backend/deadlift.py new file mode 100644 index 0000000..9e1a152 --- /dev/null +++ b/Backend/deadlift.py @@ -0,0 +1,250 @@ +from subprocess import check_call +from draw import DrawInfo + +import numpy as np +import mediapipe as mp + +mp_pose = mp.solutions.pose + +class Deadlift: + """Squat exercise module.""" + + def __init__(self): + + # Framework Refernece given by the framework itself + self.framework = None + + # Count of the current frame since the exercise started + self.start_frame = 0 + + # Tags for the counting logic + self.knee_fail = False + self.back_fail = False + self.perfect_tag = False # Rep was perfect (in the range of motion requirement) + self.completed = False # Rep was done + self.stage = "idle" # "up" or "down" keeps track of the current movement + + # Cues related values + self.start_angle = 170 # less then this is not a good rep (bad range of motion) + self.min_vizibility = 0.7 + + # Debugging Counter of reps + self.counter = 0 + + def analyze_frame(self, frame_count, landmarks): + """ | Analyzes frame | calls method of framework when rep is finished with the rep information""" + + # Sync frame count + self.frame_count = frame_count + + if landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].z< landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].z: + # Get right key positions + shoulder = [landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].x,landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].y] + hip = [landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value].x,landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value].y] + knee = [landmarks[mp_pose.PoseLandmark.RIGHT_KNEE.value].x,landmarks[mp_pose.PoseLandmark.RIGHT_KNEE.value].y] + elbow = [landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value].x,landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value].y] + + if landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.RIGHT_HIP.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.RIGHT_KNEE.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.RIGHT_ELBOW.value].visibility < self.min_vizibility: + return None + else: + + # Get left key positions + shoulder = [landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x,landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y] + hip = [landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].x,landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].y] + knee = [landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].x,landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].y] + elbow = [landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].x,landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].y] + + if landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].visibility < self.min_vizibility: + return None + if landmarks[mp_pose.PoseLandmark.LEFT_ELBOW.value].visibility < self.min_vizibility: + return None + + + # Calculate angle + hip_angle = self.calculate_angle(knee, hip, shoulder) + + # Right Curl counter logic + self.count(shoulder, knee, elbow, hip_angle) + + # Return body form feed + return self.create_draw((shoulder, hip, knee, elbow), hip_angle) + + def create_draw(self, joints, angles): + """ Called every frame to give graphycal feedback of the joints (good or bad form)""" + + drawing = DrawInfo() + + drawing.add_point('shoulder', joints[0][0], joints[0][1]) + drawing.add_point('hip', joints[1][0], joints[1][1]) + drawing.add_point('knee', joints[2][0], joints[2][1]) + drawing.add_point('elbow', joints[3][0], joints[3][1]) + + + + #drawing.add_segment('shoulder', 'hip', True) # Back + drawing.add_segment('shoulder', 'elbow', True) # Upper leg + + + good = self.check_knee(joints[2], joints[1], joints[0], joints[3]) + drawing.add_segment('hip', 'knee', good) # Lower leg + + good = self.check_back(joints[1], joints[0], joints[3]) + drawing.add_segment('hip', 'shoulder', good) + + return drawing + + def check_knee(self, knee, hip, shoulder, elbow): + """ Form Checking method """ + + # shoulder/elbow for reference + offset = abs(shoulder[1] - elbow[1])/2 + print("knee: " + str(knee[1]) + "hip: " + str(hip[1])) + + # hip in relation to knee + if (hip[1] > knee[1]-offset) and self.stage != 'idle': + + self.knee_fail = True + return False + + return True + + def check_back(self, hip, shoulder, elbow): + """ Form Checking method """ + + # shoulder/elbow for reference + offset = abs(shoulder[1] - elbow[1])/2 + + # hip in relation to shoulder + if (hip[1] < shoulder[1]-offset) and self.stage != 'idle': + + self.back_fail = True + return False + + + return True + + + def count(self, shoulder, knee, elbow, angle): + """ Responsible for counting reps and keep track of range of motion type problems""" + + offset = abs(shoulder[1] - elbow[1])/3 + + if elbow[1] < knee[1] - offset and self.stage == 'idle' and not self.completed: + # Started motion + + # Save current frame as the start of the rep + self.start_frame = self.frame_count + # Update state + self.stage = "up" + print("\n[State] Movement Started ! \n") + + if angle > self.start_angle and not self.perfect_tag: + # Perfected motion rep + + # Update flags + self.completed = True + self.perfect_tag =True + + # Update state + self.stage="down" + + print("[State] Maximum reached...\n") + + if elbow[1] > knee[1] - offset and self.stage =='down' and self.completed: + # Completed movement + + # Update counter + self.counter +=1 + # Update state + self.stage = "idle" + + # Call framework method to announce that the rep has ended + self.framework.repetition_done(self.start_frame) + + print("[State] Rep Finished...\n") + print("[Info] Rep Count: " + str(self.counter) + "\n\n") + + #Check all form errors + if self.knee_fail: + self.framework.add_feedback("Dont let your heap beneath your knees") + print("[FeedBack] is not a squat boy") + + if self.back_fail: + self.framework.add_feedback("Back shouldnt be paralel to ground") + print("[FeedBack] Back shouldnt be paralel to ground") + + if not self.perfect_tag: + self.framework.add_feedback("Not full range motion") + print("[FeedBack] Not full motion rep >:(\n\n") + + if self.perfect_tag and not self.back_fail and not self.knee_fail: + print("[FeedBack] Good rep :)\n") + + + # Reset flags + self.knee_fail = False + self.perfect_tag = False + self.back_fail = False + self.completed = False + + # Get a frame's bounds + def get_bounds(self, landmarks) -> tuple: + + # If it's right facing... + if landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].z < landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].z: + x = landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].x + max_y = landmarks[mp_pose.PoseLandmark.RIGHT_SHOULDER.value].y + min_y = landmarks[mp_pose.PoseLandmark.RIGHT_ANKLE.value].y + + # The padding will be 10% from max to min + padding = (max_y - min_y) / 10 + # Left facing + else: + x = landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].x + max_y = landmarks[mp_pose.PoseLandmark.LEFT_SHOULDER.value].y + min_y = landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].y + + # The padding will be 10% from max to min + padding = (max_y - min_y) / 10 + + # Adds the padding + upper_left = [x + padding * 8, max_y + padding * 2] + lower_right = [x - padding * 8, min_y - padding * 2] + + def clamp(n, smallest, largest): + return max(smallest, min(n, largest)) + + #Clam the values + upper_left = [clamp(upper_left[0], 0, 1), clamp(upper_left[1], 0, 1)] + lower_right = [clamp(lower_right[0],0,1), clamp(lower_right[1],0,1)] + + return upper_left, lower_right + + def calculate_angle(self, a_,b_,c_): + """ + Utiltity method to calculate angle abc given those 3 coordinates + """ + + angle = None + + a = np.array(a_) + b = np.array(b_) + c = np.array(c_) + + radians = np.arctan2(c[1]-b[1], c[0]-b[0]) - np.arctan2(a[1]-b[1], a[0]-b[0]) + angle = np.abs(radians*180.0/np.pi) + + if angle >180.0: + angle = 360-angle + + return angle \ No newline at end of file diff --git a/Backend/framework.py b/Backend/framework.py index 1de6a38..72cf22f 100644 --- a/Backend/framework.py +++ b/Backend/framework.py @@ -4,6 +4,7 @@ from PIL import Image, ImageDraw # For creating gifs from protocol import * # For sending messages import base64 # For encoding gifs +import strength_level # For calculating strength mp_pose = mp.solutions.pose @@ -182,11 +183,14 @@ def get_distance(p, q): else: # If the gesture is done and the detection timer is up, the gesture is computed if self.set_gesture_count >= self.SET_gesture_DETECT: - #Start timer! + # Start timer! self.gesture_timer_state = True self.gesture_timer_count = 0 self.set_gesture_count = 0 + # Inform the frontend + self.send_message(GestureDetected()) + #Print the timer duration if not self.started_set: print(f'Starting {self.gesture_timer_max / self.FPS}s timer...') @@ -209,8 +213,6 @@ def ccw(A,B,C): if not (ccw(R1,L1,L2) != ccw(R2,L1,L2) and ccw(R1,R2,L1) != ccw(R1,R2,L2)): return False - return True # CHANGE THIS LATER - # However, the angle between the forearms should be close to 90º # Helper dot product function @@ -368,6 +370,9 @@ def set_ended(self): if self.rep_count == 0: return + # Write to db + strength_level.write_data((type(self.exercise).__name__).lower()) + # Generate gifs for count in range(1, self.rep_count+1): # Generates the gif @@ -378,7 +383,9 @@ def set_ended(self): # Sends the encoded gif to the frontend with open(f'RepetitionGifs/rep_{count}.gif', "rb") as image_file: encoded_string = base64.b64encode(image_file.read()).decode('utf-8') - self.send_message(Gif(count, encoded_string)) + # self.send_message(Gif(count, encoded_string)) + + self.send_message(SetState("false")) # Reset self.clean() diff --git a/Backend/protocol.py b/Backend/protocol.py index 32a2a88..bdf0f13 100644 --- a/Backend/protocol.py +++ b/Backend/protocol.py @@ -4,12 +4,14 @@ from curl import Curl from squat import Squat from pushup import Pushup +from deadlift import Deadlift class Message: """Base message. Other messages should extend this.""" def encode(self): """Serializes to JSON""" + print(json.dumps(self, default=lambda o: o.__dict__, sort_keys=False)) return json.dumps(self, default=lambda o: o.__dict__, sort_keys=False) @classmethod @@ -28,6 +30,10 @@ def decode(cls, msg : dict): elif type == "IN_FRAME": return InFrame(JSON["in_frame"]) + # Gesture detected + elif type == "GESTURE": + return GestureDetected() + # Set State elif type == "SET_STATE": return SetState(JSON["state"]) @@ -61,6 +67,8 @@ def create_object(self): return Curl() elif self.exercise == "Pushup": return Pushup() + elif self.exercise == "Deadlift": + return Deadlift() class InFrame(Message): """Message informing whether the user is in frame or not.""" @@ -68,6 +76,11 @@ def __init__(self, in_frame): self.type = "IN_FRAME" self.in_frame = in_frame +class GestureDetected(Message): + """Message informing that the starting set gesture has been detected""" + def __init__(self): + self.type = "GESTURE" + class SetState(Message): """Message informing that a set has started / ended.""" def __init__(self, state): diff --git a/Backend/pushup.py b/Backend/pushup.py index 19aeebc..ebc95f5 100644 --- a/Backend/pushup.py +++ b/Backend/pushup.py @@ -230,15 +230,15 @@ def count(self, angle, shoulder, elbow, wrist): #Check all form errors if self.hip_fail: - self.framework.add_feedback("pushup_hip") + self.framework.add_feedback("Hip is too hight or too low") print("[FeedBack] Hip too hight") if self.elbow_fail: - self.framework.add_feedback("pushup_elbow") + self.framework.add_feedback("Tuck your arms in") print("[FeedBack] Tuck your arms in") if not self.perfect_tag: - self.framework.add_feedback("pushup_rom") + self.framework.add_feedback("Not full range motion") print("[FeedBack] Not full motion rep >:(\n\n") if self.perfect_tag and not self.elbow_fail and not self.hip_fail: diff --git a/Backend/requirements.txt b/Backend/requirements.txt new file mode 100644 index 0000000..e2a935f --- /dev/null +++ b/Backend/requirements.txt @@ -0,0 +1,2 @@ +opencv-python +mediapipe \ No newline at end of file diff --git a/Backend/squat.py b/Backend/squat.py index 51ff4b2..3f40aca 100644 --- a/Backend/squat.py +++ b/Backend/squat.py @@ -199,15 +199,15 @@ def count(self, hip, knee, angle): #Check all form errors if self.knee_fail: - self.framework.add_feedback("squat_knee") + self.framework.add_feedback("Knees to far away from toes") print("[FeedBack] Knee to far from toe") if self.back_fail: - self.framework.add_feedback("squat_back") + self.framework.add_feedback("Back is too leaned forward") print("[FeedBack] Back is to leaned forward") if not self.perfect_tag: - self.framework.add_feedback("squat_rom") + self.framework.add_feedback("Not full range motion") print("[FeedBack] Not full motion rep >:(\n\n") if self.perfect_tag and not self.back_fail and not self.knee_fail: diff --git a/Backend/strength_level.py b/Backend/strength_level.py new file mode 100644 index 0000000..3da538f --- /dev/null +++ b/Backend/strength_level.py @@ -0,0 +1,135 @@ +import csv # For handling the exercise data +import os # For paths +import requests +import json + +from enum import Enum + +class Classification(Enum): + Beginner = 1 + Novice = 2 + Intermediate = 3 + Advanced = 4 + Elite = 5 + +# Base location +BASE_PATH = "ExerciseData/" + +def strength_classification(gender : str, age : int, bodyweight : int, one_rep_max : int, exercise : str) -> dict: + """Gets a strength classification in respect to age and bodyweight""" + + # By age + age_classification = None + with open(os.path.join(BASE_PATH, exercise + '_age_' + gender + '.csv'), 'r') as csv_file: + csv_reader = csv.DictReader(csv_file) + + # Find the row + selected_row = None + for row in csv_reader: + if selected_row == None: + selected_row = row + else: + if int(row['Age']) <= age: + selected_row = row + else: + break + + if int(selected_row['Advanced']) < one_rep_max: + age_classification = Classification['Elite'] + age_diff = one_rep_max/int(selected_row['Advanced']) + + elif int(selected_row['Intermediate']) < one_rep_max: + age_classification = Classification['Advanced'] + age_diff = one_rep_max/int(selected_row['Intermediate']) + + elif int(selected_row['Novice']) < one_rep_max: + age_classification = Classification['Intermediate'] + age_diff = one_rep_max/int(selected_row['Novice']) + + elif int(selected_row['Beginner']) < one_rep_max: + age_classification = Classification['Novice'] + age_diff = one_rep_max/int(selected_row['Beginner']) + + else: + age_classification = Classification['Beginner'] + age_diff = one_rep_max/int(selected_row['Beginner']) + + # By bodyweight + bw_classification = None + with open(os.path.join(BASE_PATH, exercise + '_bw_' + gender + '.csv'), 'r') as csv_file: + csv_reader = csv.DictReader(csv_file) + + # Find the row + selected_row = None + for row in csv_reader: + if selected_row == None: + selected_row = row + else: + if int(row['Bodyweight']) <= bodyweight: + selected_row = row + else: + break + + if int(selected_row['Advanced']) < one_rep_max: + bw_classification = Classification['Elite'] + bw_diff = one_rep_max/int(selected_row['Advanced']) + + elif int(selected_row['Intermediate']) < one_rep_max: + bw_classification = Classification['Advanced'] + bw_diff = one_rep_max/int(selected_row['Intermediate']) + + elif int(selected_row['Novice']) < one_rep_max: + bw_classification = Classification['Intermediate'] + bw_diff = one_rep_max/int(selected_row['Novice']) + + elif int(selected_row['Beginner']) < one_rep_max: + bw_classification = Classification['Novice'] + bw_diff = one_rep_max/int(selected_row['Beginner']) + + else: + bw_classification = Classification['Beginner'] + bw_diff = one_rep_max/int(selected_row['Beginner']) + + # Average out + average = int(( bw_classification.value + age_classification.value )/2) + general_classification = Classification( average ) + + # Round diffs to 2 decimals + bw_diff = round(bw_diff, 2) + age_diff = round(age_diff, 2) + + # How good are ya + rate = 'N/A' + with open(os.path.join(BASE_PATH, exercise + '_rates_' + gender + '.csv'), 'r') as csv_file: + csv_reader = csv.DictReader(csv_file) + + # Find the row + selected_row = None + for row in csv_reader: + rate = row[general_classification.name] + + return {"class" : general_classification.value, "rate" : rate} + + +def write_data(exercise : str): + try: + user_data= requests.get("http://192.168.10.150/user?username=filipe").json()[0] + + gender = 'm' if user_data['gender'] == 'male' else 'f' + age = 22 # Fix later + bodyweight = user_data['weight'] + + one_rep_max = user_data[exercise] + + result = (strength_classification(gender,age, int(bodyweight), int(one_rep_max), exercise)) + result['user'] = 'filipe' + result['exercise'] = exercise + + requests.put("http://192.168.10.150/potencial", data=result) + + except: + pass + + pass + +write_data('curl') \ No newline at end of file diff --git a/Backend/test.py b/Backend/test.py deleted file mode 100644 index 54a0a6d..0000000 --- a/Backend/test.py +++ /dev/null @@ -1,82 +0,0 @@ -import asyncio -from websockets import serve -import cv2 -import numpy as np -import json - - - -async def recieve(websocket): - count = 1 - repCount = 0 - cv2.namedWindow("test") - - async for message in websocket: - - # frames - if type(message) != str: - - # get image from bytes - decoded = cv2.imdecode(np.frombuffer(message, np.uint8), -1) - decoded = cv2.rotate(decoded, cv2.ROTATE_180) - - # show image - cv2.imshow("test", decoded) - cv2.waitKey(10) - - data = {} - - # if finished set - if count % 56 == 0: - - # do some shit - - data = {'message': 'finished', 'gifs': "there are no gifs available"} - - # if finished rep - elif count % 10 == 0: - - # do some shit - - repCount += 1 - data = {'message': 'repCount', 'repCount': repCount} - - # send message - if "message" in data.keys(): - print(f"Sending Message: {data}") - await websocket.send(json.dumps(data)) - - count += 1 - - # messages like get statistics - else: - json_message = json.loads(message) - - # normal message - if "message" in json_message.keys(): - - # statistics - if json_message["message"] == "statistics": - - # do some shit - - # send statistics - data = {'message': 'statistics'} - print(f"Sending Message: {data}") - await websocket.send(json.dumps(data)) - - -async def main(): - async with serve(recieve, port=5000): - await asyncio.Future() - -asyncio.run(main()) - - - -""" -messages: - - repCount = {'message': 'repCount', 'repCount': } - - finishSet = {'message': 'finished', 'gifs': } - - statistics = {'message': 'statistics'} -""" \ No newline at end of file diff --git a/DemoImages/Curl/good.gif b/DemoImages/Curl/good.gif new file mode 100644 index 0000000..3196a54 Binary files /dev/null and b/DemoImages/Curl/good.gif differ diff --git a/DemoImages/Curl/rep_1.gif b/DemoImages/Curl/rep_1.gif new file mode 100644 index 0000000..43fde32 Binary files /dev/null and b/DemoImages/Curl/rep_1.gif differ diff --git a/DemoImages/Curl/rep_2.gif b/DemoImages/Curl/rep_2.gif new file mode 100644 index 0000000..6ef93e9 Binary files /dev/null and b/DemoImages/Curl/rep_2.gif differ diff --git a/DemoImages/Curl/rep_3.gif b/DemoImages/Curl/rep_3.gif new file mode 100644 index 0000000..3b9fd6a Binary files /dev/null and b/DemoImages/Curl/rep_3.gif differ diff --git a/DemoImages/Curl/rep_4.gif b/DemoImages/Curl/rep_4.gif new file mode 100644 index 0000000..356fef8 Binary files /dev/null and b/DemoImages/Curl/rep_4.gif differ diff --git a/DemoImages/Pushups/bad_archRotated.gif b/DemoImages/Pushups/bad_archRotated.gif new file mode 100644 index 0000000..5ab27f4 Binary files /dev/null and b/DemoImages/Pushups/bad_archRotated.gif differ diff --git a/DemoImages/Pushups/bad_sagRotated.gif b/DemoImages/Pushups/bad_sagRotated.gif new file mode 100644 index 0000000..5b21df3 Binary files /dev/null and b/DemoImages/Pushups/bad_sagRotated.gif differ diff --git a/DemoImages/Pushups/goodRotated.gif b/DemoImages/Pushups/goodRotated.gif new file mode 100644 index 0000000..2dbc563 Binary files /dev/null and b/DemoImages/Pushups/goodRotated.gif differ diff --git a/DemoImages/Pushups/rep_1.gif b/DemoImages/Pushups/rep_1.gif new file mode 100644 index 0000000..b0af359 Binary files /dev/null and b/DemoImages/Pushups/rep_1.gif differ diff --git a/DemoImages/Pushups/rep_2.gif b/DemoImages/Pushups/rep_2.gif new file mode 100644 index 0000000..30f33f6 Binary files /dev/null and b/DemoImages/Pushups/rep_2.gif differ diff --git a/DemoImages/Pushups/rep_3.gif b/DemoImages/Pushups/rep_3.gif new file mode 100644 index 0000000..130d6ff Binary files /dev/null and b/DemoImages/Pushups/rep_3.gif differ diff --git a/DemoImages/Pushups/rep_4.gif b/DemoImages/Pushups/rep_4.gif new file mode 100644 index 0000000..5844e5b Binary files /dev/null and b/DemoImages/Pushups/rep_4.gif differ diff --git a/DemoImages/Pushups/rep_5.gif b/DemoImages/Pushups/rep_5.gif new file mode 100644 index 0000000..90d505f Binary files /dev/null and b/DemoImages/Pushups/rep_5.gif differ diff --git a/DemoImages/Squat/rep_2.gif b/DemoImages/Squat/rep_2.gif new file mode 100644 index 0000000..66170cc Binary files /dev/null and b/DemoImages/Squat/rep_2.gif differ diff --git a/DemoImages/Squat/rep_3.gif b/DemoImages/Squat/rep_3.gif new file mode 100644 index 0000000..664d337 Binary files /dev/null and b/DemoImages/Squat/rep_3.gif differ diff --git a/DemoImages/UI/.gitkeep b/DemoImages/UI/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/DemoImages/UI/.gitkeep @@ -0,0 +1 @@ + diff --git a/DemoImages/UI/Create Account UI.gif b/DemoImages/UI/Create Account UI.gif new file mode 100644 index 0000000..256868f Binary files /dev/null and b/DemoImages/UI/Create Account UI.gif differ diff --git a/DemoImages/UI/Select and Perform Exercise UI.gif b/DemoImages/UI/Select and Perform Exercise UI.gif new file mode 100644 index 0000000..4840dce Binary files /dev/null and b/DemoImages/UI/Select and Perform Exercise UI.gif differ diff --git a/DemoImages/UI/Statistics UI.gif b/DemoImages/UI/Statistics UI.gif new file mode 100644 index 0000000..37cb99e Binary files /dev/null and b/DemoImages/UI/Statistics UI.gif differ diff --git a/smart_gains/.gitignore b/Frontend/.gitignore similarity index 100% rename from smart_gains/.gitignore rename to Frontend/.gitignore diff --git a/smart_gains/.metadata b/Frontend/.metadata similarity index 100% rename from smart_gains/.metadata rename to Frontend/.metadata diff --git a/smart_gains/README.md b/Frontend/README.md similarity index 100% rename from smart_gains/README.md rename to Frontend/README.md diff --git a/smart_gains/analysis_options.yaml b/Frontend/analysis_options.yaml similarity index 100% rename from smart_gains/analysis_options.yaml rename to Frontend/analysis_options.yaml diff --git a/smart_gains/android/.gitignore b/Frontend/android/.gitignore similarity index 100% rename from smart_gains/android/.gitignore rename to Frontend/android/.gitignore diff --git a/smart_gains/android/app/build.gradle b/Frontend/android/app/build.gradle similarity index 100% rename from smart_gains/android/app/build.gradle rename to Frontend/android/app/build.gradle diff --git a/smart_gains/android/app/src/debug/AndroidManifest.xml b/Frontend/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from smart_gains/android/app/src/debug/AndroidManifest.xml rename to Frontend/android/app/src/debug/AndroidManifest.xml diff --git a/smart_gains/android/app/src/main/AndroidManifest.xml b/Frontend/android/app/src/main/AndroidManifest.xml similarity index 86% rename from smart_gains/android/app/src/main/AndroidManifest.xml rename to Frontend/android/app/src/main/AndroidManifest.xml index 802c9f5..5e2b760 100644 --- a/smart_gains/android/app/src/main/AndroidManifest.xml +++ b/Frontend/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,12 @@ + + + + + + + + +

+

+Uma boa repetição de bicep curl. +

+ +

+ +

+ +

+

+Uma má repetição de bicep curl. Em vermelho, a fonte dos erros. +

+

+ +

+ +

+

+Uma boa repetição de pushups. +

+

+ +

+ + +

+Má repetições de pushups. Em vermelho, a fonte dos erros. +

+

+ + + +

+ +

+

+Uma boa repetição de agachamento. +

+ +

+

+ +

+

+Uma má repetição de agachamento. Em vermelho, a fonte dos erros. +

+

+ +# UI +

+

+ +

+

+Criando uma nova conta. +

+

+ +
+ +

+

+ +

+

+Estatísticas de um usuário em específico. +

+

+ +
+ +

+

+ +

+

+Selecionar e executar exercícios. +

+

+ diff --git "a/Sum\303\241rio_Executivo.pdf" "b/Sum\303\241rio_Executivo.pdf" new file mode 100644 index 0000000..7b98760 Binary files /dev/null and "b/Sum\303\241rio_Executivo.pdf" differ diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e2b9e8c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -numpy -opencv-python -mediapipe -pillow diff --git a/smart_gains/lib/CreateAccountPage.dart b/smart_gains/lib/CreateAccountPage.dart deleted file mode 100644 index 4072b99..0000000 --- a/smart_gains/lib/CreateAccountPage.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'InputUserDataPage.dart'; - -class CreateAccountPage extends StatelessWidget { - const CreateAccountPage({Key? key, required this.title}) : super(key: key); - final String title; - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/womanImage.jpg"), - fit: BoxFit.cover, - ), - ), - child: Scaffold( - appBar: AppBar( - centerTitle: true, - elevation: 0, - bottomOpacity: 0, - shadowColor: const Color.fromARGB(0, 0, 0, 0), - backgroundColor: Colors.transparent, - title: Image.asset( - "assets/logo.png", - fit: BoxFit.contain, - height: 60, - )), - backgroundColor: Colors.transparent, - body: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40.0), - topRight: Radius.circular(40.0), - )), - alignment: FractionalOffset.center, - child: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(40.0), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Text( - textAlign: TextAlign.center, - "Create your Smart Gains Account", - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 25), - )), - ), - Padding( - padding: const EdgeInsets.only( - left: 32, right: 32, bottom: 32, top: 32), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.person), - hintText: 'Email', - ), - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.person), - hintText: 'Usarname', - ), - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.key), - hintText: 'Password', - ), - obscureText: true, - enableSuggestions: false, - autocorrect: false, - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.key), - hintText: 'Repeat Password', - ), - obscureText: true, - enableSuggestions: false, - autocorrect: false, - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(320, 40), - shape: const StadiumBorder(), - backgroundColor: - const Color.fromARGB(255, 37, 171, 117)), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const InputUserData(title: "here")), - ); - }, - child: const Text("Continue")), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/smart_gains/lib/FirstPage.dart b/smart_gains/lib/FirstPage.dart deleted file mode 100644 index b365419..0000000 --- a/smart_gains/lib/FirstPage.dart +++ /dev/null @@ -1,129 +0,0 @@ -// ignore_for_file: unnecessary_const - -import 'package:flutter/material.dart'; -import 'package:smart_gains/LogInPage.dart'; - -import 'CreateAccountPage.dart'; - -class FirstPage extends StatelessWidget { - const FirstPage({Key? key, required this.title}) : super(key: key); - final String title; - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/womanImage.jpg"), - fit: BoxFit.cover, - ), - ), - child: Scaffold( - appBar: AppBar( - elevation: 0, - bottomOpacity: 0, - shadowColor: const Color.fromARGB(0, 0, 0, 0), - backgroundColor: Colors.transparent, - title: Row( - children: [ - Expanded( - child: Center( - child: Image.asset( - "assets/logo.png", - fit: BoxFit.contain, - height: 60, - )), - ), - const Icon(Icons.more_vert) - ], - )), - backgroundColor: Colors.transparent, - body: Column( - children: [ - Expanded(child: Container()), - Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(40.0), - topRight: Radius.circular(40.0), - )), - alignment: FractionalOffset.bottomCenter, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 40.0, vertical: 10), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Center( - child: Text( - style: TextStyle(fontWeight: FontWeight.bold), - "Get healthier and fit with our smart fitness coach", - textAlign: TextAlign.center, - ), - )), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 40, vertical: 10), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Center( - child: Text( - "Train from home, like you were in a gym, with our Interactive Fitness Coach", - textAlign: TextAlign.center, - ), - )), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(320, 40), - shape: const StadiumBorder(), - backgroundColor: - const Color.fromARGB(255, 37, 171, 117)), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const CreateAccountPage(title: "here")), - ); - }, - child: const Text("Get Started")), - ), - Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - "Already have an account?", - textAlign: TextAlign.center, - ), - TextButton( - child: const Text("Log in"), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const LogInPage(title: "here")), - ); - }, - ) - ], - ), - ), - ], - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/smart_gains/lib/FitnessGoalsPage.dart b/smart_gains/lib/FitnessGoalsPage.dart deleted file mode 100644 index a10853c..0000000 --- a/smart_gains/lib/FitnessGoalsPage.dart +++ /dev/null @@ -1,218 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'InputUserDataPage.dart'; - -class FitnessGoalsPage extends StatelessWidget { - const FitnessGoalsPage({Key? key, required this.title}) : super(key: key); - final String title; - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/womanImage.jpg"), - fit: BoxFit.cover, - ), - ), - child: Scaffold( - appBar: AppBar( - centerTitle: true, - elevation: 0, - bottomOpacity: 0, - shadowColor: const Color.fromARGB(0, 0, 0, 0), - backgroundColor: Colors.transparent, - title: Image.asset( - "assets/logo.png", - fit: BoxFit.contain, - height: 60, - )), - backgroundColor: Colors.transparent, - body: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40.0), - topRight: Radius.circular(40.0), - )), - alignment: FractionalOffset.center, - child: SingleChildScrollView( - reverse: false, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(40.0), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Text( - textAlign: TextAlign.center, - "Your Fitness Goals", - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 25), - )), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: "Get healthier in body and mind"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget(title: "Lose weight/fat"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget(title: "Gain weight/muscle"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: "Lose fat and gain muscle (aka “tone” up)"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: "Specific Doctor-given weight or exercise goal"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: - "Strengthen the heart and decrease resting heart rate"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: - "build muscle and increase your resting metabolism"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget(title: "Gain Flexibility"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget(title: "Perfect your form"), - ), - const Padding( - padding: EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: MyStatefulWidget( - title: "Be more explosive for a specific sport"), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(320, 40), - shape: const StadiumBorder(), - backgroundColor: - const Color.fromARGB(255, 37, 171, 117)), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const InputUserData(title: "here")), - ); - }, - child: const Text("Continue")), - ), - ], - ), - ), - ), - ), - ), - ); - } -} - -class LabeledCheckbox extends StatelessWidget { - const LabeledCheckbox({ - super.key, - required this.label, - required this.padding, - required this.value, - required this.onChanged, - }); - - final String label; - final EdgeInsets padding; - final bool value; - final ValueChanged onChanged; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: () { - onChanged(!value); - }, - child: Padding( - padding: padding, - child: Container( - height: 50, - decoration: const BoxDecoration( - color: const Color.fromARGB(255, 6, 6, 32), - borderRadius: BorderRadius.all(Radius.circular(20))), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(left: 20), - child: Checkbox( - fillColor: MaterialStateProperty.all( - const Color.fromARGB(255, 37, 171, 117)), - activeColor: Color.fromARGB(255, 37, 171, 117), - shape: const CircleBorder(), - value: value, - onChanged: (bool? newValue) { - onChanged(newValue!); - }, - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.only(right: 10), - child: Text( - style: const TextStyle(color: Colors.white), - label, - textAlign: TextAlign.center, - ), - )), - ], - ), - ), - ), - ); - } -} - -class MyStatefulWidget extends StatefulWidget { - const MyStatefulWidget({Key? key, required this.title}) : super(key: key); - final String title; - @override - State createState() => _MyStatefulWidgetState(title); -} - -class _MyStatefulWidgetState extends State { - bool _isSelected = false; - String titulo = ""; - - _MyStatefulWidgetState(String title) { - titulo = title; - } - @override - Widget build(BuildContext context) { - return LabeledCheckbox( - label: titulo, - padding: const EdgeInsets.symmetric(horizontal: 20.0), - value: _isSelected, - onChanged: (bool newValue) { - setState(() { - _isSelected = newValue; - }); - }, - ); - } -} diff --git a/smart_gains/lib/InputUserDataPage.dart b/smart_gains/lib/InputUserDataPage.dart deleted file mode 100644 index 3b2f07f..0000000 --- a/smart_gains/lib/InputUserDataPage.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'FitnessGoalsPage.dart'; - -class InputUserData extends StatelessWidget { - const InputUserData({Key? key, required this.title}) : super(key: key); - final String title; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/womanImage.jpg"), - fit: BoxFit.cover, - ), - ), - child: Scaffold( - appBar: AppBar( - centerTitle: true, - elevation: 0, - bottomOpacity: 0, - shadowColor: const Color.fromARGB(0, 0, 0, 0), - backgroundColor: Colors.transparent, - title: Image.asset( - "assets/logo.png", - fit: BoxFit.contain, - height: 60, - )), - backgroundColor: Colors.transparent, - body: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40.0), - topRight: Radius.circular(40.0), - )), - alignment: FractionalOffset.center, - child: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(40.0), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Text( - textAlign: TextAlign.center, - "Information about yourself", - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 25), - )), - ), - Padding( - padding: const EdgeInsets.only( - left: 32, right: 32, bottom: 32, top: 32), - child: Row( - children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.scale), - hintText: 'Weigth', - ), - ), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.square_foot), - hintText: 'Height', - ), - ), - ), - ], - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: Row( - children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.transgender), - hintText: 'Gender', - ), - ), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.date_range), - hintText: 'dd/mm/yyyy', - ), - ), - ), - ], - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(320, 40), - shape: const StadiumBorder(), - backgroundColor: - const Color.fromARGB(255, 37, 171, 117)), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const FitnessGoalsPage(title: "here")), - ); - }, - child: const Text("Continue")), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/smart_gains/lib/LogInPage.dart b/smart_gains/lib/LogInPage.dart deleted file mode 100644 index b721fdd..0000000 --- a/smart_gains/lib/LogInPage.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; - -class LogInPage extends StatelessWidget { - const LogInPage({Key? key, required this.title}) : super(key: key); - final String title; - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/womanImage.jpg"), - fit: BoxFit.cover, - ), - ), - child: Scaffold( - appBar: AppBar( - centerTitle: true, - elevation: 0, - bottomOpacity: 0, - shadowColor: const Color.fromARGB(0, 0, 0, 0), - backgroundColor: Colors.transparent, - title: Image.asset( - "assets/logo.png", - fit: BoxFit.contain, - height: 60, - )), - backgroundColor: Colors.transparent, - body: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40.0), - topRight: Radius.circular(40.0), - )), - alignment: FractionalOffset.center, - child: SingleChildScrollView( - reverse: true, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(40.0), - child: Title( - color: const Color.fromARGB(255, 6, 6, 32), - child: const Text( - "Sign in", - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 30), - )), - ), - Padding( - padding: const EdgeInsets.only( - left: 32, right: 32, bottom: 32, top: 64), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.person), - hintText: 'Usarname', - ), - ), - ), - Padding( - padding: - const EdgeInsets.only(left: 32, right: 32, bottom: 32), - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20)), - prefixIcon: const Icon(Icons.key), - hintText: 'password', - ), - obscureText: true, - enableSuggestions: false, - autocorrect: false, - ), - ), - Padding( - padding: const EdgeInsets.only( - left: 32, right: 32, bottom: 32, top: 32), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(320, 40), - shape: const StadiumBorder(), - backgroundColor: - const Color.fromARGB(255, 37, 171, 117)), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const LogInPage(title: "here")), - ); - }, - child: const Text("Log In")), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/smart_gains/lib/TrainTab.dart b/smart_gains/lib/TrainTab.dart deleted file mode 100644 index 810cfef..0000000 --- a/smart_gains/lib/TrainTab.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'models/exercise_model.dart'; - -class SecondPage extends StatelessWidget { - const SecondPage({Key? key, required this.title}) : super(key: key); - final String title; - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: const Color.fromARGB(255, 255, 255, 255), - body: Column( - children: [ - SizedBox( - height: 260.0, - child: Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - backgroundColor: Colors.transparent, - iconTheme: const IconThemeData( - color: Color.fromARGB(255, 255, 255, 255)), - elevation: 0, - bottomOpacity: 0, - ), - body: Container( - height: 300.0, - width: double.infinity, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/deadLift.jpg"), - fit: BoxFit.cover, - )), - child: Row( - children: [ - const SizedBox( - width: 10, - ), - Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text( - "Free Style", - style: TextStyle( - fontSize: 33, - color: Colors.white, - fontFamily: 'KronaOne-Regular', - fontWeight: FontWeight.bold), - ), - Text( - "Choose your training", - style: TextStyle( - fontSize: 14, - color: Colors.white, - fontFamily: 'KronaOne-Regular'), - ), - SizedBox(height: 8) - ]), - ], - ), - ))), - Container( - height: 424, - decoration: const BoxDecoration(color: Colors.white), - child: ListView.builder( - itemCount: 4, - itemBuilder: (BuildContext context, int index) { - return InkWell( - child: Container( - margin: const EdgeInsets.only( - top: 0.0, bottom: 5.0, right: 10, left: 10), - padding: - EdgeInsets.symmetric(horizontal: 5, vertical: 5.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - SizedBox( - height: 50, - width: 50, - child: Image.asset(exercises[index] - .icon), //add image location here - ), - const SizedBox(width: 15), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - exercises[index].name, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.black38), - ), - const SizedBox( - height: 6, - ), - Text( - exercises[index].description, - style: const TextStyle( - fontSize: 12, - color: Colors.black87), - ) - ], - ) - ], - ), - ]))); - }, - ), - ), - ], - ), - bottomNavigationBar: BottomNavigationBar(items: const [ - BottomNavigationBarItem( - label: 'Statistics', - icon: Icon(Icons.insights), - ), - BottomNavigationBarItem( - label: 'Train', - icon: Icon(Icons.timer), - ), - BottomNavigationBarItem( - icon: Icon(Icons.person_outline), - label: 'Profile', - ), - ]), - ); - } -} diff --git a/smart_gains/lib/main.dart b/smart_gains/lib/main.dart deleted file mode 100644 index f9e10a0..0000000 --- a/smart_gains/lib/main.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:smart_gains/FirstPage.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - title: 'Flutter Camera Demo', - debugShowCheckedModeBanner: false, - home: FirstPage(title: "asdklasd"), - ); - } -} diff --git a/smart_gains/lib/models/exercise_model.dart b/smart_gains/lib/models/exercise_model.dart deleted file mode 100644 index 7fa656d..0000000 --- a/smart_gains/lib/models/exercise_model.dart +++ /dev/null @@ -1,50 +0,0 @@ -class Exercise { - final int id; - final String name; - final String description; - final String icon; - final String instructions; - - Exercise({ - required this.id, - required this.name, - required this.description, - required this.icon, - required this.instructions, - }); -} - -final Exercise deadLift = Exercise( - id: 1, - name: 'Deadlift', - description: 'My name Jeff', - icon: 'assets/icons/deadlift_icon.jpg', - instructions: '', -); - -final Exercise squat = Exercise( - id: 1, - name: 'Squat', - description: 'My name Jeff', - icon: 'assets/icons/squat_icon.png', - instructions: - "Here’s how to Squat with proper form, using a barbell:\n1. Stand with the bar on your upper-back, and your feet shoulder-width apart\n2. Squat down by pushing your knees to the side while moving hips back\n3. Break parallel by Squatting down until your hips are lower than your knees\n4. Squat back up while keeping your knees out and chest up\n 5.Stand with your hips and knees locked at the top\n", -); - -final Exercise pushup = Exercise( - id: 1, - name: 'Pushup', - description: 'My name Jeff', - icon: 'assets/icons/pushup_icon.png', - instructions: '', -); - -final Exercise curl = Exercise( - id: 1, - name: 'Curl', - description: 'My name Jeff', - icon: 'assets/icons/curl_icon.png', - instructions: '', -); - -List exercises = [deadLift, squat, pushup, curl]; diff --git a/smart_gains/windows/runner/runner.exe.manifest b/smart_gains/windows/runner/runner.exe.manifest deleted file mode 100644 index a42ea76..0000000 --- a/smart_gains/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - -