From d47870e1bb485d57eeda5d3b2eec73efa9bd6ded Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Thu, 7 Nov 2024 12:28:01 -0500 Subject: [PATCH 01/11] added consert --- flaskApp/consert/consert_engine.py | 25 ++++++++++++++++++++++++ flaskApp/firebase/serviceAccountKey.json | 0 2 files changed, 25 insertions(+) create mode 100644 flaskApp/consert/consert_engine.py delete mode 100644 flaskApp/firebase/serviceAccountKey.json diff --git a/flaskApp/consert/consert_engine.py b/flaskApp/consert/consert_engine.py new file mode 100644 index 0000000..eb00749 --- /dev/null +++ b/flaskApp/consert/consert_engine.py @@ -0,0 +1,25 @@ +import consert + +class ConsertProcess: + def __init__(self): + """Initializes the ConsertProcess with test data and runs consert.""" + print("starting init") + self.media_file = '/Users/tucker/Documents/ORCA/CONSert/test_video.mp3' #TODO will need to adjust for input data + self.run_consert(self.media_file) + + def run_consert(self, media_file): + # Test 1: Run pause identification and classification + consert.classify_pauses( + input_filepath=media_file, + output_directory='test_output', # TODO: Adjust output path as needed + save_output_file=True, + save_intermediate_files=True, + audio_file_format='mp3', # Ensure correct format for 'mp3' + whisper_model_name='medium' + ) + + # Test 2: Plot results + consert.plot_classification(media_file, 'test_output') + +# Initialize the class to run the process +process = ConsertProcess() diff --git a/flaskApp/firebase/serviceAccountKey.json b/flaskApp/firebase/serviceAccountKey.json deleted file mode 100644 index e69de29..0000000 From 4553ebdc49531b8f9a2235488e18740aae9fc551 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:00:36 -0500 Subject: [PATCH 02/11] moved consert_install into consert directory --- .../consert/CONSERT_INSTALLATION.md | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) rename CONSERT_INSTALLION.MD => flaskApp/consert/CONSERT_INSTALLATION.md (78%) diff --git a/CONSERT_INSTALLION.MD b/flaskApp/consert/CONSERT_INSTALLATION.md similarity index 78% rename from CONSERT_INSTALLION.MD rename to flaskApp/consert/CONSERT_INSTALLATION.md index a025c7a..3d8f246 100644 --- a/CONSERT_INSTALLION.MD +++ b/flaskApp/consert/CONSERT_INSTALLATION.md @@ -12,12 +12,9 @@ This section of the guide is meant for HUA contributors who have access to the C As of 10/24/2024 testing and implementation of CONSert has been done with CONDA environments running python 3.10.11. The linux/macos version of this package must be run on python 3.10 or earlier. -### Cloning the Repository - -Start by cloning the repository from github into your desired directory and activating any desired virtual environment. - ### Install Via pip +Start by cloning the repository from github into your desired directory and activating any desired virtual environment. If you are a owner/maintainer/developer you can install the packagage via pip install (path to cloned repo). ``` @@ -26,9 +23,9 @@ pip install path/to/cloned/CONSert ### The Models -CONSert's pause detection requires several models to run: CNN, RF, and BERT. The paths to the models are specified in package.json which can be found here: +CONSert's pause detection and classification requires several models to run: CNN, RF, and BERT. The paths to the models are specified in package.json which can be found in the root directory of the package: ``` -CONSERT/consert/package.json +/consert/package.json ``` and looks like this: @@ -45,7 +42,7 @@ and looks like this: Update your model paths by replacaing: aboslute/path/to/ with the absolute path to the original code directory. When you're done, save the packages.json file. -As of now the BERT models are too large to store in GitHub. The can be transferred as .tar compressed file using fileshare. Create a directory title BERT inside "absolute/path/to/original_code/models/ " and extract the tar file here. +As of now we are not sure the best practice for storing these models in git. The can be transferred as .tar compressed file using fileshare. Create a directory title BERT inside "absolute/path/to/original_code/models/ " and extract the tar file here. ### Whisper AI Transcription @@ -59,10 +56,10 @@ This will downgrade the version of Whisper to the most recent compatable version ### Testing -The CONSert repository now has a test_script.py file. Update the media_file fiel to have the correct path to your mp3 or wav. Similarly update the audio format field to the correct file type. - +You can verify the package's installation via +```` + pip show consert ```` -media_file = 'path/to/test/file.wav' -audio_file_format = 'wav' -```` \ No newline at end of file + + From 791dc4a477fb12f529fed9cc3568125fc08c7420 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:00:52 -0500 Subject: [PATCH 03/11] linked consert install in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7bda9c9..688262e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ TBD ### Installing -TBD +- [Consert Install Guide](flaskApp/consert/CONSERT_INSTALLATION.md) ### Executing program From e54f8314c7a88f34e8f08aa07d0104702e245ba6 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:10:51 -0500 Subject: [PATCH 04/11] added the referenced test script --- flaskApp/consert/consert_test_script.py | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 flaskApp/consert/consert_test_script.py diff --git a/flaskApp/consert/consert_test_script.py b/flaskApp/consert/consert_test_script.py new file mode 100644 index 0000000..91fbcd1 --- /dev/null +++ b/flaskApp/consert/consert_test_script.py @@ -0,0 +1,28 @@ +import consert + +media_file = 'path/to/media/file' + +# Test 1: run the pauses identification and classification +consert.classify_pauses( + input_filepath = media_file, + output_directory = 'flaskApp/consert/test_output', + save_output_file = True, + save_intermediate_files = True, + # use_intermediate_files = + audio_file_format = 'mp3', # be sure to change this for other audio formats + # audio_sampling_rate = + # cnn_confidence_threshold = + # rf_confidence_threshold = + # frame_length = , + # cnn_time_per_prediction = + # rf_feature_length = + # rf_feature_step = + whisper_model_name = 'medium', + # whisper_device = + # whisper_time_before_pause = + # whisper_time_after_pause = + # bert_n_folds = +) + +# Test 2: plot results +consert.plot_classification(media_file, 'flaskApp/consert/test_output') \ No newline at end of file From 73958e926b36cc0e05ebc1dfc052e44973cc0d36 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:11:51 -0500 Subject: [PATCH 05/11] editing text --- flaskApp/consert/CONSERT_INSTALLATION.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/flaskApp/consert/CONSERT_INSTALLATION.md b/flaskApp/consert/CONSERT_INSTALLATION.md index 3d8f246..0f5a1ce 100644 --- a/flaskApp/consert/CONSERT_INSTALLATION.md +++ b/flaskApp/consert/CONSERT_INSTALLATION.md @@ -12,7 +12,7 @@ This section of the guide is meant for HUA contributors who have access to the C As of 10/24/2024 testing and implementation of CONSert has been done with CONDA environments running python 3.10.11. The linux/macos version of this package must be run on python 3.10 or earlier. -### Install Via pip +### Pip Install Via Local Repository Start by cloning the repository from github into your desired directory and activating any desired virtual environment. If you are a owner/maintainer/developer you can install the packagage via pip install (path to cloned repo). @@ -21,6 +21,14 @@ If you are a owner/maintainer/developer you can install the packagage via pip in pip install path/to/cloned/CONSert ``` +### Pip Install Via Github Repository + +Alternatively the package can be installed without the original source code. + +``` +pip install git+https://github.com/Heard-and-Understood/CONSert.git +``` + ### The Models CONSert's pause detection and classification requires several models to run: CNN, RF, and BERT. The paths to the models are specified in package.json which can be found in the root directory of the package: @@ -56,10 +64,11 @@ This will downgrade the version of Whisper to the most recent compatable version ### Testing -You can verify the package's installation via +You can verify the package's installation via: ```` pip show consert ```` +Once consert has been installed update the path to media file in [Consert Test Script](consert_test_script.py) and give it a run. If consert is able to fully process it will create a summary png in the test_output directory. From b260c771bc719c6c4ea00a39ea892e304d17bab3 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:12:03 -0500 Subject: [PATCH 06/11] editing text --- flaskApp/consert/CONSERT_INSTALLATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flaskApp/consert/CONSERT_INSTALLATION.md b/flaskApp/consert/CONSERT_INSTALLATION.md index 0f5a1ce..cc6902f 100644 --- a/flaskApp/consert/CONSERT_INSTALLATION.md +++ b/flaskApp/consert/CONSERT_INSTALLATION.md @@ -8,7 +8,7 @@ Read more here: https://github.com/Heard-and-Understood/CONSert ## Installing the CONSert Package -This section of the guide is meant for HUA contributors who have access to the CONSert repository. The installation guide there has inaccuracies and can be streamlined for HUA contributors. +This section of the guide is meant for HUA contributors who have access to the CONSert repository. As of 10/24/2024 testing and implementation of CONSert has been done with CONDA environments running python 3.10.11. The linux/macos version of this package must be run on python 3.10 or earlier. From 8b8e659fbdfa83e478e0d460bce8df8a91b514e1 Mon Sep 17 00:00:00 2001 From: Tuckers15 Date: Fri, 8 Nov 2024 11:14:13 -0500 Subject: [PATCH 07/11] added consert install to dependencies --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 688262e..94469a1 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ TBD ### Dependencies -TBD +- [Consert Install Guide](flaskApp/consert/CONSERT_INSTALLATION.md) ### Installing -- [Consert Install Guide](flaskApp/consert/CONSERT_INSTALLATION.md) +TBD ### Executing program From 57a9e3dc075ba61416729cfc6b57bee33ee6096e Mon Sep 17 00:00:00 2001 From: felixwalberg Date: Thu, 14 Nov 2024 12:27:24 -0500 Subject: [PATCH 08/11] Updated gitignore --- flaskApp/.gitignore | 1 + .../firebase/__pycache__/config.cpython-311.pyc | Bin 813 -> 832 bytes 2 files changed, 1 insertion(+) diff --git a/flaskApp/.gitignore b/flaskApp/.gitignore index a78a652..87dcda0 100644 --- a/flaskApp/.gitignore +++ b/flaskApp/.gitignore @@ -4,3 +4,4 @@ firebase/serviceAccountKey.json __pycache__/ *.pyc *.log +email_credentials.py \ No newline at end of file diff --git a/flaskApp/firebase/__pycache__/config.cpython-311.pyc b/flaskApp/firebase/__pycache__/config.cpython-311.pyc index 9298513b87428949152b8ae7504e751247bc1abd..45ebe1d37e0370327011528a658b02aa9c99c5a6 100644 GIT binary patch delta 63 zcmZ3>c7Tn0IWI340}!a_nQi12Wm1dN4=qkDD%MX+&B?4NPs~Y5ElSr94f6%zcsEBM OpLin!Ba_L-OuqmXC=yTr delta 44 ycmX@Www8^1IWI340}#|-7vIP&$|UTqA6lGRRIHzvQk0pRmz$rLT|C*1=@$SBLJiIU From 67acc63ad832fb0df838ce509d6bf9fef483b815 Mon Sep 17 00:00:00 2001 From: adrienmonks11 Date: Thu, 14 Nov 2024 12:29:16 -0500 Subject: [PATCH 09/11] started implementing --- .DS_Store | Bin 0 -> 6148 bytes flaskApp/.DS_Store | Bin 0 -> 6148 bytes flaskApp/app.py | 18 ++++++- .../__pycache__/config.cpython-311.pyc | Bin 813 -> 813 bytes flaskApp/templates/forgot_password.html | 45 ++++++++++++++++++ flaskApp/templates/reset_password.html | 1 + 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 flaskApp/.DS_Store create mode 100644 flaskApp/templates/forgot_password.html create mode 100644 flaskApp/templates/reset_password.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ee522b1494eba069da762150a26972ba004e1bd3 GIT binary patch literal 6148 zcmeHKJ5EC}5S)b+Pohbg(pTUHCJHCu0)#{~JSmYP`d4u-j>hb#AbQXxO*AX5$6oK) z@)U310@7;iWrg3Gd}QyBaZviZk%QR9dPb|cZ}HL^qc>7 zIE=n2lLAse3P=GdAO$X}K$X|w;i9MNFexAfZb1S6J~X;xFPswN)4?HH0OEq-FwUcw zAT|#Wd*PJG2+fj8OsZ9jVM%AaRbDTg5|a+A=ELe{s}9BDcAnoN9o7>yN&zWwuE2dR zmtOy$>HqZq=OnG9fE2hX1#Gc-*{u1bs;#rfd97{qSGwnX)7>}^3Wq4i#3;vHcsX80 cQsyTIIp#h{zmtlGu@5zpm2zCOpJ2Og_q;! cNXoqCbME)TF)`?j2c4*&0oO$)1#Ydt50dW{YXATM literal 0 HcmV?d00001 diff --git a/flaskApp/app.py b/flaskApp/app.py index fa4eaac..3084ef3 100644 --- a/flaskApp/app.py +++ b/flaskApp/app.py @@ -1,7 +1,7 @@ from flask import Flask, render_template, request, redirect, url_for, jsonify, flash, json import firebase_admin from firebase_admin import credentials, firestore -import bcrypt +import bcrypt, secrets from firebase.config import Config from db_utils import upload_file_to_db, connect_to_database @@ -62,6 +62,22 @@ def forgot_password(): return render_template('forgot_password.html') +@app.route('/reset_password', methods=['GET', 'POST']) +def reset_password(): + if request.method == 'POST': + email = request.form.get('email') + user_ref = db.collection('users').document(email) + if user_ref.get().exists: + verificationSecret = secrets.token_hex(3) + #send email + return render_template('reset_password.html', verificationSecret = verificationSecret) + else: + flash("No account with this email exists.", "danger") + return redirect(url_for('forgot_password')) + + + + @app.route('/login', methods=['GET', 'POST']) def login(): email = request.form.get('email') diff --git a/flaskApp/firebase/__pycache__/config.cpython-311.pyc b/flaskApp/firebase/__pycache__/config.cpython-311.pyc index 9298513b87428949152b8ae7504e751247bc1abd..f620cb76ca6096b430b4cfd1fffcebf27a072837 100644 GIT binary patch delta 20 acmZ3>ww8^1IWI340}#Aer@WC{i5UPjF$C8D delta 20 acmZ3>ww8^1IWI340}#|-7vIRO#0&s43 + + + + + Register + + + + +
+
+
+

