Skip to content

Commit

Permalink
Refactor: models (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
angela-tran authored Sep 15, 2022
2 parents a4235be + 7e64a94 commit b2d38f4
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 20 deletions.
13 changes: 7 additions & 6 deletions data/server.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
A1234567;Garcia;['type1']
B2345678;Hernandez;['type2']
C3456789;Smith;[]
D4567890;Jones;['type1', 'type2']
a93ef111829829ea5038398ba6583dc2e1a2f2c309e24aee535f725dbe1f1250;2c3aaefea8267c66822f6edc0e42d9b7384695f9c0407eabda141770aab8901e;['type1']
095feaba889222ee1bc97aaf3bb89c90c21238fb556fb48b5cc54760461ea18ce814d016e70b1929d05190edbec1032e07921228fcb61ad3417aaff0abd9d082;123c86e1f2ac255ba31f1ad742defe23d194269669d2aac0d2572e20e9378e395976f84db305caeba1f91e7996463031d4c49365a7a9f4c7dc404873ad330974;['type1']
A1234567;Garcia;type1
B2345678;Hernandez;type2
C3456789;Smith;
D4567890;Jones;type1
D4567890;Jones;type2
a93ef111829829ea5038398ba6583dc2e1a2f2c309e24aee535f725dbe1f1250;2c3aaefea8267c66822f6edc0e42d9b7384695f9c0407eabda141770aab8901e;type1
095feaba889222ee1bc97aaf3bb89c90c21238fb556fb48b5cc54760461ea18ce814d016e70b1929d05190edbec1032e07921228fcb61ad3417aaff0abd9d082;123c86e1f2ac255ba31f1ad742defe23d194269669d2aac0d2572e20e9378e395976f84db305caeba1f91e7996463031d4c49365a7a9f4c7dc404873ad330974;type1
19 changes: 17 additions & 2 deletions eligibility_server/db/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
from eligibility_server.db import db


user_eligibility = db.Table(
"user_eligibility",
db.Column("user_id", db.Integer, db.ForeignKey("user.id"), primary_key=True),
db.Column("eligibility_id", db.Integer, db.ForeignKey("eligibility.id"), primary_key=True),
)


class Eligibility(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)

def __repr__(self):
return f"<Eligibility {self.name}>"


class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
sub = db.Column(db.String, unique=True, nullable=False)
name = db.Column(db.String, unique=True, nullable=False)
types = db.Column(db.String, unique=False, nullable=False)
types = db.relationship("Eligibility", secondary=user_eligibility, lazy="subquery", backref=db.backref("users", lazy=True))

def __repr__(self):
return "<User %r>" % self.sub
return f"<User {self.sub}>"
31 changes: 22 additions & 9 deletions eligibility_server/db/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from flask_sqlalchemy import inspect

from eligibility_server.db import db
from eligibility_server.db.models import User
from eligibility_server.db.models import User, Eligibility
from eligibility_server.settings import Configuration


Expand Down Expand Up @@ -50,7 +50,7 @@ def import_users():
with open(file_path) as file:
data = json.load(file)["users"]
for user in data:
save_users(user, data[user][0], str(data[user][1]))
save_users(user, data[user][0], data[user][1])
elif file_format == "csv":
with open(file_path, newline=config.csv_newline, encoding="utf-8") as file:
data = csv.reader(
Expand All @@ -60,24 +60,32 @@ def import_users():
quotechar=config.csv_quotechar,
)
for user in data:
save_users(user[0], user[1], user[2])
# lists are expected to be a comma-separated value and quoted if the CSV delimiter is a comma
types = [type.replace(config.csv_quotechar, "") for type in user[2].split(",") if type]
save_users(user[0], user[1], types)
else:
click.echo(f"Warning: File format is not supported: {file_format}")

click.echo(f"Users added: {User.query.count()}")
click.echo(f"Eligibility types added: {Eligibility.query.count()}")


def save_users(sub: str, name: str, types: str):
def save_users(sub: str, name: str, types):
"""
Add users to the database User table
@param sub - User's sub
@param name - User's name
@param types - Types of eligibilities, in a stringified list
@param types - Types of eligibilities, in the form of a list of strings
"""

item = User(sub=sub, name=name, types=types)
db.session.add(item)
user = User.query.filter_by(sub=sub, name=name).first() or User(sub=sub, name=name)
eligibility_types = [Eligibility.query.filter_by(name=type).first() or Eligibility(name=type) for type in types]
user.types.extend(eligibility_types)

db.session.add(user)
db.session.add_all(eligibility_types)

db.session.commit()


Expand All @@ -90,9 +98,14 @@ def drop_db_command():
try:
click.echo(f"Users to be deleted: {User.query.count()}")
User.query.delete()
db.session.commit()

click.echo(f"Eligibility types to be deleted: {Eligibility.query.count()}")
Eligibility.query.delete()

except Exception as e:
click.echo("Failed to query for Users", e)
click.echo(f"Failed to query models. Exception: {e}", err=True)

db.session.commit()

db.drop_all()
click.echo("Database dropped.")
Expand Down
2 changes: 1 addition & 1 deletion eligibility_server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
CSV_DELIMITER = ";"
CSV_NEWLINE = ""
CSV_QUOTING = 3
CSV_QUOTECHAR = ""
CSV_QUOTECHAR = '"'


class Configuration:
Expand Down
3 changes: 1 addition & 2 deletions eligibility_server/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Eligibility Verification route
"""

import ast
import datetime
import json
import logging
Expand Down Expand Up @@ -140,7 +139,7 @@ def _check_user(self, sub, name, types):

existing_user = User.query.filter_by(sub=sub, name=name).first()
if existing_user:
existing_user_types = ast.literal_eval(existing_user.types)
existing_user_types = [type.name for type in existing_user.types]
else:
existing_user_types = []

Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ def flask():
@pytest.fixture
def client():
return app.test_client()


@pytest.fixture()
def runner():
return app.test_cli_runner()
Empty file added tests/db/__init__.py
Empty file.
42 changes: 42 additions & 0 deletions tests/db/test_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest

from flask_sqlalchemy import inspect
from eligibility_server.db import db
from eligibility_server.db.models import Eligibility, User


@pytest.mark.usefixtures("flask")
def test_init_db_command(runner):
"""Assumes that IMPORT_FILE_PATH is data/server.csv."""
runner.invoke(args="drop-db")

result = runner.invoke(args="init-db")

assert result.exit_code == 0

assert User.query.count() == 6
assert Eligibility.query.count() == 2

user_with_one_eligibility = User.query.filter_by(sub="A1234567", name="Garcia").first()
type1_eligibility = Eligibility.query.filter_by(name="type1").first()
assert user_with_one_eligibility.types == [type1_eligibility]

user_with_no_eligibility = User.query.filter_by(sub="C3456789", name="Smith").first()
assert user_with_no_eligibility.types == []

user_with_multiple_eligibilities = User.query.filter_by(sub="D4567890", name="Jones").first()
type2_eligibility = Eligibility.query.filter_by(name="type2").first()
assert user_with_multiple_eligibilities.types == [type1_eligibility, type2_eligibility]


@pytest.mark.usefixtures("flask")
def test_drop_db_command(runner):
result = runner.invoke(args="drop-db")

assert result.exit_code == 0

inspector = inspect(db.engine)
assert inspector.get_table_names() == []

# temp fix to ensure database tests are idempotent - later tests need the database to exist
runner.invoke(args="init-db")

0 comments on commit b2d38f4

Please sign in to comment.