diff --git a/.DS_Store b/.DS_Store index 953c41e94..eac18c8ef 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8bc141ad2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +#Hides CSRF key +.env \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 000000000..105ce2da2 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..db8786c06 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index b2588bc51..02b468505 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,20 @@ -My groupmembers are: -- XXXX -- XXXX -- XXXX -- XXXX +Team Penguin Members: +- Jo Vinson +- Andrew Dickerson +- Ben Karp +- Arth Chavda +- Nasmir Pasic +- Owen Cosner ------------------- Fill in some information about your project under this ------------------ +------------------ Project Summary ------------------ + +A medical records sharing platform, supporting both emergency sharing and recipient specified sharing. + +The program has a web front end, with different portals for doctors and patients. Patients are able to fill in and create medical fields or upload a pdf, which then fills in for a sql table on a database. Patients are also able to search for and share documents with doctors that have public accounts on the site, and patients have the ability to create emergency keys, in the form of qr codes, to enable doctors to access their records without sharing access. + +The web app contains an AI tool which helps provide a quick summary of a patient’s medical history for doctors who need quick access to specific information. + +Doctors have the ability to edit records which are shared with them, and can access new records using an emergency key. + +Both types of accounts can set their privacy status to public or private, to enable or disable new accounts from sharing/accessing documents with them. Accounts are created using an email, which is sent a verification email, and can be deleted. diff --git a/Requirements Specifications.pdf b/Requirements Specifications.pdf new file mode 100644 index 000000000..823423f7e Binary files /dev/null and b/Requirements Specifications.pdf differ diff --git a/Sprint 1/Flask Backend/.DS_Store b/Sprint 1/Flask Backend/.DS_Store new file mode 100644 index 000000000..387c0a35d Binary files /dev/null and b/Sprint 1/Flask Backend/.DS_Store differ diff --git a/Sprint 1/Flask Backend/.env b/Sprint 1/Flask Backend/.env new file mode 100644 index 000000000..727096334 --- /dev/null +++ b/Sprint 1/Flask Backend/.env @@ -0,0 +1 @@ +CONFIG_KEY="W<0:2e?Qr9)cA{:3|~T*5%ZxS" \ No newline at end of file diff --git a/Sprint 1/Flask Backend/AssignmentRepo355 b/Sprint 1/Flask Backend/AssignmentRepo355 new file mode 160000 index 000000000..d4c328b20 --- /dev/null +++ b/Sprint 1/Flask Backend/AssignmentRepo355 @@ -0,0 +1 @@ +Subproject commit d4c328b207cb456c03b281dae9f6b2f9c36dfc1f diff --git a/Sprint 1/Flask Backend/app.py b/Sprint 1/Flask Backend/app.py new file mode 100644 index 000000000..3bd6363d6 --- /dev/null +++ b/Sprint 1/Flask Backend/app.py @@ -0,0 +1,178 @@ +from flask import Flask, render_template, request, redirect, url_for, jsonify +from flask_bcrypt import Bcrypt +from wtforms import StringField, PasswordField, SubmitField, RadioField, validators +from flask_wtf import FlaskForm, CSRFProtect +from wtforms.validators import EqualTo, InputRequired +import jinja2 +import mysql +from pyModules.sqlpy.connectToDB import connectDatabase +from pyModules.sqlpy.createAccountRow import addNewUser +from pyModules.sqlpy.fetchData import fetchUserData +from pyModules.sqlpy.saveData import saveUserData +from pyModules.sqlpy.fetchAccessMap import fetchAccessMap +import os +from dotenv import load_dotenv + +app = Flask(__name__) +load_dotenv() +app.config["SECRET_KEY"] = os.getenv("CONFIG_KEY") +csrf = CSRFProtect(app) +bcrypt = Bcrypt(app) + +@app.route("/") +def home(): + return render_template("home.html") + +@app.route("/signup_page", methods = ['GET', 'POST']) +def signup(): + patient_or_provider = None + first_name = None + last_name = None + phone_number = None + user_email = None + password = None + + sign_up_form = SignUp() + if request.method == "POST": + if sign_up_form.validate_on_submit(): + patient_or_provider = sign_up_form.patient_or_provider.data + first_name = sign_up_form.first_name.data + last_name = sign_up_form.last_name.data + phone_number = sign_up_form.phone_number.data + user_email = sign_up_form.user_email.data + password = sign_up_form.password.data + hashed_password = bcrypt.generate_password_hash(sign_up_form.password.data).decode('utf-8') + password = hashed_password + + + addNewUser(first_name, + last_name, + user_email, + phone_number, + password, + patient_or_provider, + connectDatabase() + ) + + return render_template("signup_page.html", patient_or_provider = patient_or_provider, first_name = first_name, last_name = last_name, + user_email = user_email, phone_number = phone_number, password = password, + form = sign_up_form) + +@app.route("/login_page", methods = ['GET', 'POST']) +def login(): + email_login = None + password_login = None + + login_form = LogIn() + if request.method == "POST": + if login_form.validate_on_submit(): + email_login = login_form.email_login.data + password_login = login_form.password_login.data + + #TODO: We need to validate the email and password against the database here + #If validated and the user is a doctor, redirect to doctor page + #If validated and the user is a patient, redirect to patient page + #Jo work your magic + + return redirect(url_for("provider", email=email_login)) + + return render_template("login_page.html", email_login = email_login, password_login = password_login, + form = login_form) + +@app.route("/edit_record_page") +def edit_record_page(): + return render_template("edit_record_page.html") + +# Get patient data for edit_record_page +@app.route("/api/get_patient") +def api_get_patient(): + email = request.args.get("email") + + db = connectDatabase() + result = fetchUserData(email, db) + + if result: + return jsonify(result) + + return jsonify({"error": "Could not fetch user"}), 400 + +# Save updated patient data from edit_record_page +@app.route("/api/update_record", methods=["POST"]) +def api_update_record(): + print("test") + + data = request.get_json() + updated_fields = data.get("updated_fields") + email = data.get("email") + + for column, value in updated_fields.items(): + saveUserData(userEmail=email, + dbConnection=connectDatabase(), + columnToSet=column, + valueToSet=value) + + return jsonify({"message": "Record saved"}) + +@app.route('/provider_portal') +def provider(): + doctor_email = request.args.get("email") + return render_template('provider_homepage.html', doctor_email=doctor_email) + +# Fetch patients or doctors from accessMap +@app.route("/api/fetch_access_map") +def api_fetch_access_map(): + user_email = request.args.get("email") + user_type = request.args.get("type", "doctor").lower() # default to doctor + + # Validate type + if user_type not in ["doctor", "patient"]: + return jsonify({"error": "Invalid type. Use 'doctor' or 'patient'."}), 400 + + db = connectDatabase() + + try: + result = fetchAccessMap(user_email, db, user_type) + + # Expecting a list of tuples: [("First","Last","email"), ...] + return jsonify(result) + + except Exception as e: + print("ERROR:", e) + return jsonify({"error": "Failed to fetch access map"}), 500 + +class SignUp(FlaskForm): + patient_or_provider = RadioField("Are you a patient or a provider?", choices=[('patient', 'Patient'), ('provider', 'Provider')], validators=[InputRequired()]) + + first_name = StringField("Enter your first name", validators = [validators.DataRequired(message = "First name is required")], render_kw = {'placeholder': "John"}) + + last_name = StringField("Enter your last name", validators = [validators.DataRequired(message = "Last name is required")], render_kw = {'placeholder': "Doe"}) + + phone_number = StringField("Phone number", validators = [validators.DataRequired(message = "Phone number is required"), + validators.Length(min = 10, message = "Must be at least 10 characters"), + validators.Regexp(r'^\(\d{3}\)\s?\d{3}-\d{4}$', + message="Invalid phone number format. Use (123) 456-7890") + ], render_kw = {'placeholder': "(000) 000-0000"}) + user_email = StringField("Email", validators = [validators.DataRequired(), validators.Email("Must be a valid email")], render_kw = {'placeholder': "example@email.com"}) + + password = PasswordField("Create a unique password.", validators = [validators.DataRequired(message = "Please enter a password"), + validators.Length(min = 8, max = 20, message = "Please enter a password between 8 and 20 characters"), + validators.Regexp(r'^(?=.*[A-Z])(?=.*[!@#$%^+=-])(?=.{8,20}$)[^{}[\]<|*&"()]*$', message = "Please enter a valid password. Valid special characters are @, #, !, $, -, or _"), + EqualTo('confirm_password', message="Passwords must match")], + render_kw = {'placeholder': "8-20 characters long, one capital letter, one special character"}) + + confirm_password = PasswordField("Confirm your password", validators = [validators.DataRequired(message="Please confirm your password") + ], render_kw={'placeholder': "Re-enter your password"}) + + submit_button = SubmitField("Submit") + +class LogIn(FlaskForm): + email_login = StringField("Email", validators=[validators.DataRequired(message="Please enter your email"),validators.Email("Must be a valid email")], + render_kw={'placeholder': "Enter your email"}) + password_login = PasswordField("Password.", validators = [validators.DataRequired(message = "Please enter your password"), + validators.Length(min = 8, max = 20, message = "Must be between 8 and 20 characters"), + validators.Regexp(r'^(?=.*[A-Z])(?=.*[!@#$%^+=-])(?=.{8,20}$)[^{}[\]<|*&"()]*$', message = "Invalid format.")], render_kw={'placeholder': "Enter your password"}) + + submit_button = SubmitField("Submit") + +if __name__ == "__main__": + app.run(debug = True) \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/__init__.py b/Sprint 1/Flask Backend/pyModules/__init__.py new file mode 100644 index 000000000..2998d420d --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/__init__.py @@ -0,0 +1 @@ +__all__ = ["sqlpy"] \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/__pycache__/__init__.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 000000000..89a288e5a Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/__pycache__/__init__.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__init__.py b/Sprint 1/Flask Backend/pyModules/sqlpy/__init__.py new file mode 100644 index 000000000..1c725632a --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/__init__.py @@ -0,0 +1 @@ +__all__ = ["connectToDB" "createAccountRow"] \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/__init__.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 000000000..59dd14f56 Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/__init__.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/connectToDB.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/connectToDB.cpython-313.pyc new file mode 100644 index 000000000..b71298665 Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/connectToDB.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/createAccountRow.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/createAccountRow.cpython-313.pyc new file mode 100644 index 000000000..64e991eae Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/createAccountRow.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchAccessMap.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchAccessMap.cpython-313.pyc new file mode 100644 index 000000000..ae3d3ee74 Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchAccessMap.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchData.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchData.cpython-313.pyc new file mode 100644 index 000000000..08c3f8fd1 Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/fetchData.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/saveData.cpython-313.pyc b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/saveData.cpython-313.pyc new file mode 100644 index 000000000..d024a9d4f Binary files /dev/null and b/Sprint 1/Flask Backend/pyModules/sqlpy/__pycache__/saveData.cpython-313.pyc differ diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/connectToDB.py b/Sprint 1/Flask Backend/pyModules/sqlpy/connectToDB.py new file mode 100644 index 000000000..d71ffdef2 --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/connectToDB.py @@ -0,0 +1,25 @@ +from mysql import connector + + +def connectDatabase(user = "root", + password = "r39Cfz1BE%n&al", + host = "127.0.0.1", + database = "medicalrecords", + raiseOnWarnings = True + ): + + connectionConfig = { + 'user': user, + 'password': password, + 'host': host, + 'database': database, + 'raise_on_warnings': raiseOnWarnings + } + + dbConnection = connector.connect(**connectionConfig) + + return dbConnection + + +def disconnectDatabase(connectionToClose): + connectionToClose.close() \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/createAccountRow.py b/Sprint 1/Flask Backend/pyModules/sqlpy/createAccountRow.py new file mode 100644 index 000000000..469a5e099 --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/createAccountRow.py @@ -0,0 +1,62 @@ +from mysql import connector +from mysql.connector import errorcode +import mysql + + +def addNewUser( firstName, + lastName, + userEmail, + phoneNumber, + password, + PatientOrProvider, + dbConnection + ): + + returnVal = 1 + + addUser = ("INSERT INTO users" + "(Email, First_Name, Last_Name, PasswordHash, Phone_Number, PatientOrProvider)" + "VALUES ((%(EmailVal)s)," \ + "(%(First_NameVal)s)," \ + "(%(Last_NameVal)s)," \ + "(%(PasswordVal)s)," \ + "(%(Phone_NumberVal)s)," \ + "(%(PatientOrProviderVal)s))" + ) + + + + + + dataUser = { + 'EmailVal': userEmail, + 'First_NameVal': firstName, + 'Last_NameVal': lastName, + 'PasswordVal': password, + 'Phone_NumberVal': phoneNumber, + 'PatientOrProviderVal': PatientOrProvider + } + + + cursor = dbConnection.cursor() + + try: + cursor.execute(addUser, dataUser) + except mysql.connector.Error as err: + if err.errno == 1062: + returnVal = 2 + + try: + dbConnection.commit() + except mysql.connector.Error as err: + if err.errno == 1062: + returnVal = 3 + + cursor.close() # close cursor + dbConnection.close() # close connection + + return returnVal + + +def addNewUserTest(): + print("import successful! :)") \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/fetchAccessMap.py b/Sprint 1/Flask Backend/pyModules/sqlpy/fetchAccessMap.py new file mode 100644 index 000000000..aba2c12d9 --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/fetchAccessMap.py @@ -0,0 +1,45 @@ +from mysql import connector + +def fetchAccessMap(userEmail, + dbConnection, + patientOrDoctor # "doctor" or "patient" + ): + + cursor = dbConnection.cursor() + + if patientOrDoctor == "doctor": + query = ("SELECT u.First_Name, u.Last_Name, u.Email FROM users u " + "JOIN accessMap AM " + "ON AM.patient = u.Email " + "WHERE AM.doctor = %(EmailVal)s" + ) + else: + query = ("SELECT u.First_Name, u.Last_Name, u.Email FROM users u " + "JOIN accessMap AM " + "ON AM.doctor = u.Email " + "WHERE AM.patient = %(EmailVal)s" + ) + + + queryData = { + 'EmailVal': userEmail + } # user primary key + + try: + cursor.execute(query, queryData) # execute the changes + except: + return 2 #errorval + + + returnList = [] + for (First_Name, Last_Name, Email) in cursor: + # iterate through a set of tuples + # the tuples are the rows + returnList.append((First_Name, Last_Name, Email)) + + + cursor.close() # close cursor + dbConnection.close() # close connection + + return returnList #return the map + diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/fetchData.py b/Sprint 1/Flask Backend/pyModules/sqlpy/fetchData.py new file mode 100644 index 000000000..bedc7984b --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/fetchData.py @@ -0,0 +1,48 @@ +from mysql import connector + +def fetchUserData(userEmail, + dbConnection + ): + + cursor = dbConnection.cursor() + + query = ("SELECT First_Name, Last_Name, Age, Phone_Number, Sex, Weight, Height, Medications, Allergies, Active_Problems, " + "Medical_History, Family_History, Date_Updated FROM users " + "WHERE Email = %(emailVal)s") # fetch user data + + queryData = { + 'emailVal': userEmail + } # user primary key + + try: + cursor.execute(query, queryData) # execute the changes + except: + return 2 #errorval + + returnMap = {} + for (First_Name, Last_Name, Age, Phone_Number, Sex, Weight, Height, Medications, Allergies, Active_Problems, Medical_History, + Family_History, Date_Updated) in cursor: + # iterate through a set of tuples + # the tuples are the rows, but we only fetched one + returnMap = { + 'First_Name': First_Name, + 'Last_Name': Last_Name, + 'Age': Age, + 'Phone_Number': Phone_Number, + 'Sex' : Sex, + 'Weight': Weight, + 'Height': Height, + 'Medications': Medications, + 'Allergies': Allergies, + 'Active_Problems': Active_Problems, + 'Medical_History': Medical_History, + 'Family_History': Family_History, + 'Date_Updated': Date_Updated + } # make a return map for the row + + + cursor.close() # close cursor + dbConnection.close() # close connection + + # Updated to stop it from sorting alphabetically + return [{"field": k, "value": v} for k, v in returnMap.items()] \ No newline at end of file diff --git a/Sprint 1/Flask Backend/pyModules/sqlpy/saveData.py b/Sprint 1/Flask Backend/pyModules/sqlpy/saveData.py new file mode 100644 index 000000000..a7cdd2591 --- /dev/null +++ b/Sprint 1/Flask Backend/pyModules/sqlpy/saveData.py @@ -0,0 +1,48 @@ +from mysql import connector + +def saveUserData(userEmail, + dbConnection, + columnToSet, + valueToSet + ): + + + # 1. WHITELIST columns (CRITICALLY IMPORTANT) + allowed_columns = {"First_Name", "Last_Name", "Phone_Number", "Age", "Address", "PasswordHash", "Sex", "Weight", "Height", + "Medications", "Allergies", "Active_Problems", "Medical_History", "Family_History", "Date_Updated"} + if columnToSet not in allowed_columns: + raise ValueError(f"Invalid or unsafe column name: {columnToSet}") + + + cursor = dbConnection.cursor() # make cursor + + query = ("UPDATE users " + f"SET {columnToSet} = %(changeVal)s " + "WHERE Email = %(emailVal)s") # sql update stmt + + queryData = { + 'changeVal': valueToSet, + 'emailVal': userEmail + } # sql updata vals + + try: + cursor.execute(query, queryData) # execute stmt + except: + return 2 + + if cursor.rowcount == 0: + print("no rows") + return {"status": 4, "error": "No user with that email"} + + + try: + dbConnection.commit() + except: + return 3 + + cursor.close() # close cursor + dbConnection.close() # close connection + + + return 1 + diff --git a/Sprint 1/Flask Backend/templates/edit_record_page.html b/Sprint 1/Flask Backend/templates/edit_record_page.html new file mode 100644 index 000000000..7a018baf0 --- /dev/null +++ b/Sprint 1/Flask Backend/templates/edit_record_page.html @@ -0,0 +1,195 @@ + + + + + + + + + + +
+ + + + +
+ + +
+
+ Loading patient record... +
+
+ + + + + \ No newline at end of file diff --git a/Sprint 1/Flask Backend/templates/home.html b/Sprint 1/Flask Backend/templates/home.html new file mode 100644 index 000000000..1506c5684 --- /dev/null +++ b/Sprint 1/Flask Backend/templates/home.html @@ -0,0 +1,17 @@ + + + + + Penguin Care + + +
+

