Skip to content
This repository has been archived by the owner on Jan 17, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into signout
Browse files Browse the repository at this point in the history
  • Loading branch information
mfdavies authored Jan 7, 2024
2 parents c6cf32b + 6fb01be commit c9dc71e
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 296 deletions.
32 changes: 15 additions & 17 deletions backend/server/src/constants.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
# Patient login email content
EMAIL_SUBJECT = "MobilityMate Account Access"
EMAIL_BODY_TEMPLATE = """Dear {name},
Welcome to MobilityMate - your dedicated partner in staying active and healthy! 🌟
To access your personalized exercise account and stay up to date with exercises recommended by your practitioner, simply click on the link below:
🔗 www.example.com/patient/{uid}
Your well-being is our priority! If you have any questions or need support, our team is here to assist you every step of the way.
Stay active, stay healthy!
Best regards,
The MobilityMate Team 🏋️‍♂️
"""
# Patient login email content
EMAIL_SUBJECT = "MobilityMate Account Access"
EMAIL_BODY_TEMPLATE = """Dear {name},
Welcome to MobilityMate - your dedicated partner in staying active and healthy! 🌟
To access your personalized exercise account and stay up to date with exercises recommended by your practitioner, simply click on the link below:
🔗 https://mobilitymate-a8b53.web.app/{practitionId}/patient/{patientId}
Your well-being is our priority! If you have any questions or need support, our team is here to assist you every step of the way.
Stay active, stay healthy!
The MobilityMate Team 🏋️‍♂️
"""
22 changes: 22 additions & 0 deletions backend/server/src/conversation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ def send_message():
reply = conversation.generate_reply(message)
return jsonify({"reply": reply}), 200

@conversation_blueprint.route("/send_text_message", methods=["POST"])
def send_text_message():
practitioner = request.args.get("practitioner")
patient = request.args.get("patient")
user_doc_ref = (
users_ref.document(practitioner).collection("patients").document(patient)
)
if "conversation_id" not in session:
return jsonify({"reply": "Please start a conversation first"}), 400

conversation_id = session["conversation_id"]
conversation = Conversation(
user_doc_ref=user_doc_ref, conversaton_id=conversation_id
)

# Store audio in a temp file
message = request.json.get("message")

# Generate a reply using the Conversation object
reply = conversation.generate_reply(message)
return jsonify({"reply": reply}), 200

@conversation_blueprint.route('/end', methods=['POST'])
def end():
if 'conversation_id' not in session:
Expand Down
87 changes: 1 addition & 86 deletions backend/server/src/main.py
Original file line number Diff line number Diff line change
@@ -1,86 +1 @@
from flask import Flask, request, jsonify, render_template
from flask_mail import Mail, Message
from dotenv import load_dotenv
import os
from constants import *
import time
import firebase_admin
from firebase_admin import credentials
from flask_cors import CORS

load_dotenv()
app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY")

def create_service_dict():
variables_keys = {
"type": os.getenv("TYPE"),
"project_id": os.getenv("PROJECT_ID"),
"private_key_id": os.getenv("PRIVATE_KEY_ID"),
"private_key": os.getenv("PRIVATE_KEY"),
"client_email": os.getenv("CLIENT_EMAIL"),
"client_id": os.getenv("CLIENT_ID"),
"auth_uri": os.getenv("AUTH_URI"),
"token_uri": os.getenv("TOKEN_URI"),
"auth_provider_x509_cert_url": os.getenv("AUTH_PROVIDER_X509_CERT_URL"),
"client_x509_cert_url": os.getenv("CLIENT_X509_CERT_URL"),
"universe_domain": os.getenv("UNIVERSE_DOMAIN")
}
return variables_keys

cred = credentials.Certificate(create_service_dict())
firebase_admin.initialize_app(cred)

from conversation.views import conversation_blueprint
from exercise.views import exercise_blueprint

# Register the conversation Blueprint
app.register_blueprint(conversation_blueprint, url_prefix="/conversation")
app.register_blueprint(exercise_blueprint, url_prefix="/exercise")

CORS(app)

