diff --git a/.DS_Store b/.DS_Store index 953c41e94..bbd9324c2 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..a4b6a55f5 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,36 @@ +# Use the official Python 3.10 slim image +FROM python:3.10-slim + +# Set the working directory inside the container +WORKDIR /app + +# Install system dependencies including build tools and MariaDB development files +RUN apt-get update && \ + apt-get install -y \ + build-essential \ + pkg-config \ + python3-dev \ + libmariadb-dev-compat \ + libmariadb-dev \ + && rm -rf /var/lib/apt/lists/* + +# Upgrade pip (optional but recommended) +RUN pip install --upgrade pip + +# Copy the requirements.txt file from the correct relative path into the container +COPY Sprint_1/AI_Fitness_Project/requirements.txt /app/requirements.txt + +# Install Python dependencies from requirements.txt +RUN pip install --no-cache-dir -r /app/requirements.txt + +# Copy the rest of your application code into the container +COPY Sprint_1/AI_Fitness_Project /app + +# Set environment variables (using the recommended syntax) +ENV PYTHONUNBUFFERED=1 + +# Expose the port your app uses (adjust if needed) +EXPOSE 5000 + +# Run the application +CMD ["python", "/app/app.py"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..3f7427c42 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "AI Fitness Dev Container", + "build": { + "dockerfile": "Dockerfile" + }, + "workspaceFolder": "/workspace", + "mounts": ["source=${localWorkspaceFolder},target=/workspace,type=bind"], + "remoteUser": "vscode" + } + \ No newline at end of file diff --git a/README.md b/README.md index b2588bc51..4993f3e8d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -My groupmembers are: -- XXXX -- XXXX -- XXXX -- XXXX +The groupmembers are: +- Sebastian Alcibar +- Ryan Maazous +- Thomas Marshall +- Jonah Minkoff +- Brady Miller ------------------ Fill in some information about your project under this ------------------ +## Our group has decided to work on the Mental Health Support App. diff --git a/Sprint 1/.DS_Store b/Sprint 1/.DS_Store new file mode 100644 index 000000000..23cfab5c7 Binary files /dev/null and b/Sprint 1/.DS_Store differ diff --git a/Sprint 1/AI Fitness Project/.DS_Store b/Sprint 1/AI Fitness Project/.DS_Store new file mode 100644 index 000000000..1e90259a5 Binary files /dev/null and b/Sprint 1/AI Fitness Project/.DS_Store differ diff --git a/Sprint 1/AI Fitness Project/.gitignore b/Sprint 1/AI Fitness Project/.gitignore new file mode 100644 index 000000000..cd6eb6e16 --- /dev/null +++ b/Sprint 1/AI Fitness Project/.gitignore @@ -0,0 +1,4 @@ +.env +database.db +__pycache__/ +venv/ \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/app.py b/Sprint 1/AI Fitness Project/app.py new file mode 100644 index 000000000..ee287da0f --- /dev/null +++ b/Sprint 1/AI Fitness Project/app.py @@ -0,0 +1,112 @@ +from flask import Flask, render_template, session +import os +from backend.db import db +from backend.tables import User +from flask_login import LoginManager, login_required, login_user, logout_user, current_user +from flask import Flask, render_template, redirect, url_for, flash +from flask_bcrypt import Bcrypt +from backend.forms import RegisterForm, LoginForm, SurveyForm, FitnessLogWorkoutForm, FitnessLogCardioForm +from backend.tables import User +import secrets + + +app = Flask(__name__) +bcrypt = Bcrypt(app) + +# Handles our SQLite database +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' +app.config['SECRET_KEY'] = secrets.token_hex(16) +db.init_app(app) + +# Handles login verification +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = "login" +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + + +# Renders home page +@app.route('/', methods=['GET', 'POST']) +@login_required +def home(): + fitness_goals= current_user.fitness_goals + + if fitness_goals: + fitness_goals_list = fitness_goals.split(",") + else: + fitness_goals_list = None + return render_template('index.html', first_name=current_user.first_name, fitness_goals=fitness_goals_list) + + +# Renders fitness log page +@app.route('/fitness-log', methods=['GET', 'POST']) +def fitness_log(): + workout_form = FitnessLogWorkoutForm() + cardio_form = FitnessLogCardioForm() + return render_template('fitness-log.html', workout_form=workout_form, cardio_form=cardio_form) + +# Renders about page +@app.route('/about') +def about(): + return render_template('about.html') + +# Renders main AI chat page +@app.route('/main-chat') +def main_chat(): + return render_template('main-chat.html') + +# Renders login page +@app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user: + if bcrypt.check_password_hash(user.password, form.password.data): + login_user(user) + return redirect(url_for('home')) + return render_template('login.html', form=form) + +# Handles logout functionality +@app.route('/logout', methods=['GET', 'POST']) +@login_required +def logout(): + logout_user() + return redirect(url_for('login')) + +# Renders register page +@app.route('/register', methods=['GET', 'POST']) +def register(): + form = RegisterForm() + + + if form.validate_on_submit(): + hashed_password = bcrypt.generate_password_hash(form.password.data) + new_user = User(username=form.username.data, password=hashed_password, first_name=form.first_name.data, last_name=form.last_name.data) + db.session.add(new_user) + db.session.commit() + login_user(new_user) + return redirect(url_for('survey')) + + return render_template('register.html', form=form) + +@app.route('/survey', methods=['GET', 'POST']) +def survey(): + form = SurveyForm() + + if form.validate_on_submit(): + selected_goals = ",".join(form.fitness_goals.data) + current_user.fitness_goals = selected_goals + db.session.commit() + return redirect(url_for('home')) + + return render_template('survey.html', form=form) + +with app.app_context(): + db.create_all() + +if __name__ == "__main__": + + app.run(debug=True, host="0.0.0.0", port=5000) diff --git a/Sprint 1/AI Fitness Project/backend/db.py b/Sprint 1/AI Fitness Project/backend/db.py new file mode 100644 index 000000000..97a5faf13 --- /dev/null +++ b/Sprint 1/AI Fitness Project/backend/db.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/backend/forms.py b/Sprint 1/AI Fitness Project/backend/forms.py new file mode 100644 index 000000000..eb6fe2c2f --- /dev/null +++ b/Sprint 1/AI Fitness Project/backend/forms.py @@ -0,0 +1,70 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SubmitField, SelectMultipleField, widgets, IntegerField, FieldList, FormField, DecimalField +from wtforms.validators import InputRequired, Length, ValidationError, DataRequired, NumberRange +from backend.tables import User + +# Sets up the form on the register page +class RegisterForm(FlaskForm): + username = StringField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Username"}) + password = PasswordField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Password"}) + first_name = StringField(validators=[InputRequired(), Length(min=1)], render_kw={"placeholder": "First Name"}) + last_name = StringField(validators=[InputRequired(), Length(min=1)], render_kw={"placeholder": "Last Name"}) + + submit = SubmitField("Register") + + def validate_username(self, username): + existing_user = User.query.filter_by( + username=username.data).first() + if existing_user: + raise ValidationError( + "That username already exists." + ) + + +# Sets up form on login page +class LoginForm(FlaskForm): + username = StringField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Username"}) + + password = PasswordField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Password"}) + + submit = SubmitField("Login") + + +class SurveyForm(FlaskForm): + fitness_goals = SelectMultipleField( + "What are your fitness goals?", + choices=[ + ("Build Muscle", "Build Muscle"), + ("Lose Weight", "Lose Weight"), + ("Increase Strength", "Increase Strength"), + ("Improve Cardio", "Improve Cardio") + ], + option_widget=widgets.CheckboxInput(), + widget=widgets.ListWidget(prefix_label=False) + ) + + submit = SubmitField("Submit") + + +class FitnessLogWorkoutForm(FlaskForm): + legs_reps = IntegerField('Total Reps', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + legs_sets = IntegerField('Total Sets', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + chest_reps = IntegerField('Total Reps', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + chest_sets = IntegerField('Total Sets', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + back_reps = IntegerField('Total Reps', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + back_sets = IntegerField('Total Sets', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + bicep_reps = IntegerField('Total Reps', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + bicep_sets = IntegerField('Total Sets', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + tricep_reps = IntegerField('Total Reps', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + tricep_sets = IntegerField('Total Sets', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + submit = SubmitField("Submit") + +class FitnessLogCardioForm(FlaskForm): + miles = DecimalField('Total Miles', default=0, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + submit = SubmitField("Submit") \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/backend/tables.py b/Sprint 1/AI Fitness Project/backend/tables.py new file mode 100644 index 000000000..429ba7f9c --- /dev/null +++ b/Sprint 1/AI Fitness Project/backend/tables.py @@ -0,0 +1,15 @@ +from backend.db import db +from flask_login import UserMixin + +# Creates our Sqlite table that holds user login/register info. +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(20), nullable=False, unique=True) + password = db.Column(db.String(80), nullable=False) + first_name = db.Column(db.String(20), nullable=False) + last_name = db.Column(db.String(20), nullable=False) + fitness_goals = db.Column(db.String(255), nullable=True) + + + + diff --git a/Sprint 1/AI Fitness Project/login_test.py b/Sprint 1/AI Fitness Project/login_test.py new file mode 100644 index 000000000..3e25affcc --- /dev/null +++ b/Sprint 1/AI Fitness Project/login_test.py @@ -0,0 +1,41 @@ +import requests +from bs4 import BeautifulSoup + + +base_url = "http://localhost:5000" +login_url = f"{base_url}/login" +home_url = f"{base_url}/home" + +test_username = 'test' +test_password = 'test' + +session = requests.Session() + +login_page = session.get(login_url) + +soup = BeautifulSoup(login_page.text, 'html.parser') + +csrf_token = soup.find('input', {'name': 'csrf_token'})['value'] + +login_data = { + 'csrf_token': csrf_token, + 'username': test_username, + 'password': test_password, + 'submit': 'Login' +} + +login_response = session.post(login_url, data=login_data) + +print("\nLogin Response Status Code:", login_response.status_code) + +if login_response.status_code == 200: + if "Welcome" in login_response.text: + print("Login test passed.") + elif "Invalid username or password" in login_response.text: + print("Login failed. Invalid username or password.") + else: + print("Login failed with status code 200 but no error message found.") +else: + print("Login failed. Status code:", login_response.status_code) + print("Response Content:") + print(login_response.text) diff --git a/Sprint 1/AI Fitness Project/requirements_mac.txt b/Sprint 1/AI Fitness Project/requirements_mac.txt new file mode 100644 index 000000000..0c9419795 --- /dev/null +++ b/Sprint 1/AI Fitness Project/requirements_mac.txt @@ -0,0 +1,51 @@ +asttokens==3.0.0 +bcrypt==4.3.0 +blinker==1.9.0 +click==8.1.8 +colorama==0.4.6 +comm==0.2.2 +debugpy==1.8.13 +decorator==5.2.1 +dnspython==2.7.0 +email_validator==2.2.0 +executing==2.2.0 +Flask==3.1.0 +Flask-Bcrypt==1.0.1 +Flask-Login==0.6.3 +Flask-MySQLdb==2.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.2 +greenlet==3.1.1 +idna==3.10 +ipykernel==6.29.5 +ipython==8.10.0 +ipython_pygments_lexers==1.1.1 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +PyMySQL==1.0.2 #mysqlclient==2.2.7 +nest-asyncio==1.6.0 +packaging==24.2 +parso==0.8.4 +platformdirs==4.3.7 +prompt_toolkit==3.0.50 +psutil==7.0.0 +pure_eval==0.2.3 +Pygments==2.19.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pywin32==310; sys_platform == "win32" +pyzmq==26.3.0 +six==1.17.0 +SQLAlchemy==2.0.39 +stack-data==0.6.3 +tornado==6.4.2 +traitlets==5.14.3 +typing_extensions==4.12.2 +wcwidth==0.2.13 +Werkzeug==3.1.3 +WTForms==3.2.1 diff --git a/Sprint 1/AI Fitness Project/requirements_windows.txt b/Sprint 1/AI Fitness Project/requirements_windows.txt new file mode 100644 index 000000000..2b694b2f8 --- /dev/null +++ b/Sprint 1/AI Fitness Project/requirements_windows.txt @@ -0,0 +1,51 @@ +asttokens==3.0.0 +bcrypt==4.3.0 +blinker==1.9.0 +click==8.1.8 +colorama==0.4.6 +comm==0.2.2 +debugpy==1.8.13 +decorator==5.2.1 +dnspython==2.7.0 +email_validator==2.2.0 +executing==2.2.0 +Flask==3.1.0 +Flask-Bcrypt==1.0.1 +Flask-Login==0.6.3 +Flask-MySQLdb==2.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.2 +greenlet==3.1.1 +idna==3.10 +ipykernel==6.29.5 +ipython==9.0.2 +ipython_pygments_lexers==1.1.1 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mysqlclient==2.2.7 +nest-asyncio==1.6.0 +packaging==24.2 +parso==0.8.4 +platformdirs==4.3.7 +prompt_toolkit==3.0.50 +psutil==7.0.0 +pure_eval==0.2.3 +Pygments==2.19.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pywin32==310 +pyzmq==26.3.0 +six==1.17.0 +SQLAlchemy==2.0.39 +stack-data==0.6.3 +tornado==6.4.2 +traitlets==5.14.3 +typing_extensions==4.12.2 +wcwidth==0.2.13 +Werkzeug==3.1.3 +WTForms==3.2.1 diff --git a/Sprint 1/AI Fitness Project/static/.DS_Store b/Sprint 1/AI Fitness Project/static/.DS_Store new file mode 100644 index 000000000..130e3f076 Binary files /dev/null and b/Sprint 1/AI Fitness Project/static/.DS_Store differ diff --git a/Sprint 1/AI Fitness Project/static/scripts/fitness-log.js b/Sprint 1/AI Fitness Project/static/scripts/fitness-log.js new file mode 100644 index 000000000..1b949e0dd --- /dev/null +++ b/Sprint 1/AI Fitness Project/static/scripts/fitness-log.js @@ -0,0 +1,9 @@ +function toggleWorkoutForm() { + const form = document.getElementById('workout-form'); + form.style.display = (form.style.display === 'none' || form.style.display === '') ? 'block' : 'none'; +} + +function toggleCardioForm() { + const form = document.getElementById('cardio-form'); + form.style.display = (form.style.display === 'none' || form.style.display === '') ? 'block' : 'none'; +} \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/static/styles/fitness-log.css b/Sprint 1/AI Fitness Project/static/styles/fitness-log.css new file mode 100644 index 000000000..6621768a8 --- /dev/null +++ b/Sprint 1/AI Fitness Project/static/styles/fitness-log.css @@ -0,0 +1,26 @@ +.forms-wrapper { + display: flex; + gap: 20px; + justify-content: space-between; +} + +.workout-form, .cardio-form { + width: 48%; + padding-left: 10px; +} + +#workout-form, #cardio-form { + display: none; +} + +button { + margin-bottom: 15px; +} + +label { + margin-left: 10px; +} + +#submit-button { + padding-top: 20px; +} diff --git a/Sprint 1/AI Fitness Project/static/styles/navbar.css b/Sprint 1/AI Fitness Project/static/styles/navbar.css new file mode 100644 index 000000000..22a5a8cbf --- /dev/null +++ b/Sprint 1/AI Fitness Project/static/styles/navbar.css @@ -0,0 +1,65 @@ +html, body{ + margin: 0; + padding: 0; +} +#navbar { + * { + text-decoration: none; + padding: 0; + margin: 0; + box-sizing: border-box; + } + .navbar { + background: gray; + font-family: calibri; + padding: 10px 20px; + } + .navdiv { + display: flex; + align-items: center; + justify-content: space-between; + } + .logo a { + color: white; + font-size: 21px; + font-weight: bold; + } + ul { + display: flex; + list-style: none; + padding: 0; + } + li { + margin-right: 38px; + } + li a { + color: white; + font-size: 25px; + font-weight: bold; + } + li a:hover{ + color: lightgray; + } + .buttons { + display: flex; + gap: 10px; + } + button { + background-color: black; + border: none; + border-radius: 10px; + padding: 10px; + width: 90px; + cursor: pointer; + } + button a { + color: white; + font-weight: bold; + font-size: 15px; + display: block; + text-align: center; + } + button a:hover{ + color:lightgray; + } +} diff --git a/Sprint 1/AI Fitness Project/templates/about.html b/Sprint 1/AI Fitness Project/templates/about.html new file mode 100644 index 000000000..6f6039ed1 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/about.html @@ -0,0 +1,12 @@ + + + + + + About + + + {% include 'navbar.html' %} +