Welcome to Penguin Care

+
+
+ +
+     Sign Up +     Login + + \ No newline at end of file diff --git a/Sprint 1/Flask Backend/templates/login_page.html b/Sprint 1/Flask Backend/templates/login_page.html new file mode 100644 index 000000000..10b41897d --- /dev/null +++ b/Sprint 1/Flask Backend/templates/login_page.html @@ -0,0 +1,39 @@ + + + + + Login + + +

Welcome back

+
+ {{ form.hidden_tag() }} +
+ {{ form.email_login.label }} {{ form.email_login(size=32) }} + {% for error in form.email_login.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.password_login.label }} {{ form.password_login(size=32) }} + {% for error in form.password_login.errors %} + {{ error }} + {% endfor %} +
+
+
+ {{ form.submit_button() }} +
+ +
+

Don't have an account?

+ Sign Up +
+
+
+ + + + + \ No newline at end of file diff --git a/Sprint 1/Flask Backend/templates/patient_homepage.html b/Sprint 1/Flask Backend/templates/patient_homepage.html new file mode 100644 index 000000000..f8290610a --- /dev/null +++ b/Sprint 1/Flask Backend/templates/patient_homepage.html @@ -0,0 +1,24 @@ + + + + + Patient Homepage + + + + +
+

Welcome To Your Homepage!