Reset Your Password

+ + + {% with messages = get_flashed_messages() %} + {% if messages %} + + {% endif %} + {% endwith %} + +
+ +
+ + +
+ + + + +
+
+
+
+ + + + + + + diff --git a/flaskApp/templates/reset_password.html b/flaskApp/templates/reset_password.html new file mode 100644 index 0000000..9b4dd17 --- /dev/null +++ b/flaskApp/templates/reset_password.html @@ -0,0 +1 @@ +Here \ No newline at end of file From 627efde1730b1367c2060bb93e678dded6c97ad1 Mon Sep 17 00:00:00 2001 From: felixwalberg Date: Tue, 19 Nov 2024 11:59:18 -0500 Subject: [PATCH 10/11] PI request form on homepage --- flaskApp/app.py | 56 +++++++++++++++- flaskApp/templates/home.html | 5 ++ flaskApp/templates/pi_access_request.html | 65 +++++++++++++++++++ .../templates/pre_approved_access_code.html | 44 +++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 flaskApp/templates/pi_access_request.html create mode 100644 flaskApp/templates/pre_approved_access_code.html diff --git a/flaskApp/app.py b/flaskApp/app.py index fa4eaac..9fdff0d 100644 --- a/flaskApp/app.py +++ b/flaskApp/app.py @@ -4,6 +4,8 @@ import bcrypt from firebase.config import Config from db_utils import upload_file_to_db, connect_to_database +from flask_mail import Mail, Message +import email_credentials app = Flask(__name__) app.config.from_object(Config) @@ -22,6 +24,15 @@ def initialize_firebase(): initialize_firebase() db = firestore.client() +# Configure Flask-Mail email settings +app.config['MAIL_SERVER'] = 'smtp.gmail.com' # SMTP email server +app.config['MAIL_PORT'] = 587 +app.config['MAIL_USERNAME'] = email_credentials.hua_email +app.config['MAIL_PASSWORD'] = email_credentials.hua_password +app.config['MAIL_USE_TLS'] = True +app.config['MAIL_USE_SSL'] = False +mail = Mail(app) + @app.route("/") def home(): @@ -50,7 +61,7 @@ def register(): 'email': email, 'password': hashed_password.decode('utf-8') }) - return redirect(url_for('homepage')) + return redirect(url_for('homepage', email=email)) except Exception as e: flash(f"An error occurred: {str(e)}", "danger") @@ -61,6 +72,42 @@ def register(): def forgot_password(): return render_template('forgot_password.html') +@app.route('/pi_access_request', methods=['GET', 'POST']) +def pi_access_request(): + name = request.form.get('name') + email = request.form.get('email') + institution = request.form.get('institution') + if request.method == 'POST': + try: + # Add request to HUA firebase + requests_ref = db.collection('request') + requests_doc = requests_ref.add({"username":email}) + + # Send email + recipients = email_credentials.recipients + + emailMessage = Message("Request for PI Access", sender=email,recipients=recipients) + emailMessage.body = f"Hello Bob and Donna,\n\n {name} is requesting admin access. {name} is from {institution} and reachable at {email}.\n\n You will find their request on the View Requests for Access page in the Heard and Understood App." + mail.send(emailMessage) + print('email sent successfully!') + except Exception as e: + print('problem sending email') + print(e) + + return redirect(url_for('homepage')) + return render_template('pi_access_request.html') + +@app.route('/pre_approved_access_code', methods=['GET', 'POST']) +def pre_approved_access_code(): + access_code = request.form.get('PIAccessCode') + if request.method == 'POST': + try: + print(f"pre-approved code {access_code} entered") + except: + print("issue with pre-approved code") + return redirect(url_for('homepage')) + return render_template('pre_approved_access_code.html') + @app.route('/login', methods=['GET', 'POST']) def login(): @@ -78,7 +125,7 @@ def login(): # Check if the password matches if bcrypt.checkpw(password.encode('utf-8'), stored_hashed_password.encode('utf-8')): - return redirect(url_for('homepage')) + return redirect(url_for('homepage', email=email)) else: flash("Invalid password", "danger") return redirect(url_for('login')) @@ -96,6 +143,10 @@ def login(): def dashboard(): return render_template("dashboard.html") +@app.route("/ground_truthing") +def ground_truthing(): + return render_template("ground_truthing.html") + @app.route("/homepage") def homepage(): return render_template('home.html') @@ -139,3 +190,4 @@ def view_files(): if __name__ == "__main__": app.run(debug=True, host='0.0.0.0', port=5001, threaded=False) + diff --git a/flaskApp/templates/home.html b/flaskApp/templates/home.html index 6ededdd..d84495e 100644 --- a/flaskApp/templates/home.html +++ b/flaskApp/templates/home.html @@ -17,6 +17,11 @@