# Load Flask-Mail config from .env
app.config["MAIL_SERVER"] = os.getenv("MAIL_SERVER")
app.config["MAIL_PORT"] = int(os.getenv("MAIL_PORT"))
app.config["MAIL_USE_TLS"] = os.getenv("MAIL_USE_TLS").lower() == "true"
app.config["MAIL_USERNAME"] = os.getenv("MAIL_USERNAME")
app.config["MAIL_PASSWORD"] = os.getenv("MAIL_PASSWORD")
app.config["MAIL_DEFAULT_SENDER"] = os.getenv("MAIL_DEFAULT_SENDER")
mail = Mail(app)

@app.route("/patient/send-link", methods=["POST"])
def send_link():
try:
data = request.get_json()
uid = data.get("uid")
name = data.get("name")
email = data.get("email")

# Send patient email with login link
message = Message(
subject=EMAIL_SUBJECT,
recipients=[email],
body=EMAIL_BODY_TEMPLATE.format(name=name, uid=uid),
)
mail.send(message)

return jsonify({"success": True, "message": "Email sent successfully"})

except Exception as e:
return jsonify({"success": False, "error": str(e)})


def format_server_time():
server_time = time.localtime()
return time.strftime("%I:%M:%S %p", server_time)


@app.route("/")
def index():
context = {"server_time": format_server_time()}
return render_template("index.html", context=context)


if __name__ == '__main__':
app.run(debug=True, port=os.getenv("PORT", default=5000))

3 changes: 3 additions & 0 deletions backend/server/src/nixpacks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[phases.setup]
nixPkgs = ["...", "GLIBC-2.36"]
aptPkgs = ["...", "ffmpeg"]
2 changes: 2 additions & 0 deletions frontend/firebaseConfig.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import firebase from "firebase/compat/app";
import "firebase/compat/firestore";
import "firebase/compat/auth";
import "firebase/compat/storage";

