Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create buildings table #125

Merged
merged 28 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
65cf98b
Create buildings table
helenellyx Jun 26, 2023
05e7869
Merge branch 'main' into helen/create-building-table
helenellyx Jun 27, 2023
7a8a620
Set up buildings table to have prexisting building
helenellyx Jun 30, 2023
2a2721c
Merge branch 'main' into helen/create-building-table
helenellyx Jul 18, 2023
1b33a9a
Create script
helenellyx Jul 18, 2023
6f0ed56
Merge branch 'main' into helen/create-building-table
helenellyx Jul 18, 2023
41b6453
Change building_id to building
helenellyx Jul 20, 2023
8c7f62d
move script to seeding dir
Safewaan Jul 24, 2023
f1dc59a
Update backend/app/services/implementations/log_records_service.py
helenellyx Jul 25, 2023
bf0b625
Add JOIN statement
helenellyx Jul 25, 2023
153a8d2
Merge branch 'helen/create-building-table' of github.com:uwblueprint/…
helenellyx Jul 25, 2023
41f3b15
Merge branch 'main' into helen/create-building-table
phamkelly17 Oct 7, 2023
5b983b2
fix features to use building_id instead of building
phamkelly17 Oct 9, 2023
cbd6215
format
phamkelly17 Oct 9, 2023
b97a423
chnage building to building id
phamkelly17 Oct 9, 2023
5573674
fix csv
phamkelly17 Oct 9, 2023
6ce15e3
address PR comments
phamkelly17 Oct 22, 2023
df43cb8
add default buildings on db creation
Oct 30, 2023
cfaa726
pr comments
phamkelly17 Nov 2, 2023
8ba2af2
Merge branch 'helen/create-building-table' of https://github.com/uwbl…
phamkelly17 Nov 2, 2023
b56a58c
fix merge conflicts
Nov 4, 2023
aead0dc
fix merge conflicts
Nov 4, 2023
3a7b1a0
final touchups
Nov 4, 2023
4f5a8d5
final touchups
Nov 4, 2023
e41b1d5
remove redundant date check
Nov 4, 2023
8d2df42
run linter there's so many changes
Nov 4, 2023
e49312c
add building filters when counting log records
Nov 5, 2023
0e9f68c
Merge branch 'main' into helen/create-building-table
phamkelly17 Nov 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def init_app(app):
from .tags import Tag
from .log_record_tags import LogRecordTag
from .residents import Residents
from .buildings import Buildings

app.app_context().push()
db.init_app(app)
Expand Down
32 changes: 32 additions & 0 deletions backend/app/models/buildings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from . import db
from sqlalchemy import inspect, cast, String
from sqlalchemy.orm.properties import ColumnProperty


class Buildings(db.Model):
__tablename__ = "buildings"
id = db.Column(db.Integer, primary_key=True, nullable=False)
address = db.Column(db.String, nullable=False)
name = db.Column(db.String, nullable=False)
log_record = db.relationship("LogRecords", back_populates="building")
resident = db.relationship("Residents", back_populates="building")

phamkelly17 marked this conversation as resolved.
Show resolved Hide resolved
def to_dict(self, include_relationships=False):
# define the entities table
cls = type(self)

mapper = inspect(cls)
formatted = {}
for column in mapper.attrs:
field = column.key
attr = getattr(self, field)
# if it's a regular column, extract the value
if isinstance(column, ColumnProperty):
formatted[field] = attr
# otherwise, it's a relationship field
# (currently not applicable, but may be useful for entity groups)
elif include_relationships:
# recursively format the relationship
# don't format the relationship's relationships
formatted[field] = [obj.to_dict() for obj in attr]
return formatted
4 changes: 3 additions & 1 deletion backend/app/models/log_record_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class LogRecordTag(db.Model):
__tablename__ = "log_record_tag"

log_record_tag_id = db.Column(db.Integer, primary_key=True, nullable=False)
log_record_id = db.Column(db.Integer, db.ForeignKey("log_records.log_id"), nullable=False)
log_record_id = db.Column(
db.Integer, db.ForeignKey("log_records.log_id"), nullable=False
)
tag_id = db.Column(db.Integer, db.ForeignKey("tags.tag_id"), nullable=False)