About Page

+ + \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/templates/fitness-calendar.html b/Sprint 1/AI Fitness Project/templates/fitness-calendar.html new file mode 100644 index 000000000..993066d05 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/fitness-calendar.html @@ -0,0 +1,86 @@ + + + FitBod + + + + + +
+
March 2025
+
+
Sun
+
Mon
+
Tue
+
Wed
+
Thu
+
Fri
+
Sat
+
+
+
+ +
+
+ + + diff --git a/Sprint 1/AI Fitness Project/templates/fitness-log.html b/Sprint 1/AI Fitness Project/templates/fitness-log.html new file mode 100644 index 000000000..696126f5e --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/fitness-log.html @@ -0,0 +1,74 @@ + + + + + + Fitness Log + + + + {% include 'navbar.html' %} +

Fitness Log

+ +
+
+ + +
+ {{ workout_form.hidden_tag() }} +
+

Legs

+ + {{ workout_form.legs_reps }} + + {{ workout_form.legs_sets }} +
+
+

Back

+ + {{ workout_form.back_reps }} + + {{ workout_form.back_sets }} +
+
+

Chest

+ + {{ workout_form.chest_reps }} + + {{ workout_form.chest_sets }} +
+
+

Tricep

+ + {{ workout_form.tricep_reps }} + + {{ workout_form.tricep_sets }} +
+
+