Enter Access Code

+ diff --git a/flaskApp/templates/pi_access_request.html b/flaskApp/templates/pi_access_request.html new file mode 100644 index 0000000..24f11aa --- /dev/null +++ b/flaskApp/templates/pi_access_request.html @@ -0,0 +1,65 @@ + + + + + + Bootstrap Login Form + + + + +
+
+
+

Request PI Access

+

To request PI access, you must create an account + and submit a request to the administrators.

+

Already have a pre-approved access code?

+ + + + {% with messages = get_flashed_messages() %} + {% if messages %} + + {% endif %} + {% endwith %} + +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
+
+

Once reviewed, you will receive an email with your request status: access granted or denied.

+

If access is granted, the next time you login you will have PI access.

+
+
+
+
+ + + + + + + diff --git a/flaskApp/templates/pre_approved_access_code.html b/flaskApp/templates/pre_approved_access_code.html new file mode 100644 index 0000000..26a5995 --- /dev/null +++ b/flaskApp/templates/pre_approved_access_code.html @@ -0,0 +1,44 @@ + + + + + + Bootstrap Login Form + + + + +
+
+
+

Enter Pre-Approved Access Code

+ + + {% with messages = get_flashed_messages() %} + {% if messages %} + + {% endif %} + {% endwith %} + +
+ +
+ + +
+ + + +
+
+
+
+ + + + + + + From f6f970e61c63e1b82ca8e66c70259944e4a7b634 Mon Sep 17 00:00:00 2001 From: adrienmonks11 Date: Thu, 21 Nov 2024 00:22:31 -0500 Subject: [PATCH 11/11] Implemented password reset --- flaskApp/app.py | 151 ++++++++++++++++++++++--- flaskApp/templates/enter_code.html | 44 +++++++ flaskApp/templates/login.html | 14 ++- flaskApp/templates/new_password.html | 48 ++++++++ flaskApp/templates/reset_password.html | 43 ++++++- 5 files changed, 278 insertions(+), 22 deletions(-) create mode 100644 flaskApp/templates/enter_code.html create mode 100644 flaskApp/templates/new_password.html diff --git a/flaskApp/app.py b/flaskApp/app.py index aaded55..34e7b04 100644 --- a/flaskApp/app.py +++ b/flaskApp/app.py @@ -6,6 +6,7 @@ from db_utils import upload_file_to_db, connect_to_database from flask_mail import Mail, Message import email_credentials +from datetime import datetime, timedelta, timezone app = Flask(__name__) app.config.from_object(Config) @@ -108,29 +109,133 @@ def pre_approved_access_code(): return redirect(url_for('homepage')) return render_template('pre_approved_access_code.html') - @app.route('/reset_password', methods=['GET', 'POST']) def reset_password(): if request.method == 'POST': - email = request.form.get('email') + email = request.form.get('email').strip().lower() user_ref = db.collection('users').document(email) - if user_ref.get().exists: - verificationSecret = secrets.token_hex(3) - #send email - return render_template('reset_password.html', verificationSecret = verificationSecret) + user_doc = user_ref.get() + + if user_doc.exists: + verification_secret = secrets.token_hex(3) + expiration_time = datetime.now(timezone.utc) + timedelta(minutes=15) + + + user_ref.update({ + 'verification_code': verification_secret, + 'verification_expiry': expiration_time + }) + + subject = "HUA Password Reset" + body = f"""Hi, + +You recently requested a password reset for HUA. Your verification code is below. + +{verification_secret} + +This code will expire in 15 minutes. + +If you didn't request a password reset, you can safely disregard this email. + +This account is not monitored, please do not reply to this email. +""" + try: + msg = Message(subject, sender="Huaverso@gmail.com", recipients=[email], body=body) + mail.send(msg) + + flash("A verification code has been sent to your email.", "success") + return redirect(url_for('enter_code', email=email)) + except Exception: + flash("Failed to send verification email. Please try again later.", "danger") + return redirect(url_for('reset_password')) else: flash("No account with this email exists.", "danger") - return redirect(url_for('forgot_password')) - + return redirect(url_for('reset_password')) + + return render_template('reset_password.html') + +@app.route('/enter_code', methods=['GET', 'POST']) +def enter_code(): + email = request.args.get('email') + if not email: + flash("Invalid request.", "danger") + return redirect(url_for('reset_password')) + + if request.method == 'POST': + entered_code = request.form.get('verification_code').strip() + user_ref = db.collection('users').document(email) + user_doc = user_ref.get() + + if not user_doc.exists: + flash("Invalid request.", "danger") + return redirect(url_for('reset_password')) + + stored_code = user_doc.to_dict().get('verification_code') + expiry_time = user_doc.to_dict().get('verification_expiry') + + if not stored_code or not expiry_time: + flash("No verification code found. Please initiate the password reset again.", "danger") + return redirect(url_for('reset_password')) + + if entered_code != stored_code: + flash("Invalid verification code.", "danger") + return redirect(url_for('enter_code', email=email)) + + if datetime.now(timezone.utc) > expiry_time: + flash("The verification code has expired. Please request a new one.", "danger") + user_ref.update({ + 'verification_code': firestore.DELETE_FIELD, + 'verification_expiry': firestore.DELETE_FIELD + }) + return redirect(url_for('reset_password')) + + return redirect(url_for('new_password', email=email)) + + return render_template('enter_code.html', email=email) + +@app.route('/new_password', methods=['GET', 'POST']) +def new_password(): + email = request.args.get('email') + if not email: + flash("Invalid request.", "danger") + return redirect(url_for('reset_password')) + + if request.method == 'POST': + new_password = request.form.get('new_password') + confirm_password = request.form.get('confirm_password') + + if new_password != confirm_password: + flash("Passwords do not match.", "danger") + return redirect(url_for('new_password', email=email)) + + if len(new_password) < 6: + flash("Password must be at least 6 characters long.", "danger") + return redirect(url_for('new_password', email=email)) + + # Hash the new password and decode to store as string + hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') + user_ref = db.collection('users').document(email) + user_ref.update({'password': hashed_password}) + + user_ref.update({ + 'verification_code': firestore.DELETE_FIELD, + 'verification_expiry': firestore.DELETE_FIELD + }) + + flash("Your password has been successfully reset. You can now log in.", "success") + return redirect(url_for('home')) + + return render_template('new_password.html', email=email) + @app.route('/login', methods=['GET', 'POST']) def login(): - email = request.form.get('email') - password = request.form.get('password') - if request.method == 'POST': + print("Form Data:", request.form) + email = request.form.get('email') + password = request.form.get('password') try: # Retrieve user data from Firestore user_ref = db.collection('users').document(email) @@ -139,11 +244,21 @@ def login(): if user_doc.exists: stored_hashed_password = user_doc.to_dict().get('password') - # Check if the password matches - if bcrypt.checkpw(password.encode('utf-8'), stored_hashed_password.encode('utf-8')): - return redirect(url_for('homepage', email=email)) + # Ensure the stored hashed password exists + if stored_hashed_password: + # Encode the stored hashed password to bytes + stored_hashed_password_bytes = stored_hashed_password.encode('utf-8') + + # Check if the password matches + if bcrypt.checkpw(password.encode('utf-8'), stored_hashed_password_bytes): + flash("Logged in successfully.", "success") + return redirect(url_for('homepage', email=email)) + else: + flash("Invalid password", "danger") + return redirect(url_for('login')) + else: - flash("Invalid password", "danger") + flash("Password not set for this account.", "danger") return redirect(url_for('login')) else: flash("User not found", "danger") @@ -152,8 +267,10 @@ def login(): except Exception as e: flash(f"An error occurred: {str(e)}", "danger") return redirect(url_for('login')) - if request.method == "GET": - return render_template('login.html') + + # Handle GET requests + return render_template('login.html') + @app.route("/dashboard") def dashboard(): diff --git a/flaskApp/templates/enter_code.html b/flaskApp/templates/enter_code.html new file mode 100644 index 0000000..6a69f4c --- /dev/null +++ b/flaskApp/templates/enter_code.html @@ -0,0 +1,44 @@ + + + + + + Enter Verification Code + + + + +
+
+
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} +