def to_dict(self, include_relationships=False):
Expand Down
7 changes: 5 additions & 2 deletions backend/app/models/log_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ class LogRecords(db.Model):
attn_to = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
# TODO: replace open String fields with VarChar(NUM_CHARS)
note = db.Column(db.String, nullable=False)
building = db.Column(db.String, nullable=False)
tags = db.relationship("Tag", secondary="log_record_tag", back_populates="log_records")
building_id = db.Column(db.Integer, db.ForeignKey("buildings.id"), nullable=False)
tags = db.relationship(
"Tag", secondary="log_record_tag", back_populates="log_records"
)
building = db.relationship("Buildings", back_populates="log_record")
connor-bechthold marked this conversation as resolved.
Show resolved Hide resolved

phamkelly17 marked this conversation as resolved.
Show resolved Hide resolved
def to_dict(self, include_relationships=False):
# define the entities table
Expand Down
7 changes: 5 additions & 2 deletions backend/app/models/residents.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class Residents(db.Model):
room_num = db.Column(db.Integer, nullable=False)
date_joined = db.Column(db.Date, nullable=False)
date_left = db.Column(db.Date, nullable=True)
building = db.Column(db.Enum("144", "402", "362", name="buildings"), nullable=False)
building_id = db.Column(db.Integer, db.ForeignKey("buildings.id"), nullable=False)
building = db.relationship("Buildings", back_populates="resident")

phamkelly17 marked this conversation as resolved.
Show resolved Hide resolved
resident_id = db.column_property(initial + cast(room_num, String))

Expand All @@ -31,7 +32,9 @@ def to_dict(self, include_relationships=False):
attr = getattr(self, field)
# if it's a regular column, extract the value
if isinstance(column, ColumnProperty):
if (field == "date_joined" or field == "date_left") and attr:
if field == "building_id":
formatted["building"] = {"id": attr}
elif (field == "date_joined" or field == "date_left") and attr:
formatted[field] = attr.strftime("%Y-%m-%d")
else:
formatted[field] = attr
Expand Down
4 changes: 3 additions & 1 deletion backend/app/models/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ class Tag(db.Model):
tag_id = db.Column(db.Integer, primary_key=True, nullable=False)
name = db.Column(db.String, nullable=False)
status = db.Column(db.Enum("Deleted", "Active", name="status"), nullable=False)
log_records = db.relationship("LogRecords", secondary="log_record_tag", back_populates="tags")
log_records = db.relationship(
"LogRecords", secondary="log_record_tag", back_populates="tags"
)

def to_dict(self, include_relationships=False):
# define the entities table
Expand Down
4 changes: 2 additions & 2 deletions backend/app/rest/auth_routes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from ..utilities.exceptions.firebase_exceptions import (
InvalidPasswordException,
TooManyLoginAttemptsException
InvalidPasswordException,
TooManyLoginAttemptsException,
)

from flask import Blueprint, current_app, jsonify, request
Expand Down
4 changes: 2 additions & 2 deletions backend/app/services/implementations/auth_service.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import firebase_admin.auth

from ...utilities.exceptions.firebase_exceptions import (
InvalidPasswordException,
TooManyLoginAttemptsException
InvalidPasswordException,
TooManyLoginAttemptsException,
)
from ..interfaces.auth_service import IAuthService
from ...resources.auth_dto import AuthDTO
Expand Down
85 changes: 48 additions & 37 deletions backend/app/services/implementations/log_records_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from ...models import db
from datetime import datetime
from pytz import timezone
from sqlalchemy import select, cast, Date, text
import os
from sqlalchemy import text


class LogRecordsService(ILogRecordsService):
Expand Down Expand Up @@ -37,12 +36,12 @@ def add_record(self, log_record):
return log_record
except Exception as postgres_error:
raise postgres_error

def construct_tags(self, log_record, tag_names):
for tag_name in tag_names:
tag = Tag.query.filter_by(name=tag_name).first()

if not tag:
if not tag:
raise Exception(f"Tag with name {tag_name} does not exist")
log_record.tags.append(tag)