Bicep

+ + {{ workout_form.bicep_reps }} + + {{ workout_form.bicep_sets }} +
+
{{ workout_form.submit }}
+
+
+ +
+ + +
+ {{ cardio_form.hidden_tag() }} +
+ + {{ cardio_form.miles }} +
+
{{ cardio_form.submit }}
+
+
+
+ + + + diff --git a/Sprint 1/AI Fitness Project/templates/index.html b/Sprint 1/AI Fitness Project/templates/index.html new file mode 100644 index 000000000..863610483 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/index.html @@ -0,0 +1,25 @@ + + + + + + Home + + + {% include 'navbar.html' %} +

Welcome, {{ first_name|capitalize }}!

+

Your Fitness Goals:

+ + + \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/templates/login.html b/Sprint 1/AI Fitness Project/templates/login.html new file mode 100644 index 000000000..78225206a --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/login.html @@ -0,0 +1,20 @@ + + + + + + Login Page + + +

Login

+ +
+ {{ form.hidden_tag() }} + {{ form.username }} + {{ form.password }} + {{ form.submit }} +
+ + Don't have an account? Register + + \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/templates/main-chat.html b/Sprint 1/AI Fitness Project/templates/main-chat.html new file mode 100644 index 000000000..a2b68bd6d --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/main-chat.html @@ -0,0 +1,12 @@ + + + + + + Main Chat + + + {% include 'navbar.html' %} +

