Skip to content

Commit

Permalink
Create buildings table (#125)
Browse files Browse the repository at this point in the history
* Create buildings table

* Set up buildings table to have prexisting building

* Create script

* Change building_id to building

* move script to seeding dir

* Update backend/app/services/implementations/log_records_service.py

Co-authored-by: Safwaan Chowdhury <57375371+Safewaan@users.noreply.github.com>

* Add JOIN statement

* fix features to use building_id instead of building

* format

* chnage building to building id

* fix csv

* address PR comments

* add default buildings on db creation

* pr comments

* final touchups

* final touchups

* remove redundant date check

* run linter there's so many changes

* add building filters when counting log records

---------

Co-authored-by: Safewaan <Safewaan@users.noreply.github.com>
Co-authored-by: Safwaan Chowdhury <57375371+Safewaan@users.noreply.github.com>
Co-authored-by: Kelly Pham <phamkelly17@gmail.com>
Co-authored-by: Connor Bechthold <connorbechthold@icloud.com>
  • Loading branch information
5 people authored Nov 5, 2023
1 parent 4905d93 commit 211dc99
Show file tree
Hide file tree
Showing 41 changed files with 460 additions and 284 deletions.
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")

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")

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")

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],
"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):
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:
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

0 comments on commit 211dc99

Please sign in to comment.