const firebaseConfig = {
apiKey: "AIzaSyCdjuKe_DkpM9PT71WMfl7l3SVpCeZfD5c",
Expand All @@ -19,6 +20,7 @@ if (!firebase.apps.length) {

export const db = firebase.firestore();
export const auth = firebase.auth();
export const storage = firebase.storage();
export async function getCurrentUser() {
return new Promise((resolve, reject) => {
if (auth.currentUser) {
Expand Down
55 changes: 29 additions & 26 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";

import Landing from "./views/landing/Landing";
import PatientHome from "./views/patient/PatientHome";
import PractitionerDashboard from "./views/practitioner/PractitionerDashboard";
import PractitionerSignUp from "./views/practitioner/PractitionerSignUp";
import PractitionerLogin from "./views/practitioner/PractitionerLogin";

const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/patient/:patientID" element={<PatientHome />} />
<Route
path="/practitioner/dashboard"
element={<PractitionerDashboard />}
/>
<Route path="/practitioner/signUp" element={<PractitionerSignUp />} />
<Route path="/practitioner/login" element={<PractitionerLogin />} />
</Routes>
</BrowserRouter>
);
};

export default App;
import { BrowserRouter, Routes, Route } from "react-router-dom";

import Landing from "./views/landing/Landing";
import PatientHome from "./views/patient/PatientHome";
import PractitionerDashboard from "./views/practitioner/PractitionerDashboard";
import PractitionerSignUp from "./views/practitioner/PractitionerSignUp";
import PractitionerLogin from "./views/practitioner/PractitionerLogin";

const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route
path="/:practitionerID/patient/:patientID"
element={<PatientHome />}
/>
<Route
path="/practitioner/dashboard"
element={<PractitionerDashboard />}
/>
<Route path="/practitioner/signUp" element={<PractitionerSignUp />} />
<Route path="/practitioner/login" element={<PractitionerLogin />} />
</Routes>
</BrowserRouter>
);
};

export default App;
91 changes: 66 additions & 25 deletions frontend/src/views/patient/PatientHome.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,51 @@
import Navbar from './components/Navbar';
import Exercises from './components/Exercises';
import './styles.css';
import { useState, useEffect, useCallback } from 'react';
import VoiceAI from './components/VoiceAI';
import axios from 'axios';
import Skeleton from './components/Skeleton';
import { LogOut } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import Navbar from "./components/Navbar";
import Exercises from "./components/Exercises";
import "./styles.css";
import { useState, useEffect, useCallback } from "react";
import VoiceAI from "./components/VoiceAI";
import axios from "axios";
import Skeleton from "./components/Skeleton";
import apiUrl from "../../config";
import { LogOut } from "lucide-react";
import { useNavigate } from "react-router-dom";

const PatientHome = () => {
const navigate = useNavigate();
const [convo, setConvo] = useState({
user: null,
gpt: null,
});
const [userInput, setUserInput] = useState("");

const handleInputChange = (e) => {
setUserInput(e.target.value);
};

const handleFormSubmit = async (e) => {
e.preventDefault();
setConvo((prevConvo) => ({ ...prevConvo, gpt: null }));
updateUserMessage(userInput);
const queryParams = new URLSearchParams({
patient: "demo",
practitioner: "demo",
});
try {
const response = await axios.post(
`${apiUrl}/conversation/send_text_message?${queryParams.toString()}`,
{ message: userInput }
);
console.log(response.data.reply);
setConvo((prevConvo) => {
if (prevConvo.gpt === null) {
return { ...prevConvo, gpt: response.data.reply };
}
return prevConvo;
});
} catch (error) {
console.error("Error fetching conversation start:", error);
}
setUserInput("");
};

const updateUserMessage = useCallback((newMessage) => {
setConvo((prevConvo) => ({ ...prevConvo, user: newMessage }));
Expand All @@ -26,8 +58,8 @@ const PatientHome = () => {
useEffect(() => {
const startConversation = async () => {
const queryParams = new URLSearchParams({
patient: 'demo',
practitioner: 'demo',
patient: "demo",
practitioner: "demo",
});
try {
const response = await axios.get(
Expand All @@ -40,24 +72,28 @@ const PatientHome = () => {
return prevConvo;
});
} catch (error) {
console.error('Error fetching conversation start:', error);
console.error("Error fetching conversation start:", error);
}
};
startConversation();
}, []);

const handleEndSession = async () => {
try {
await axios.post('http://localhost:8080/conversation/end', {}, {
// TODO: what are thooooose
params: new URLSearchParams({
patient: 'demo',
practitioner: 'demo',
})
});
navigate('/')
await axios.post(
`${apiUrl}/conversation/end`,
{},
{
// TODO: what are thooooose
params: new URLSearchParams({
patient: "demo",
practitioner: "demo",
}),
}
);
navigate("/");
} catch (error) {
console.error('Error ending conversation:', error);
console.error("Error ending conversation:", error);
}
};

Expand All @@ -71,11 +107,12 @@ const PatientHome = () => {
<header className="flex justify-between items-center">
<div>
<h1 className="text-4xl font-medium">Welcome Back</h1>
<div className="text-3xl">John</div>
<div className="text-2xl">John Doe</div>
</div>
<button
onClick={handleEndSession}
className='flex items-center gap-2 btn btn-active btn-glass'>
className="flex items-center gap-2 btn btn-active btn-glass"
>
Done for the day?
<LogOut size={18} />
</button>
Expand All @@ -87,16 +124,20 @@ const PatientHome = () => {
{convo.gpt !== null ? convo.gpt : <Skeleton />}
</div>
</div>
<form className="flex items-center">
<form className="flex items-center" onSubmit={handleFormSubmit}>
<input
type="text"
placeholder="You can type here..."
className="input input-bordered w-full max-w-xs mr-2"
value={userInput}
onChange={handleInputChange}
/>
<button className="btn btn-neutral">Prompt</button>
</form>
</div>
<div className="w-1/3 right-column border-l-[1px]">
<div className="border-l-[1px] -my-2"></div>
<div className="w-1/3 h-full flex flex-col gap-4 justify-center items-center">
<h3 className="text-lg ml-3">Exercises</h3>
<Exercises />
</div>
</div>
Expand Down
Loading

0 comments on commit c9dc71e

Please sign in to comment.