Main AI Chat Page

+ + \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/templates/navbar.html b/Sprint 1/AI Fitness Project/templates/navbar.html new file mode 100644 index 000000000..5e1397826 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/navbar.html @@ -0,0 +1,28 @@ + + + + + + Navigation Bar + + + + + + + diff --git a/Sprint 1/AI Fitness Project/templates/register.html b/Sprint 1/AI Fitness Project/templates/register.html new file mode 100644 index 000000000..a24e1f4f2 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/register.html @@ -0,0 +1,30 @@ + + + + + + Register Page + + +

Register

+ +
+ {{ form.hidden_tag() }} + +
+ {{ form.username }} + {{ form.password }} +
+ +
+ {{ form.first_name }} + {{ form.last_name }} +
+ + + {{ form.submit }} +
+ + Already have an account? Log In + + \ No newline at end of file diff --git a/Sprint 1/AI Fitness Project/templates/survey.html b/Sprint 1/AI Fitness Project/templates/survey.html new file mode 100644 index 000000000..763bc7ef9 --- /dev/null +++ b/Sprint 1/AI Fitness Project/templates/survey.html @@ -0,0 +1,23 @@ + + + + + + Document + + +

Fitness Survey: What are your goals?

+ +
+ {{ form.hidden_tag() }} +

{{ form.fitness_goals.label }}

