Skip to content

Commit

Permalink
Fixed broken tests + seed user with profile pics
Browse files Browse the repository at this point in the history
  • Loading branch information
neilshaabi committed Apr 30, 2024
1 parent 7211783 commit 297b760
Show file tree
Hide file tree
Showing 26 changed files with 172 additions and 253 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ app:
celery:
@echo "Starting celery worker..."
@echo "Note: requires connection with redis via redis-server"
celery -A app.utils.celery worker --loglevel INFO
celery -A make_celery worker --loglevel INFO

redis:
@echo "Starting redis server..."
Expand Down
16 changes: 6 additions & 10 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ def load_user(user_id: str) -> User:
).scalar_one_or_none()


def create_app(config: Config = CONFIGS[os.environ["ENV"]]):
def create_app(config: Config = None):
if not config:
config = CONFIGS[os.environ["ENV"]]

app = Flask(__name__)
app.config.from_object(config)

Expand Down Expand Up @@ -93,15 +96,8 @@ def handle_exception(e: Exception) -> Response:
)

# Register blueprints with endpoints
from app.views import (
appointment_types,
appointments,
auth,
clients,
main,
messages,
profile,
)
from app.views import (appointment_types, appointments, auth, clients,
main, messages, profile)
from app.views import stripe as stripe_bp
from app.views import therapists, users