+ +
+ Logout +
+
+ Edit + Send Access + Profile Settings +
+
+ + + diff --git a/Sprint 1/Flask Backend/templates/provider_homepage.html b/Sprint 1/Flask Backend/templates/provider_homepage.html new file mode 100644 index 000000000..0c0bfa6f7 --- /dev/null +++ b/Sprint 1/Flask Backend/templates/provider_homepage.html @@ -0,0 +1,181 @@ + + + + + Provider Portal + + + +

Provider Portal

+ +
+ + + +
+ + + + + +
+ + + +
+ +
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/Sprint 1/Flask Backend/templates/signup_page.html b/Sprint 1/Flask Backend/templates/signup_page.html new file mode 100644 index 000000000..37953f983 --- /dev/null +++ b/Sprint 1/Flask Backend/templates/signup_page.html @@ -0,0 +1,78 @@ + + + + + Signup + + +

Enter your information below

+
+ {{ form.hidden_tag() }} +
+ {{ form.patient_or_provider.label }} + {% for subfield in form.patient_or_provider %} +
+ + {{ subfield }}{{ subfield.label }} + +
+ {% endfor %} +
+
+ {{ form.first_name.label }} {{ form.first_name(size=32) }} + {% for error in form.first_name.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.last_name.label }} {{ form.last_name(size=32) }} + {% for error in form.last_name.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.phone_number.label }} {{ form.phone_number(size=32) }} + {% for error in form.phone_number.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.user_email.label }} {{ form.user_email(size=32) }} + {% for error in form.user_email.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.password.label }} {{ form.password(size=32) }} + {% for error in form.password.errors %} + {{ error }} + {% endfor %} +
+ +
+ {{ form.confirm_password.label }} {{ form.confirm_password(size=32) }} + {% for error in form.confirm_password.errors %} + {{ error }} + {% endfor %} +
+
+
+ {{ form.submit_button() }} +
+
+