+ {% for subfield in form.fitness_goals %} +
+ {{ subfield }} {{ subfield.label }} +
+ {% endfor %} +
+ {{ form.submit() }} +
+ + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/.gitignore b/Sprint 2/AI Fitness Project/.gitignore new file mode 100644 index 000000000..cd6eb6e16 --- /dev/null +++ b/Sprint 2/AI Fitness Project/.gitignore @@ -0,0 +1,4 @@ +.env +database.db +__pycache__/ +venv/ \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/app.py b/Sprint 2/AI Fitness Project/app.py new file mode 100644 index 000000000..07a53dee3 --- /dev/null +++ b/Sprint 2/AI Fitness Project/app.py @@ -0,0 +1,124 @@ +from flask import Flask, render_template, session +import os +from backend.db import db +from backend.tables import User +from flask_login import LoginManager, login_required, login_user, logout_user, current_user +from flask import Flask, render_template, redirect, url_for, flash +from flask_bcrypt import Bcrypt +from backend.forms import RegisterForm, LoginForm, SurveyForm, FitnessLogWorkoutForm, FitnessLogCardioForm +from backend.tables import User +import secrets + + +app = Flask(__name__) +bcrypt = Bcrypt(app) + +# Handles our SQLite database +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' +app.config['SECRET_KEY'] = secrets.token_hex(16) +db.init_app(app) + +# Handles login verification +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = "login" +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + + +# Renders home page +@app.route('/', methods=['GET', 'POST']) +@login_required +def home(): + fitness_goals= current_user.fitness_goals + + if fitness_goals: + fitness_goals_list = fitness_goals.split(",") + else: + fitness_goals_list = None + return render_template('index.html', first_name=current_user.first_name, fitness_goals=fitness_goals_list) + + +# Renders fitness log page +@app.route('/fitness-log', methods=['GET', 'POST']) +def fitness_log(): + workout_form = FitnessLogWorkoutForm() + cardio_form = FitnessLogCardioForm() + return render_template('fitness-log.html', workout_form=workout_form, cardio_form=cardio_form) + +# Renders about page +@app.route('/about') +def about(): + return render_template('about.html') + +# Renders main AI chat page +@app.route('/main-chat') +def main_chat(): + return render_template('main-chat.html') + +# Renders login page +@app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user: + if bcrypt.check_password_hash(user.password, form.password.data): + login_user(user) + return redirect(url_for('home')) + return render_template('login.html', form=form) + +# Handles logout functionality +@app.route('/logout', methods=['GET', 'POST']) +@login_required +def logout(): + logout_user() + return redirect(url_for('login')) + +# Renders register page +@app.route('/register', methods=['GET', 'POST']) +def register(): + form = RegisterForm() + + + if form.validate_on_submit(): + hashed_password = bcrypt.generate_password_hash(form.password.data) + new_user = User(username=form.username.data, password=hashed_password, first_name=form.first_name.data, last_name=form.last_name.data) + db.session.add(new_user) + db.session.commit() + login_user(new_user) + return redirect(url_for('survey')) + + return render_template('register.html', form=form) + +@app.route('/survey', methods=['GET', 'POST']) +def survey(): + form = SurveyForm() + + if form.validate_on_submit(): + selected_goals = ",".join(form.fitness_goals.data) + current_user.fitness_goals = selected_goals + db.session.commit() + return redirect(url_for('home')) + + return render_template('survey.html', form=form) + +@app.route('/workout-log', methods=['GET', 'POST']) +@login_required +def workout_log(): + form = FitnessLogWorkoutForm() + return render_template('workout-log.html', form=form) + +@app.route('/cardio-log', methods=['GET', 'POST']) +@login_required +def cardio_log(): + form = FitnessLogCardioForm() + return render_template('cardio-log.html', form=form) + +with app.app_context(): + db.create_all() + +if __name__ == "__main__": + + app.run(debug=True, host="0.0.0.0", port=5000) diff --git a/Sprint 2/AI Fitness Project/backend/db.py b/Sprint 2/AI Fitness Project/backend/db.py new file mode 100644 index 000000000..97a5faf13 --- /dev/null +++ b/Sprint 2/AI Fitness Project/backend/db.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/backend/forms.py b/Sprint 2/AI Fitness Project/backend/forms.py new file mode 100644 index 000000000..a4ecd8da8 --- /dev/null +++ b/Sprint 2/AI Fitness Project/backend/forms.py @@ -0,0 +1,66 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, SubmitField, SelectMultipleField, widgets, IntegerField, FieldList, FormField, DecimalField, TimeField +from wtforms.validators import InputRequired, Length, ValidationError, DataRequired, NumberRange +from backend.tables import User +from datetime import time + +# Sets up the form on the register page +class RegisterForm(FlaskForm): + username = StringField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Username"}) + password = PasswordField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Password"}) + first_name = StringField(validators=[InputRequired(), Length(min=1)], render_kw={"placeholder": "First Name"}) + last_name = StringField(validators=[InputRequired(), Length(min=1)], render_kw={"placeholder": "Last Name"}) + + submit = SubmitField("Register") + + def validate_username(self, username): + existing_user = User.query.filter_by( + username=username.data).first() + if existing_user: + raise ValidationError( + "That username already exists." + ) + + +# Sets up form on login page +class LoginForm(FlaskForm): + username = StringField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Username"}) + + password = PasswordField(validators=[InputRequired(), Length(min=4, max=20)], render_kw={"placeholder": "Password"}) + + submit = SubmitField("Login") + + +class SurveyForm(FlaskForm): + fitness_goals = SelectMultipleField( + "What are your fitness goals?", + choices=[ + ("Build Muscle", "Build Muscle"), + ("Lose Weight", "Lose Weight"), + ("Increase Strength", "Increase Strength"), + ("Improve Cardio", "Improve Cardio") + ], + option_widget=widgets.CheckboxInput(), + widget=widgets.ListWidget(prefix_label=False) + ) + + submit = SubmitField("Submit") + + +class FitnessLogWorkoutForm(FlaskForm): + reps_field = IntegerField('Total Reps', default=1, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + sets_field = IntegerField('Total Sets', default=1, validators=[NumberRange(min=0, message="Value must be 0 or greater")]) + + exercise_input = StringField(validators=[InputRequired(), Length(min=1, max=50)], render_kw={"placeholder": "Exercise Input"}) + + submit = SubmitField("Submit") + +class FitnessLogCardioForm(FlaskForm): + minute_field = IntegerField('Minutes', default=0, validators=[NumberRange(min=0)]) + + second_field = IntegerField( 'Seconds', default=0, validators=[NumberRange(min=0, max=59)]) + + distance_field = DecimalField('Total Miles', default=0, validators=[InputRequired(), NumberRange(min=0, message="Value must be 0 or greater")]) + + + submit = SubmitField("Submit") \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/backend/tables.py b/Sprint 2/AI Fitness Project/backend/tables.py new file mode 100644 index 000000000..d27e4ce5d --- /dev/null +++ b/Sprint 2/AI Fitness Project/backend/tables.py @@ -0,0 +1,17 @@ +from backend.db import db +from flask_login import UserMixin + +# Creates our Sqlite table that holds user login/register info. +class User(db.Model, UserMixin): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(20), nullable=False, unique=True) + password = db.Column(db.String(80), nullable=False) + first_name = db.Column(db.String(20), nullable=False) + last_name = db.Column(db.String(20), nullable=False) + fitness_goals = db.Column(db.String(255), nullable=True) + + + + + + diff --git a/Sprint 2/AI Fitness Project/login_test.py b/Sprint 2/AI Fitness Project/login_test.py new file mode 100644 index 000000000..3e25affcc --- /dev/null +++ b/Sprint 2/AI Fitness Project/login_test.py @@ -0,0 +1,41 @@ +import requests +from bs4 import BeautifulSoup + + +base_url = "http://localhost:5000" +login_url = f"{base_url}/login" +home_url = f"{base_url}/home" + +test_username = 'test' +test_password = 'test' + +session = requests.Session() + +login_page = session.get(login_url) + +soup = BeautifulSoup(login_page.text, 'html.parser') + +csrf_token = soup.find('input', {'name': 'csrf_token'})['value'] + +login_data = { + 'csrf_token': csrf_token, + 'username': test_username, + 'password': test_password, + 'submit': 'Login' +} + +login_response = session.post(login_url, data=login_data) + +print("\nLogin Response Status Code:", login_response.status_code) + +if login_response.status_code == 200: + if "Welcome" in login_response.text: + print("Login test passed.") + elif "Invalid username or password" in login_response.text: + print("Login failed. Invalid username or password.") + else: + print("Login failed with status code 200 but no error message found.") +else: + print("Login failed. Status code:", login_response.status_code) + print("Response Content:") + print(login_response.text) diff --git a/Sprint 2/AI Fitness Project/requirements_mac.txt b/Sprint 2/AI Fitness Project/requirements_mac.txt new file mode 100644 index 000000000..0c9419795 --- /dev/null +++ b/Sprint 2/AI Fitness Project/requirements_mac.txt @@ -0,0 +1,51 @@ +asttokens==3.0.0 +bcrypt==4.3.0 +blinker==1.9.0 +click==8.1.8 +colorama==0.4.6 +comm==0.2.2 +debugpy==1.8.13 +decorator==5.2.1 +dnspython==2.7.0 +email_validator==2.2.0 +executing==2.2.0 +Flask==3.1.0 +Flask-Bcrypt==1.0.1 +Flask-Login==0.6.3 +Flask-MySQLdb==2.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.2 +greenlet==3.1.1 +idna==3.10 +ipykernel==6.29.5 +ipython==8.10.0 +ipython_pygments_lexers==1.1.1 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +PyMySQL==1.0.2 #mysqlclient==2.2.7 +nest-asyncio==1.6.0 +packaging==24.2 +parso==0.8.4 +platformdirs==4.3.7 +prompt_toolkit==3.0.50 +psutil==7.0.0 +pure_eval==0.2.3 +Pygments==2.19.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pywin32==310; sys_platform == "win32" +pyzmq==26.3.0 +six==1.17.0 +SQLAlchemy==2.0.39 +stack-data==0.6.3 +tornado==6.4.2 +traitlets==5.14.3 +typing_extensions==4.12.2 +wcwidth==0.2.13 +Werkzeug==3.1.3 +WTForms==3.2.1 diff --git a/Sprint 2/AI Fitness Project/requirements_windows.txt b/Sprint 2/AI Fitness Project/requirements_windows.txt new file mode 100644 index 000000000..2b694b2f8 --- /dev/null +++ b/Sprint 2/AI Fitness Project/requirements_windows.txt @@ -0,0 +1,51 @@ +asttokens==3.0.0 +bcrypt==4.3.0 +blinker==1.9.0 +click==8.1.8 +colorama==0.4.6 +comm==0.2.2 +debugpy==1.8.13 +decorator==5.2.1 +dnspython==2.7.0 +email_validator==2.2.0 +executing==2.2.0 +Flask==3.1.0 +Flask-Bcrypt==1.0.1 +Flask-Login==0.6.3 +Flask-MySQLdb==2.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.2 +greenlet==3.1.1 +idna==3.10 +ipykernel==6.29.5 +ipython==9.0.2 +ipython_pygments_lexers==1.1.1 +itsdangerous==2.2.0 +jedi==0.19.2 +Jinja2==3.1.6 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mysqlclient==2.2.7 +nest-asyncio==1.6.0 +packaging==24.2 +parso==0.8.4 +platformdirs==4.3.7 +prompt_toolkit==3.0.50 +psutil==7.0.0 +pure_eval==0.2.3 +Pygments==2.19.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pywin32==310 +pyzmq==26.3.0 +six==1.17.0 +SQLAlchemy==2.0.39 +stack-data==0.6.3 +tornado==6.4.2 +traitlets==5.14.3 +typing_extensions==4.12.2 +wcwidth==0.2.13 +Werkzeug==3.1.3 +WTForms==3.2.1 diff --git a/Sprint 2/AI Fitness Project/static/images/arrow.png b/Sprint 2/AI Fitness Project/static/images/arrow.png new file mode 100644 index 000000000..99a73c7c5 Binary files /dev/null and b/Sprint 2/AI Fitness Project/static/images/arrow.png differ diff --git a/Sprint 2/AI Fitness Project/static/scripts/calendarScript.js b/Sprint 2/AI Fitness Project/static/scripts/calendarScript.js new file mode 100644 index 000000000..7ea0082cf --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/scripts/calendarScript.js @@ -0,0 +1,148 @@ +let display = document.querySelector(".display"); +let previous = document.querySelector(".left"); +let next = document.querySelector(".right"); +let days = document.querySelector(".days"); +let selected = document.querySelector(".selected"); + +let today = new Date(); +let date = new Date(today); +let year = date.getFullYear(); +let month = date.getMonth(); + + +function calendarDisplay() { + days.innerHTML = ''; + + let formattedDate = date.toLocaleString("en-US", { + month: "long", + year: "numeric", + }); + display.innerHTML = formattedDate; + + const firstDay = new Date(year, month, 1); + const firstDayIndex = firstDay.getDay(); + + const lastDay = new Date(year, month + 1, 0); + const numberOfDays = lastDay.getDate(); + + for (let x = 1; x <= firstDayIndex; x++) { + let div = document.createElement("div"); + div.classList.add("days"); + div.innerHTML = ""; + days.appendChild(div); + } + for (let i = 1; i <= numberOfDays; i++) { + let div = document.createElement("div"); + div.classList.add("day"); + + let dayNum = document.createElement("div"); + dayNum.classList.add("days"); + dayNum.textContent = i; + div.appendChild(dayNum); + + let plusButton = document.createElement("button"); + plusButton.classList.add("plus-button"); + plusButton.textContent = "+"; + + div.appendChild(plusButton); + + days.appendChild(div); + + + let currentDate = new Date(year, month, i); + if ( + currentDate.getFullYear() === new Date().getFullYear() && + currentDate.getMonth() === new Date().getMonth() && + currentDate.getDate() === new Date().getDate() + ) { + div.classList.add("current-date"); + } + } + displaySelectedDate(); + displaySelectedPlus(); +} + +function displaySelectedPlus(){ + const dayElements = document.querySelectorAll(".day"); + dayElements.forEach((day) => { + day.addEventListener("click", (e) => { + e.stopPropagation(); + createPage(); + }); + }); +} + +function createPage(){ + const bottomPage = document.createElement("div"); + bottomPage.classList.add("bottom-page"); + bottomPage.innerHTML = ` +
+ +