Expand All @@ -53,30 +52,39 @@ def to_json_list(self, logs):
logs_list.append(
{
"log_id": log[0],
"resident_id": log[2],
"datetime": str(log[3].astimezone(timezone("US/Eastern"))),
"flagged": log[4],
"attn_to": {
"id": log[5],
"first_name": log[11],
"last_name": log[12]
},
"employee": {
"id": log[1],
"first_name": log[9],
"last_name": log[10]
"first_name": log[2],
"last_name": log[3],
},
"note": log[6],
"tags ": log[7],
"building": log[8],
helenellyx marked this conversation as resolved.
Show resolved Hide resolved
"resident_id": log[4],
"attn_to": {
"id": log[5],
"first_name": log[6],
"last_name": log[7],
}
if log[5]
else None,
"building": {"id": log[8], "name": log[9]},
"tags": log[10] if log[10] else [],
"note": log[11],
"flagged": log[12],
"datetime": str(log[13].astimezone(timezone("US/Eastern"))),
}
)
return logs_list
except Exception as postgres_error:
raise postgres_error

def filter_by_building(self, building):
return f"\nlogs.building='{building}'"
def filter_by_building_id(self, building_id):
phamkelly17 marked this conversation as resolved.
Show resolved Hide resolved
if type(building_id) == list:
sql_statement = f"\nlogs.building_id={building_id[0]}"
for i in range(1, len(building_id)):
sql_statement = (
sql_statement + f"\nOR logs.building_id={building_id[i]}"
)
return sql_statement
return f"\logs.building_id={building_id}"

def filter_by_employee_id(self, employee_id):
if type(employee_id) == list:
Expand Down Expand Up @@ -140,7 +148,7 @@ def filter_log_records(self, filters=None):
is_first_filter = True