Enter Verification Code

+
+ +
+ + +
+ +
+ Back +
+
+
+ + + + + + diff --git a/flaskApp/templates/login.html b/flaskApp/templates/login.html index f08f53b..e08ecbf 100644 --- a/flaskApp/templates/login.html +++ b/flaskApp/templates/login.html @@ -14,14 +14,20 @@

Sign In

- {% with messages = get_flashed_messages() %} + {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} - + {% for category, message in messages %} + + {% endfor %} {% endif %} {% endwith %} +
diff --git a/flaskApp/templates/new_password.html b/flaskApp/templates/new_password.html new file mode 100644 index 0000000..fd37a4c --- /dev/null +++ b/flaskApp/templates/new_password.html @@ -0,0 +1,48 @@ + + + + + + Set New Password + + + + +
+
+
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} +

Set New Password

+ + +
+ + +
+
+ + +
+ + + Back +
+
+
+ + + + + + diff --git a/flaskApp/templates/reset_password.html b/flaskApp/templates/reset_password.html index 9b4dd17..9140442 100644 --- a/flaskApp/templates/reset_password.html +++ b/flaskApp/templates/reset_password.html @@ -1 +1,42 @@ -Here \ No newline at end of file + + + + + + Reset Password + + + + +
+
+
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} +

Reset Your Password

+
+
+ + +
+ +
+
+
+
+ + + + + +