FITNESS LOG INFO TO BE PLACED HERE

+
+`; +document.body.appendChild(bottomPage); + +bottomPage.querySelector(".close-button").addEventListener("click", () => { + bottomPage.remove(); +}); +} + +function displaySelectedDate() { + const plusElement = document.querySelectorAll(".plus-button"); + plusElement.forEach((plusButton)=> { + plusButton.addEventListener("click", (e) => { + openPopup(); + }); + }); + } +function openPopup(){ + const popup = document.createElement("div"); + popup.classList.add("popup-container"); + + popup.innerHTML = ` + + `; + + document.body.appendChild(popup); + + popup.querySelector(".removeButton").addEventListener("click", () => { + popup.remove(); + }); + popup.querySelector(".cardio").addEventListener("click", cardioLink); + popup.querySelector(".muscular").addEventListener("click", muscularLink); +} + +function cardioLink() { + window.open("/Sprint 2/AI Fitness Project/templates/cardio-log.html", "_blank"); +} + +function muscularLink() { + window.open("/Sprint 2/AI Fitness Project/templates/fitness-log.html", "_blank"); +} + +previous.addEventListener("click", () => { + month--; + if (month < 0) { + month = 11; + year--; + } + date.setFullYear(year, month); + calendarDisplay(); +}); + +next.addEventListener("click", () => { + month++; + if (month > 11) { + month = 0; + year++; + } + date.setFullYear(year, month); + calendarDisplay(); +}); + +calendarDisplay(); diff --git a/Sprint 2/AI Fitness Project/static/scripts/cardio-log.js b/Sprint 2/AI Fitness Project/static/scripts/cardio-log.js new file mode 100644 index 000000000..3e4a3a437 --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/scripts/cardio-log.js @@ -0,0 +1,24 @@ +document.getElementById("add-form-btn").addEventListener("click", function() { + const formContainer = document.getElementById("cardio-form"); + const lastFormChild = document.querySelector(".form-child"); + const submitButton = document.getElementById("submit-button"); + + if (lastFormChild && submitButton) { + const clonedFormChild = lastFormChild.cloneNode(true); + clonedFormChild.querySelectorAll("input").forEach(input => input.value = ""); + + let distance = 0; + + clonedFormChild.querySelector("#distance_field input").value = distance.toFixed(2); + clonedFormChild.querySelector("#minute_field input").value = 0; + clonedFormChild.querySelector("#second_field input").value = 0; + + formContainer.insertBefore(clonedFormChild, submitButton); + + clonedFormChild.querySelector("#delete-field").addEventListener("click", function() { + clonedFormChild.remove(); + }); + } +}); + + diff --git a/Sprint 2/AI Fitness Project/static/scripts/fitness-log.js b/Sprint 2/AI Fitness Project/static/scripts/fitness-log.js new file mode 100644 index 000000000..ef8f3e001 --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/scripts/fitness-log.js @@ -0,0 +1,87 @@ +let display = document.querySelector(".display"); +let previous = document.querySelector(".left"); +let next = document.querySelector(".right"); +let days = document.querySelector(".days"); +let selected = document.querySelector(".selected"); + +let today = new Date(); +let date = new Date(today); +let year = date.getFullYear(); +let month = date.getMonth(); + + +function calendarDisplay() { + days.innerHTML = ''; + + let formattedDate = date.toLocaleString("en-US", { + month: "long", + year: "numeric", + }); + display.innerHTML = formattedDate; + + const firstDay = new Date(year, month, 1); + const firstDayIndex = firstDay.getDay(); + + const lastDay = new Date(year, month + 1, 0); + const numberOfDays = lastDay.getDate(); + + for (let x = 1; x <= firstDayIndex; x++) { + let div = document.createElement("div"); + div.classList.add("days"); + div.innerHTML = ""; + days.appendChild(div); + } + + for (let i = 1; i <= numberOfDays; i++) { + let div = document.createElement("div"); + + div.classList.add("day"); + div.innerHTML = i; + days.appendChild(div); + + let currentDate = new Date(year, month, i); + if ( + currentDate.getFullYear() === new Date().getFullYear() && + currentDate.getMonth() === new Date().getMonth() && + currentDate.getDate() === new Date().getDate() + ) { + div.classList.add("current-date"); + } + } + + displaySelected(); +} + +function displaySelected() { + const dayElements = document.querySelectorAll(".days div"); + dayElements.forEach((day) => { + day.addEventListener("click", (e) => { + const selectedDate = e.target.dataset.date; + selected.innerHTML = `Selected Date : ${selectedDate}`; + window.open("/Sprint 2/AI Fitness Project/templates/fitness-log.html", '_blank') + }); + }); + } + displaySelected(); + +previous.addEventListener("click", () => { + month--; + if (month < 0) { + month = 11; + year--; + } + date.setFullYear(year, month); + calendarDisplay(); +}); + +next.addEventListener("click", () => { + month++; + if (month > 11) { + month = 0; + year++; + } + date.setFullYear(year, month); + calendarDisplay(); +}); + +calendarDisplay(); \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/static/scripts/workout-log.js b/Sprint 2/AI Fitness Project/static/scripts/workout-log.js new file mode 100644 index 000000000..b65fec771 --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/scripts/workout-log.js @@ -0,0 +1,21 @@ +document.getElementById("add-form-btn").addEventListener("click", function() { + const formContainer = document.getElementById("workout-form"); + const lastFormChild = document.querySelector(".form-child"); + const submitButton = document.getElementById("submit-button"); + + if (lastFormChild && submitButton) { + const clonedFormChild = lastFormChild.cloneNode(true); + clonedFormChild.querySelectorAll("input").forEach(input => input.value = ""); + + clonedFormChild.querySelector("#sets_field input").value = 1; + clonedFormChild.querySelector("#reps_field input").value = 1; + + formContainer.insertBefore(clonedFormChild, submitButton); + + clonedFormChild.querySelector("#delete-field").addEventListener("click", function() { + clonedFormChild.remove(); + }); + } +}); + + diff --git a/Sprint 2/AI Fitness Project/static/styles/cardio-log.css b/Sprint 2/AI Fitness Project/static/styles/cardio-log.css new file mode 100644 index 000000000..1a68f4918 --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/styles/cardio-log.css @@ -0,0 +1,56 @@ +.forms-wrapper { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-top: 25px; + +} + + +.cardio-form { + width: 800px; +} + +.form-child{ + display: inline-flex; + border-style: solid; + padding: 15px; + border-radius: 10px; + background-color: rgb(228, 224, 224); + font-weight: 1000; + margin-bottom: 10px; +} + +#delete-field { + all: unset; + color: black; + border-radius: 5px; + size: 5px; + padding-right: 15px; + cursor: pointer; +} + +#minute_field { + padding-right: 10px; +} + + +#distance_field { + padding-right: 20px; +} + + + +#submit-button { + padding-top: 15px; +} + +#add-form-btn { + float: right; + margin-right: 965px; +} + +#back-btn{ + padding-left: 15px; +} \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/static/styles/fitness-log.css b/Sprint 2/AI Fitness Project/static/styles/fitness-log.css new file mode 100644 index 000000000..6621768a8 --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/styles/fitness-log.css @@ -0,0 +1,26 @@ +.forms-wrapper { + display: flex; + gap: 20px; + justify-content: space-between; +} + +.workout-form, .cardio-form { + width: 48%; + padding-left: 10px; +} + +#workout-form, #cardio-form { + display: none; +} + +button { + margin-bottom: 15px; +} + +label { + margin-left: 10px; +} + +#submit-button { + padding-top: 20px; +} diff --git a/Sprint 2/AI Fitness Project/static/styles/navbar.css b/Sprint 2/AI Fitness Project/static/styles/navbar.css new file mode 100644 index 000000000..22a5a8cbf --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/styles/navbar.css @@ -0,0 +1,65 @@ +html, body{ + margin: 0; + padding: 0; +} +#navbar { + * { + text-decoration: none; + padding: 0; + margin: 0; + box-sizing: border-box; + } + .navbar { + background: gray; + font-family: calibri; + padding: 10px 20px; + } + .navdiv { + display: flex; + align-items: center; + justify-content: space-between; + } + .logo a { + color: white; + font-size: 21px; + font-weight: bold; + } + ul { + display: flex; + list-style: none; + padding: 0; + } + li { + margin-right: 38px; + } + li a { + color: white; + font-size: 25px; + font-weight: bold; + } + li a:hover{ + color: lightgray; + } + .buttons { + display: flex; + gap: 10px; + } + button { + background-color: black; + border: none; + border-radius: 10px; + padding: 10px; + width: 90px; + cursor: pointer; + } + button a { + color: white; + font-weight: bold; + font-size: 15px; + display: block; + text-align: center; + } + button a:hover{ + color:lightgray; + } +} diff --git a/Sprint 2/AI Fitness Project/static/styles/workout-log.css b/Sprint 2/AI Fitness Project/static/styles/workout-log.css new file mode 100644 index 000000000..f67868b4c --- /dev/null +++ b/Sprint 2/AI Fitness Project/static/styles/workout-log.css @@ -0,0 +1,56 @@ +.forms-wrapper { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-top: 25px; + +} + + +.workout-form { + width: 800px; +} + +.form-child{ + display: inline-flex; + border-style: solid; + padding: 15px; + border-radius: 10px; + background-color: rgb(228, 224, 224); + font-weight: 1000; + margin-bottom: 10px; +} + +#delete-field { + all: unset; + color: black; + border-radius: 5px; + size: 5px; + padding-right: 15px; + cursor: pointer; +} + +#sets_field { + margin-right: 15px; +} + + +#exercise_field { + padding-right: 20px; +} + + + +#submit-button { + padding-top: 15px; +} + +#add-form-btn { + float: right; + margin-right: 965px; +} + +#back-btn{ + padding-left: 15px; +} \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/about.html b/Sprint 2/AI Fitness Project/templates/about.html new file mode 100644 index 000000000..6f6039ed1 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/about.html @@ -0,0 +1,12 @@ + + + + + + About + + + {% include 'navbar.html' %} +

About Page

+ + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/calendarPage.html b/Sprint 2/AI Fitness Project/templates/calendarPage.html new file mode 100644 index 000000000..7ba719654 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/calendarPage.html @@ -0,0 +1,246 @@ + + + + + + Calendar + + + + +
+
+
+
+
+

""

+
+
+
+
+
Su
+
Mo
+
Tu
+
We
+
Th
+
Fr
+
Sa
+
+
+
+
+
+

+
+ + + + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/cardio-log.html b/Sprint 2/AI Fitness Project/templates/cardio-log.html new file mode 100644 index 000000000..7e5884876 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/cardio-log.html @@ -0,0 +1,39 @@ + + + + + + Fitness Log + + + + {% include 'navbar.html' %} + Back

+ + + +
+
+
+
+ +
+ + {{ form.distance_field }} +
+
+ + {{ form.minute_field }} +
+
+ + {{ form.second_field }} +
+
+
{{ form.submit }}
+
+
+ + + + diff --git a/Sprint 2/AI Fitness Project/templates/fitness-log.html b/Sprint 2/AI Fitness Project/templates/fitness-log.html new file mode 100644 index 000000000..696126f5e --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/fitness-log.html @@ -0,0 +1,74 @@ + + + + + + Fitness Log + + + + {% include 'navbar.html' %} +

Fitness Log

+ +
+
+ + +
+ {{ workout_form.hidden_tag() }} +
+

Legs

+ + {{ workout_form.legs_reps }} + + {{ workout_form.legs_sets }} +
+
+

Back

+ + {{ workout_form.back_reps }} + + {{ workout_form.back_sets }} +
+
+

Chest

+ + {{ workout_form.chest_reps }} + + {{ workout_form.chest_sets }} +
+
+

Tricep

+ + {{ workout_form.tricep_reps }} + + {{ workout_form.tricep_sets }} +
+
+

Bicep

+ + {{ workout_form.bicep_reps }} + + {{ workout_form.bicep_sets }} +
+
{{ workout_form.submit }}
+
+
+ +
+ + +
+ {{ cardio_form.hidden_tag() }} +
+ + {{ cardio_form.miles }} +
+
{{ cardio_form.submit }}
+
+
+
+ + + + diff --git a/Sprint 2/AI Fitness Project/templates/index.html b/Sprint 2/AI Fitness Project/templates/index.html new file mode 100644 index 000000000..863610483 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/index.html @@ -0,0 +1,25 @@ + + + + + + Home + + + {% include 'navbar.html' %} +

Welcome, {{ first_name|capitalize }}!

+

Your Fitness Goals:

+ + + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/login.html b/Sprint 2/AI Fitness Project/templates/login.html new file mode 100644 index 000000000..78225206a --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/login.html @@ -0,0 +1,20 @@ + + + + + + Login Page + + +

Login

+ +
+ {{ form.hidden_tag() }} + {{ form.username }} + {{ form.password }} + {{ form.submit }} +
+ + Don't have an account? Register + + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/main-chat.html b/Sprint 2/AI Fitness Project/templates/main-chat.html new file mode 100644 index 000000000..a2b68bd6d --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/main-chat.html @@ -0,0 +1,12 @@ + + + + + + Main Chat + + + {% include 'navbar.html' %} +

Main AI Chat Page

+ + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/navbar.html b/Sprint 2/AI Fitness Project/templates/navbar.html new file mode 100644 index 000000000..5e1397826 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/navbar.html @@ -0,0 +1,28 @@ + + + + + + Navigation Bar + + + + + + + diff --git a/Sprint 2/AI Fitness Project/templates/register.html b/Sprint 2/AI Fitness Project/templates/register.html new file mode 100644 index 000000000..a24e1f4f2 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/register.html @@ -0,0 +1,30 @@ + + + + + + Register Page + + +

Register

+ +
+ {{ form.hidden_tag() }} + +
+ {{ form.username }} + {{ form.password }} +
+ +
+ {{ form.first_name }} + {{ form.last_name }} +
+ + + {{ form.submit }} +
+ + Already have an account? Log In + + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/survey.html b/Sprint 2/AI Fitness Project/templates/survey.html new file mode 100644 index 000000000..763bc7ef9 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/survey.html @@ -0,0 +1,23 @@ + + + + + + Document + + +

Fitness Survey: What are your goals?

+ +
+ {{ form.hidden_tag() }} +

{{ form.fitness_goals.label }}

+ {% for subfield in form.fitness_goals %} +
+ {{ subfield }} {{ subfield.label }} +
+ {% endfor %} +
+ {{ form.submit() }} +
+ + \ No newline at end of file diff --git a/Sprint 2/AI Fitness Project/templates/workout-log.html b/Sprint 2/AI Fitness Project/templates/workout-log.html new file mode 100644 index 000000000..e19d6b454 --- /dev/null +++ b/Sprint 2/AI Fitness Project/templates/workout-log.html @@ -0,0 +1,41 @@ + + + + + + Fitness Log + + + + {% include 'navbar.html' %} + Back

+ + + +
+
+
+
+ +
+ {{ form.exercise_input }} +
+
+ + {{ form.sets_field }} +
+ +
+ + {{ form.reps_field }} +
+
+ + +
{{ form.submit }}
+
+
+ + + +