options = {
"building": self.filter_by_building,
"building_id": self.filter_by_building_id,
"employee_id": self.filter_by_employee_id,
"resident_id": self.filter_by_resident_id,
"attn_to": self.filter_by_attn_to,
Expand All @@ -157,38 +165,40 @@ def filter_log_records(self, filters=None):
if filters.get(filter):
sql = sql + "\nAND " + options[filter](filters.get(filter))
return sql

def join_tag_attributes(self):
return "\nLEFT JOIN\n \
(SELECT logs.log_id, ARRAY_AGG(tags.name) AS tag_names FROM log_records logs\n \
JOIN log_record_tag lrt ON logs.log_id = lrt.log_record_id\n \
JOIN tags ON lrt.tag_id = tags.tag_id\n \
GROUP BY logs.log_id \n \
) t ON logs.log_id = t.log_id\n"

def get_log_records(
self, page_number, return_all, results_per_page=10, filters=None
):
try:
sql = "SELECT\n \
logs.log_id,\n \
logs.employee_id,\n \
CONCAT(residents.initial, residents.room_num) AS resident_id,\n \
logs.datetime,\n \
logs.flagged,\n \
logs.attn_to,\n \
logs.note,\n \
t.tag_names, \n \
logs.building,\n \
employees.first_name AS employee_first_name,\n \
employees.last_name AS employee_last_name,\n \
CONCAT(residents.initial, residents.room_num) AS resident_id,\n \
logs.attn_to,\n \
attn_tos.first_name AS attn_to_first_name,\n \
attn_tos.last_name AS attn_to_last_name\n \
attn_tos.last_name AS attn_to_last_name,\n \
buildings.id AS building_id,\n \
buildings.name AS building_name,\n \
t.tag_names, \n \
logs.note,\n \
logs.flagged,\n \
logs.datetime\n \
FROM log_records logs\n \
LEFT JOIN users attn_tos ON logs.attn_to = attn_tos.id\n \
JOIN users employees ON logs.employee_id = employees.id\n \
JOIN residents ON logs.resident_id = residents.id"

JOIN residents ON logs.resident_id = residents.id\n \
JOIN buildings on logs.building_id = buildings.id"

sql += self.join_tag_attributes()
sql += self.filter_log_records(filters)

Expand All @@ -214,10 +224,11 @@ def count_log_records(self, filters=None):
COUNT(*)\n \
FROM log_records logs\n \
LEFT JOIN users attn_tos ON logs.attn_to = attn_tos.id\n \
JOIN users employees ON logs.employee_id = employees.id"

sql += f"\n{self.join_tag_attributes()}"
JOIN users employees ON logs.employee_id = employees.id\n \
JOIN residents ON logs.resident_id = residents.id\n \
JOIN buildings on logs.building_id = buildings.id"

sql += f"\n{self.join_tag_attributes()}"
sql += self.filter_log_records(filters)

num_results = db.session.execute(text(sql))
Expand Down Expand Up @@ -254,7 +265,7 @@ def update_log_record(self, log_id, updated_log_record):
)
if "tags" in updated_log_record:
log_record = LogRecords.query.filter_by(log_id=log_id).first()
if (log_record):
if log_record:
log_record.tags = []
self.construct_tags(log_record, updated_log_record["tags"])
else:
Expand All @@ -268,7 +279,7 @@ def update_log_record(self, log_id, updated_log_record):
LogRecords.employee_id: updated_log_record["employee_id"],
LogRecords.resident_id: updated_log_record["resident_id"],
LogRecords.flagged: updated_log_record["flagged"],
LogRecords.building: updated_log_record["building"],
LogRecords.building_id: updated_log_record["building_id"],
LogRecords.note: updated_log_record["note"],
LogRecords.datetime: updated_log_record["datetime"],
}
Expand Down
38 changes: 31 additions & 7 deletions backend/app/services/implementations/residents_service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ..interfaces.residents_service import IResidentsService
from ...models.residents import Residents
from ...models.log_records import LogRecords
from ...models.buildings import Buildings
from ...models import db
from datetime import datetime
from sqlalchemy import select, cast, Date
Expand All @@ -21,6 +22,16 @@ def __init__(self, logger):
"""
self.logger = logger

def to_residents_json_list(self, resident_results):
residents_json_list = []
for result in resident_results:
phamkelly17 marked this conversation as resolved.
Show resolved Hide resolved
resident, building = result[0], result[1]

resident_dict = resident.to_dict()
resident_dict["building"]["name"] = building
residents_json_list.append(resident_dict)
return residents_json_list

def convert_to_date_obj(self, date):
return datetime.strptime(date, "%Y-%m-%d")

Expand Down Expand Up @@ -94,20 +105,33 @@ def get_residents(
):
try:
if resident_id:
residents_results = Residents.query.filter_by(resident_id=resident_id)
residents_results = (
Residents.query.join(
Buildings, Buildings.id == Residents.building_id
)
.with_entities(Residents, Buildings.name.label("building"))
.filter_by(resident_id=resident_id)
)
elif return_all:
residents_results = Residents.query.all()
residents_results = (
Residents.query.join(
Buildings, Buildings.id == Residents.building_id
)
.with_entities(Residents, Buildings.name.label("building"))
.all()
)
else:
residents_results = (
Residents.query.limit(results_per_page)
Residents.query.join(
Buildings, Buildings.id == Residents.building_id
)
.limit(results_per_page)
.offset((page_number - 1) * results_per_page)
.with_entities(Residents, Buildings.name.label("building"))
.all()
)

residents_results = list(
map(lambda resident: resident.to_dict(), residents_results)
)
return {"residents": residents_results}
return {"residents": self.to_residents_json_list(residents_results)}
except Exception as postgres_error:
raise postgres_error

Expand Down
8 changes: 4 additions & 4 deletions backend/app/services/interfaces/residents_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ class IResidentsService(ABC):
"""

@abstractmethod
def add_resident(self, id, initial, room_num, date_joined, date_left, building):
def add_resident(self, id, initial, room_num, date_joined, date_left, building_id):
"""
Add a resident to the database
:param id: autogenerated unique id of the resident
:type id: int
:param initial: initial of resident
:type initial: string
:param room_num: room number which the resident resides in
:type room_num: integer
:type room_num: int
:param date_joined: the date the resident joined
:type date_joined: date
:param date_left: the date the resident left, if exists
:type date_left: date
:param building: the building in which the resident is staying
:type building: Enum("144", "402", "362)
:param building_id: the building_id in which the resident is staying
:type building_id: int
:raises Exception: if resident creation fails
"""
pass
Expand Down
Loading