Expand Down
4 changes: 3 additions & 1 deletion app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class DevConfig(Config):
WTF_CSRF_ENABLED: str = True
ERROR_HANDLER: bool = False
SQLALCHEMY_DATABASE_URI: str = "sqlite:///" + os.path.join(basedir, "mindli.sqlite")
SEED_FROM_EXTERNAL_API: bool = True
CELERY_ENABLED: bool = True
CELERY: dict = {
"broker_url": "redis://localhost",
Expand All @@ -50,6 +51,7 @@ class ProdConfig(Config):
WTF_CSRF_ENABLED: str = True
ERROR_HANDLER: bool = True
SQLALCHEMY_DATABASE_URI: str = os.environ["DATABASE_URL"]
SEED_FROM_EXTERNAL_API: bool = False


class TestConfig(Config):
Expand All @@ -59,7 +61,7 @@ class TestConfig(Config):
WTF_CSRF_ENABLED: str = False
ERROR_HANDLER: bool = False
SQLALCHEMY_DATABASE_URI: str = "sqlite://" # Use in-memory database

SEED_FROM_EXTERNAL_API: bool = False
CELERY_ENABLED: bool = False
CELERY: dict = {
"broker_url": "memory://",
Expand Down
1 change: 1 addition & 0 deletions app/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Email addresses used for example therapist and client accounts in development and testing.
EXAMPLE_THERAPIST_EMAIL = "[email protected]"
EXAMPLE_CLIENT_EMAIL = "[email protected]"
EXAMPLE_VALID_PASSWORD = "ValidPassword1"

COUNTRIES = [
"Afghanistan",
Expand Down
3 changes: 2 additions & 1 deletion app/forms/appointment_types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from wtforms import DecimalField, HiddenField, IntegerField, SelectField, SubmitField
from wtforms import (DecimalField, HiddenField, IntegerField, SelectField,
SubmitField)
from wtforms.validators import DataRequired, NumberRange

from app.constants import CURRENCIES
Expand Down
24 changes: 6 additions & 18 deletions app/forms/appointments.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
from wtforms import (
BooleanField,
DateField,
IntegerField,
SelectField,
StringField,
SubmitField,
TextAreaField,
TimeField,
)
from wtforms import (BooleanField, DateField, IntegerField, SelectField,
StringField, SubmitField, TextAreaField, TimeField)
from wtforms.validators import DataRequired, Length, NumberRange, Optional

from app.constants import CURRENCIES
from app.forms import CustomFlaskForm, CustomSelectField, CustomSelectMultipleField
from app.models.enums import (
AppointmentStatus,
PaymentStatus,
TherapyMode,
TherapyType,
UserRole,
)
from app.forms import (CustomFlaskForm, CustomSelectField,
CustomSelectMultipleField)
from app.models.enums import (AppointmentStatus, PaymentStatus, TherapyMode,
TherapyType, UserRole)
from app.models.intervention import Intervention
from app.models.issue import Issue

Expand Down
3 changes: 2 additions & 1 deletion app/forms/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from wtforms import HiddenField, PasswordField, RadioField, StringField, SubmitField
from wtforms import (HiddenField, PasswordField, RadioField, StringField,
SubmitField)
from wtforms.validators import DataRequired, Email, Length

from app.forms import CustomFlaskForm
Expand Down
14 changes: 6 additions & 8 deletions app/forms/clients.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
from wtforms import BooleanField, DateField, IntegerField, StringField, SubmitField
from wtforms import (BooleanField, DateField, IntegerField, StringField,
SubmitField)
from wtforms.validators import DataRequired, Length, NumberRange, Optional

from app.forms import CustomFlaskForm, CustomSelectField, CustomSelectMultipleField
from app.forms import (CustomFlaskForm, CustomSelectField,
CustomSelectMultipleField)
from app.models.enums import Gender, Occupation, ReferralSource
from app.models.issue import Issue
from app.utils.validators import (
DateBeforeToday,
NotWhitespace,
ValidName,
ValidPhoneNumber,
)
from app.utils.validators import (DateBeforeToday, NotWhitespace, ValidName,
ValidPhoneNumber)


class ClientProfileForm(CustomFlaskForm):
Expand Down
3 changes: 2 additions & 1 deletion app/forms/therapists.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from wtforms.validators import DataRequired, Length, NumberRange, Optional

from app.constants import COUNTRIES
from app.forms import CustomFlaskForm, CustomSelectField, CustomSelectMultipleField
from app.forms import (CustomFlaskForm, CustomSelectField,
CustomSelectMultipleField)
from app.models.enums import Gender, TherapyMode, TherapyType
from app.models.intervention import Intervention
from app.models.issue import Issue
Expand Down
12 changes: 3 additions & 9 deletions app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,9 @@ def seed(cls, db: SQLAlchemy, fake: Optional[Faker] = None) -> None:
from .appointment import Appointment
from .appointment_notes import AppointmentNotes
from .appointment_type import AppointmentType
from .associations import (
client_issue,
note_intervention,
note_issue,
therapist_intervention,
therapist_issue,
therapist_language,
therapist_title,
)
from .associations import (client_issue, note_intervention, note_issue,
therapist_intervention, therapist_issue,
therapist_language, therapist_title)
from .client import Client
from .conversation import Conversation
from .enums import Gender, TherapyMode, UserRole
Expand Down
156 changes: 90 additions & 66 deletions app/models/user.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import os
import random
from datetime import date
from typing import List, Optional

import requests
import sqlalchemy as sa
import sqlalchemy.orm as so
from faker import Faker
from flask import current_app
from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash

from app import db
from app.constants import EXAMPLE_CLIENT_EMAIL, EXAMPLE_THERAPIST_EMAIL
from app.constants import (EXAMPLE_CLIENT_EMAIL, EXAMPLE_THERAPIST_EMAIL,
EXAMPLE_VALID_PASSWORD)
from app.models import SeedableMixin
from app.models.enums import Gender, UserRole

Expand Down Expand Up @@ -69,78 +73,98 @@ def onboarding_complete(self) -> bool:

@classmethod
def seed(cls, db: SQLAlchemy, fake: Faker) -> None:
# Create a fake email that doesn't already exist in the database
def unique_fake_email() -> str:
fake_email = fake.unique.email().lower()
while (
db.session.execute(
db.select(User).filter_by(email=fake_email)
).scalar_one_or_none()
is not None
):
fake_email = fake.unique.email()
return fake_email

# Fake password that meets requirements to be used for all users
fake_password_hash = generate_password_hash("ValidPassword1")

# Insert example client for development purposes
example_fake_user_client = User(
email=EXAMPLE_CLIENT_EMAIL.lower(),
password_hash=fake_password_hash,
first_name="John",
last_name="Smith",
gender=Gender.MALE,
role=UserRole.CLIENT,
date_joined=fake.past_date(start_date="-1y", tzinfo=None),
verified=True,
active=True,
)
db.session.add(example_fake_user_client)

# Insert example therapist for development purposes
example_fake_user_therapist = User(
email=EXAMPLE_THERAPIST_EMAIL.lower(),
password_hash=fake_password_hash,
first_name="Jane",
last_name="Doe",
gender=Gender.FEMALE,
role=UserRole.THERAPIST,
date_joined=fake.past_date(start_date="-1y", tzinfo=None),
verified=True,
active=True,
)
db.session.add(example_fake_user_therapist)
used_emails = set()

# Insert 10 clients with fake and randomly selected data
for _ in range(10):
fake_user_therapist = User(
email=unique_fake_email(),
password_hash=fake_password_hash,
first_name=fake.first_name(),
last_name=fake.last_name(),
gender=random.choice(list(Gender)),
role=UserRole.CLIENT,
def fetch_random_user_from_api(save_profile_picture: bool = True) -> dict:
while True:
# Fetch random user data from external API
response = requests.get("https://randomuser.me/api/")
data = response.json()

# Extract user data
user_data = data.get("results")[0]
email = user_data.get("email")

# Retry if email has already been used
if email in used_emails:
continue

user_data["profile_pic_filename"] = None

if save_profile_picture:
# Save profile picture locally
profile_pic_url = user_data["picture"]["medium"]
profile_pic_filename = f"user_{fake.uuid4()}.jpg"
profile_pic_filepath = os.path.join(
"app", "static", "img", "profile_pictures", profile_pic_filename
)
with open(profile_pic_filepath, "wb") as f:
pic_response = requests.get(profile_pic_url)
f.write(pic_response.content)
user_data["profile_pic_filename"] = profile_pic_filename
return user_data

def make_random_user(role: UserRole, **kwargs) -> User:
random_user_data = {}

# Fetch data from external API
if current_app.config["SEED_FROM_EXTERNAL_API"]:
random_user_data = fetch_random_user_from_api()
email = random_user_data["email"]
first_name = random_user_data["name"]["first"]
last_name = random_user_data["name"]["last"]
profile_pic = random_user_data.get("profile_pic_filename")
try:
gender = Gender[random_user_data["gender"].upper()]
except KeyError:
gender = random.choice(list(Gender))

# Generate data using faker and random
else:
email = fake.unique.email().lower()
while email in used_emails:
email = fake.unique.email().lower()
first_name = fake.first_name()
last_name = fake.last_name()
gender = random.choice(list(Gender))
profile_pic = None

user = User(
email=email,
password_hash=generate_password_hash(EXAMPLE_VALID_PASSWORD),
first_name=first_name,
last_name=last_name,
gender=gender,
role=role,
date_joined=fake.past_date(start_date="-1y", tzinfo=None),
profile_picture=profile_pic,
verified=True,
active=True,
)
db.session.add(fake_user_therapist)

# Insert 10 therapists with fake and randomly selected data
# Update values with kwargs if provided
for key, value in kwargs.items():
setattr(user, key, value)

# Keep track of used emails to prevent duplicates
used_emails.add(email)
return user

fake_users = []

# Insert example therapist and client for development purposes
fake_users.append(
make_random_user(role=UserRole.CLIENT, email=EXAMPLE_CLIENT_EMAIL)
)
fake_users.append(
make_random_user(role=UserRole.THERAPIST, email=EXAMPLE_THERAPIST_EMAIL)
)

# Insert 20 fake therapists and clients (10 each)
for _ in range(10):
fake_user_therapist = User(
email=unique_fake_email(),
password_hash=fake_password_hash,
first_name=fake.first_name(),
last_name=fake.last_name(),
gender=random.choice(list(Gender)),
role=UserRole.THERAPIST,
date_joined=fake.past_date(start_date="-1y", tzinfo=None),
verified=True,
active=True,
)
db.session.add(fake_user_therapist)
fake_users.append(make_random_user(UserRole.THERAPIST))
fake_users.append(make_random_user(UserRole.CLIENT))

db.session.add_all(fake_users)
db.session.commit()
return
5 changes: 0 additions & 5 deletions app/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,6 @@ $(document).ready(function() {
// Update the URL query string with the new section
var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?section=' + target.substring(1);
window.history.pushState({ path: newUrl }, '', newUrl);

// Adjust height of messages selector
// if () TODO: re-implement adjustMessagesSelectorHeight
adjustMessagesSelectorHeight();
$(window).resize(adjustMessagesSelectorHeight);
});


Expand Down
16 changes: 0 additions & 16 deletions app/tasks.py

This file was deleted.

Loading

0 comments on commit 297b760

Please sign in to comment.