Already have an account?

+ Login +
+
+
+
+ + + +
+ + \ No newline at end of file diff --git a/Sprint 1/codePlaceHolder.txt b/Sprint 1/codePlaceHolder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/Sprint 1/demoForSprint1.mkv b/Sprint 1/demoForSprint1.mkv new file mode 100644 index 000000000..d3f5a12fa --- /dev/null +++ b/Sprint 1/demoForSprint1.mkv @@ -0,0 +1 @@ + diff --git a/Sprint 1/sqlCode/addNewUserFields.sql b/Sprint 1/sqlCode/addNewUserFields.sql new file mode 100644 index 000000000..892ab6a8f --- /dev/null +++ b/Sprint 1/sqlCode/addNewUserFields.sql @@ -0,0 +1,11 @@ +ALTER TABLE users ADD COLUMN Sex VARCHAR(20); +ALTER TABLE users ADD COLUMN Weight VARCHAR(20); +ALTER TABLE users ADD COLUMN Height VARCHAR(20); + +ALTER TABLE users ADD COLUMN Medications TEXT; +ALTER TABLE users ADD COLUMN Allergies TEXT; +ALTER TABLE users ADD COLUMN Active_Problems TEXT; +ALTER TABLE users ADD COLUMN Medical_History TEXT; +ALTER TABLE users ADD COLUMN Family_History TEXT; + +ALTER TABLE users ADD COLUMN Date_Updated DATETIME; \ No newline at end of file diff --git a/Sprint 1/sqlCode/createAccessTable.sql b/Sprint 1/sqlCode/createAccessTable.sql new file mode 100644 index 000000000..e1343d98f --- /dev/null +++ b/Sprint 1/sqlCode/createAccessTable.sql @@ -0,0 +1,7 @@ +CREATE TABLE accessMap ( + connectionUID VARCHAR(50) PRIMARY KEY, + + doctor VARCHAR(50) NOT NULL, + + patient VARCHAR(50) NOT NULL +) \ No newline at end of file diff --git a/Sprint 1/sqlCode/createDB.sql b/Sprint 1/sqlCode/createDB.sql new file mode 100644 index 000000000..4750c1869 --- /dev/null +++ b/Sprint 1/sqlCode/createDB.sql @@ -0,0 +1 @@ +CREATE DATABASE MedicalRecords; \ No newline at end of file diff --git a/Sprint 1/sqlCode/createUsersTable.sql b/Sprint 1/sqlCode/createUsersTable.sql new file mode 100644 index 000000000..a15129458 --- /dev/null +++ b/Sprint 1/sqlCode/createUsersTable.sql @@ -0,0 +1,29 @@ +CREATE TABLE users ( + Email VARCHAR(50) PRIMARY KEY, + IsRegistered BOOLEAN, + + First_Name VARCHAR(50), + MiddleNameOrInitial VARCHAR(50), + Last_Name VARCHAR(50), + + PasswordHash VARCHAR(100) NOT NULL, + + Age INT, + + Phone_Number VARCHAR(15), + + PatientOrProvider VARCHAR(8), + + Sex VARCHAR(20), + + Weight VARCHAR(20), + Height VARCHAR(20), + + Medications TEXT, + Allergies TEXT, + Active_Problems TEXT, + Medical_History TEXT, + Family_History TEXT, + + Date_Updated DATETIME +) \ No newline at end of file diff --git a/Sprint 1/sqlCode/fillDbForTests.sql b/Sprint 1/sqlCode/fillDbForTests.sql new file mode 100644 index 000000000..a5f021671 --- /dev/null +++ b/Sprint 1/sqlCode/fillDbForTests.sql @@ -0,0 +1,13 @@ +INSERT INTO users (Email, First_Name, Last_Name, PatientOrProvider, PasswordHash) + +VALUES ('bob@bob.com', 'Bobf', 'Bobl', 'patient', '$2b$12$Vam60GL5xHEvpq/6EfMINOByfVe/7.tBwWBY/jhIPgroXT28yxUmO'), + ('sally@sally.com', 'Sallyf', 'Sallyl', 'provider', '$2b$12$Vam60GL5xHEvpq/6EfMINOByfVe/7.tBwWBY/jhIPgroXT28yxUmO'), + ('jimothy@jimothy.com', 'jimothyf', 'jimmothyl', 'patient', '$2b$12$Vam60GL5xHEvpq/6EfMINOByfVe/7.tBwWBY/jhIPgroXT28yxUmO'); + + + +INSERT INTO accessMap (connectionUID, doctor, patient) + +VALUES ('1', 'sally@sally.com','bob@bob.com'), + ('2','sally@sally.com','jimothy@jimothy.com'); + diff --git a/Sprint 1/testCases.pdf b/Sprint 1/testCases.pdf new file mode 100644 index 000000000..1e337cbff Binary files /dev/null and b/Sprint 1/testCases.pdf differ diff --git a/Sprint 1/videoPlaceholder.txt b/Sprint 1/videoPlaceholder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/Sprint 2/Sprint 2 Test Cases.pdf b/Sprint 2/Sprint 2 Test Cases.pdf new file mode 100644 index 000000000..9c57422f7 Binary files /dev/null and b/Sprint 2/Sprint 2 Test Cases.pdf differ diff --git a/Sprint 2/Sprint2TestCases.mkv b/Sprint 2/Sprint2TestCases.mkv new file mode 100644 index 000000000..91bd17aa1 Binary files /dev/null and b/Sprint 2/Sprint2TestCases.mkv differ diff --git a/Sprint 2/TestCasesPlaceholder.txt b/Sprint 2/TestCasesPlaceholder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/Sprint 2/codePlaceHolder.txt b/Sprint 2/codePlaceHolder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/Sprint 2/videoPlaceholder.txt b/Sprint 2/videoPlaceholder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/documentation/planning.txt b/documentation/planning.txt new file mode 100644 index 000000000..c7ae27009 --- /dev/null +++ b/documentation/planning.txt @@ -0,0 +1,4 @@ +Web framework: flask +Database: SQLite/PostgreSQL +RestApi: flask-restful/restx/other +Python sql toolkit: SQLAlchemy diff --git a/documentation/process diagrams/coding and testing process.pdf b/documentation/process diagrams/coding and testing process.pdf new file mode 100644 index 000000000..2cdd9cddb Binary files /dev/null and b/documentation/process diagrams/coding and testing process.pdf differ diff --git a/documentation/process diagrams/deployment process.pdf b/documentation/process diagrams/deployment process.pdf new file mode 100644 index 000000000..252f40706 Binary files /dev/null and b/documentation/process diagrams/deployment process.pdf differ diff --git a/documentation/process diagrams/requirements design process.pdf b/documentation/process diagrams/requirements design process.pdf new file mode 100644 index 000000000..7f9d20134 Binary files /dev/null and b/documentation/process diagrams/requirements design process.pdf differ diff --git a/documentation/process diagrams/software design process.pdf b/documentation/process diagrams/software design process.pdf new file mode 100644 index 000000000..7ed00d7a9 Binary files /dev/null and b/documentation/process diagrams/software design process.pdf differ diff --git a/Sprint 1/TestCasesPlaceholder.txt b/documentation/requirements specifications/requirements.txt similarity index 100% rename from Sprint 1/TestCasesPlaceholder.txt rename to documentation/requirements specifications/requirements.txt diff --git a/documentation/requirements specifications/requirementsSpecificationRawPDF.pdf b/documentation/requirements specifications/requirementsSpecificationRawPDF.pdf new file mode 100644 index 000000000..b7785aaaa Binary files /dev/null and b/documentation/requirements specifications/requirementsSpecificationRawPDF.pdf differ