Skip to content

Commit

Permalink
Log record many residents: Backend (#192)
Browse files Browse the repository at this point in the history
* Create & migrate log_record_residents table

* changed add record function

* change delete and update

* modify update logic

* change get function

* changed filtering by residents

* modify comments

* change filter logic again

* initial frontend changes

* fix count function

* changing create and edit

* Enable multiselect for residents

* small fixes

* Enable resident multiselect when creating log

* small changes

* Fix residents array for API request

* Multiselect residents for edit log

* testing some stuff

* fix edit log

* change migration files

* send empty tags array

* change filter query to filter by initial + room num

* Pass resident labels

* Revert "change filter query to filter by initial + room num"

This reverts commit 173d504.

* Revert "Pass resident labels"

This reverts commit 3b03c7f.

* resolve pr comments

* run lint function

* run linter

* resolve PR comments

* Style residents dropdown

* Minor style fixes

---------

Co-authored-by: Carolyn Zhang <[email protected]>
  • Loading branch information
braydonwang and carolynzhang18 authored Nov 23, 2023
1 parent f912d50 commit 90a181e
Show file tree
Hide file tree
Showing 24 changed files with 227 additions and 123 deletions.
1 change: 1 addition & 0 deletions backend/app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def init_app(app):
from .log_record_tags import LogRecordTag
from .residents import Residents
from .buildings import Buildings
from .log_record_residents import LogRecordResidents

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

from . import db


class LogRecordResidents(db.Model):
__tablename__ = "log_record_residents"

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
)
resident_id = db.Column(db.Integer, db.ForeignKey("residents.id"), nullable=False)

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_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ class LogRecords(db.Model):
__tablename__ = "log_records"
log_id = db.Column(db.Integer, primary_key=True, nullable=False)
employee_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
resident_id = db.Column(db.Integer, db.ForeignKey("residents.id"), nullable=False)
datetime = db.Column(db.DateTime(timezone=True), nullable=False)
flagged = db.Column(db.Boolean, nullable=False)
attn_to = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
Expand All @@ -17,6 +16,9 @@ class LogRecords(db.Model):
tags = db.relationship(
"Tag", secondary="log_record_tag", back_populates="log_records"
)
residents = db.relationship(
"Residents", secondary="log_record_residents", back_populates="log_records"
)
building = db.relationship("Buildings", back_populates="log_record")

def to_dict(self, include_relationships=False):
Expand Down
3 changes: 3 additions & 0 deletions backend/app/models/residents.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class Residents(db.Model):
date_left = db.Column(db.Date, nullable=True)
building_id = db.Column(db.Integer, db.ForeignKey("buildings.id"), nullable=False)
building = db.relationship("Buildings", back_populates="resident")
log_records = db.relationship(
"LogRecords", secondary="log_record_residents", back_populates="residents"
)

resident_id = db.column_property(initial + cast(room_num, String))

Expand Down
58 changes: 44 additions & 14 deletions backend/app/services/implementations/log_records_service.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from ..interfaces.log_records_service import ILogRecordsService
from ...models.log_records import LogRecords
from ...models.residents import Residents
from ...models.user import User
from ...models.tags import Tag
from ...models import db
from datetime import datetime
Expand All @@ -23,12 +25,15 @@ def __init__(self, logger):

def add_record(self, log_record):
new_log_record = log_record.copy()

residents = new_log_record["residents"]
tag_names = new_log_record["tags"]

del new_log_record["residents"]
del new_log_record["tags"]

try:
new_log_record = LogRecords(**new_log_record)
self.construct_residents(new_log_record, residents)
self.construct_tags(new_log_record, tag_names)

db.session.add(new_log_record)
Expand All @@ -37,6 +42,15 @@ def add_record(self, log_record):
except Exception as postgres_error:
raise postgres_error

def construct_residents(self, log_record, residents):
residents = list(set(residents))
for resident_id in residents:
resident = Residents.query.filter_by(id=resident_id).first()

if not resident:
raise Exception(f"Resident with id {resident_id} does not exist")
log_record.residents.append(resident)

def construct_tags(self, log_record, tag_names):
for tag_name in tag_names:
tag = Tag.query.filter_by(name=tag_name).first()
Expand All @@ -57,7 +71,7 @@ def to_json_list(self, logs):
"first_name": log[2],
"last_name": log[3],
},
"resident_id": log[4],
"residents": log[4],
"attn_to": {
"id": log[5],
"first_name": log[6],
Expand Down Expand Up @@ -94,13 +108,15 @@ def filter_by_employee_id(self, employee_id):
return sql_statement
return f"\nemployee_id={employee_id}"

def filter_by_resident_id(self, resident_id):
if type(resident_id) == list:
sql_statement = f"\nresident_id={resident_id[0]}"
for i in range(1, len(resident_id)):
sql_statement = sql_statement + f"\nOR resident_id={resident_id[i]}"
def filter_by_residents(self, residents):
if type(residents) == list:
sql_statement = f"\n'{residents[0]}'=ANY (resident_ids)"
for i in range(1, len(residents)):
sql_statement = (
sql_statement + f"\nAND '{residents[i]}'=ANY (resident_ids)"
)
return sql_statement
return f"\nresident_id={resident_id}"
return f"\n'{residents}'=ANY (resident_ids)"

def filter_by_attn_to(self, attn_to):
if type(attn_to) == list:
Expand Down Expand Up @@ -150,7 +166,7 @@ def filter_log_records(self, filters=None):
options = {
"building_id": self.filter_by_building_id,
"employee_id": self.filter_by_employee_id,
"resident_id": self.filter_by_resident_id,
"residents": self.filter_by_residents,
"attn_to": self.filter_by_attn_to,
"date_range": self.filter_by_date_range,
"tags": self.filter_by_tags,
Expand All @@ -166,6 +182,14 @@ def filter_log_records(self, filters=None):
sql = sql + "\nAND " + options[filter](filters.get(filter))
return sql

def join_resident_attributes(self):
return "\nLEFT JOIN\n \
(SELECT logs.log_id, ARRAY_AGG(residents.id) AS resident_ids, ARRAY_AGG(CONCAT(residents.initial, residents.room_num)) AS residents FROM log_records logs\n \
JOIN log_record_residents lrr ON logs.log_id = lrr.log_record_id\n \
JOIN residents ON lrr.resident_id = residents.id\n \
GROUP BY logs.log_id \n \
) r ON logs.log_id = r.log_id\n"

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 \
Expand All @@ -183,7 +207,7 @@ def get_log_records(
logs.employee_id,\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 \
r.residents,\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 \
Expand All @@ -196,9 +220,9 @@ def get_log_records(
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\n \
JOIN buildings on logs.building_id = buildings.id"

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

Expand All @@ -225,10 +249,10 @@ def count_log_records(self, filters=None):
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\n \
JOIN buildings on logs.building_id = buildings.id"

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

num_results = db.session.execute(text(sql))
Expand All @@ -246,6 +270,7 @@ def delete_log_record(self, log_id):
raise Exception(
"Log record with id {log_id} not found".format(log_id=log_id)
)
log_record_to_delete.residents = []
log_record_to_delete.tags = []
db.session.delete(log_record_to_delete)
db.session.commit()
Expand Down Expand Up @@ -274,10 +299,15 @@ def update_log_record(self, log_id, updated_log_record):
LogRecords.tags: None,
}
)

log_record = LogRecords.query.filter_by(log_id=log_id).first()
if log_record:
log_record.residents = []
self.construct_residents(log_record, updated_log_record["residents"])

updated_log_record = LogRecords.query.filter_by(log_id=log_id).update(
{
LogRecords.employee_id: updated_log_record["employee_id"],
LogRecords.resident_id: updated_log_record["resident_id"],
LogRecords.flagged: updated_log_record["flagged"],
LogRecords.building_id: updated_log_record["building_id"],
LogRecords.note: updated_log_record["note"],
Expand Down
2 changes: 1 addition & 1 deletion backend/app/services/interfaces/log_records_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def add_record(self, log_record):
:param user_id: user id of the user adding the log record
:type user_id: int
:param resident_id: resident's id
:param residents: list of resident ids
:param flagged: checkbox if attention is needed
:type flagged: boolean
:param note: note that user inputs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""add log_record_residents
Revision ID: 0c76a8fe211d
Revises: 24fad25f60e3
Create Date: 2023-10-19 00:02:43.259307
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "0c76a8fe211d"
down_revision = "24fad25f60e3"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"log_record_residents",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("log_record_id", sa.Integer(), nullable=False),
sa.Column("resident_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["log_record_id"],
["log_records.log_id"],
),
sa.ForeignKeyConstraint(
["resident_id"],
["residents.id"],
),
sa.PrimaryKeyConstraint("id"),
)
with op.batch_alter_table("log_records", schema=None) as batch_op:
batch_op.drop_constraint("log_records_resident_id_fkey", type_="foreignkey")
batch_op.drop_column("resident_id")

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("log_records", schema=None) as batch_op:
batch_op.add_column(
sa.Column("resident_id", sa.INTEGER(), autoincrement=False, nullable=False)
)
batch_op.create_foreign_key(
"log_records_resident_id_fkey", "residents", ["resident_id"], ["id"]
)

op.drop_table("log_record_residents")
# ### end Alembic commands ###
24 changes: 0 additions & 24 deletions backend/migrations/versions/8b5132609f1f_merging.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

# revision identifiers, used by Alembic.
revision = "a2a0a16b6d51"
down_revision = "82f36cdf325f"
down_revision = "65a56c245ad7"
branch_labels = None
depends_on = None

Expand Down
16 changes: 8 additions & 8 deletions frontend/src/APIClients/LogRecordAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const countLogRecords = async ({
employeeId = [],
attnTo = [],
dateRange = [],
residentId = [],
residents = [],
tags = [],
flagged = false,
}: CountLogRecordFilters): Promise<GetLogRecordCountResponse> => {
Expand All @@ -32,7 +32,7 @@ const countLogRecords = async ({
employeeId,
attnTo,
dateRange,
residentId,
residents,
tags,
flagged,
},
Expand All @@ -51,7 +51,7 @@ const filterLogRecords = async ({
employeeId = [],
attnTo = [],
dateRange = [],
residentId = [],
residents = [],
tags = [],
flagged = false,
returnAll = false,
Expand All @@ -70,7 +70,7 @@ const filterLogRecords = async ({
employeeId,
attnTo,
dateRange,
residentId,
residents,
tags,
flagged,
},
Expand All @@ -89,7 +89,7 @@ const filterLogRecords = async ({

const createLog = async ({
employeeId,
residentId,
residents,
datetime,
flagged,
note,
Expand All @@ -106,7 +106,7 @@ const createLog = async ({
"/log_records/",
{
employeeId,
residentId,
residents,
datetime,
flagged,
note,
Expand Down Expand Up @@ -140,7 +140,7 @@ const deleteLogRecord = async (logId: number): Promise<boolean> => {
const editLogRecord = async ({
logId,
employeeId,
residentId,
residents,
datetime,
flagged,
note,
Expand All @@ -157,7 +157,7 @@ const editLogRecord = async ({
`/log_records/${logId}`,
{
employeeId,
residentId,
residents,
datetime,
flagged,
note,
Expand Down
Loading

0 comments on commit 90a181e

Please sign in to comment.