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 17 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 @@ -12,6 +12,7 @@ def init_app(app):
from .log_records import LogRecords
from .tag import Tag
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
3 changes: 2 additions & 1 deletion backend/app/models/log_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class LogRecords(db.Model):
# TODO: replace open String fields with VarChar(NUM_CHARS)
note = db.Column(db.String, nullable=False)
tags = db.Column(db.ARRAY(db.String), nullable=True)
building = db.Column(db.String, nullable=False)
building_id = db.Column(db.Integer, db.ForeignKey("buildings.id"), nullable=False)
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
3 changes: 2 additions & 1 deletion 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 Down
10 changes: 5 additions & 5 deletions backend/app/rest/tags_routes.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from flask import Blueprint, current_app, jsonify,request
from flask import Blueprint, current_app, jsonify, request
from ..middlewares.auth import require_authorization_by_role
from ..services.implementations.tags_service import TagsService

tags_service = TagsService(current_app.logger)
blueprint = Blueprint("tags", __name__, url_prefix="/tags")


@blueprint.route("/", methods=["GET"], strict_slashes=False)
@require_authorization_by_role({"Admin"})
def get_tags():
Expand All @@ -18,6 +19,7 @@ def get_tags():
error_message = getattr(e, "message", None)
return jsonify({"error": (error_message if error_message else str(e))}), 500


@blueprint.route("/<int:tag_id>", methods=["PUT"], strict_slashes=False)
@require_authorization_by_role({"Admin"})
def update_tag(tag_id):
Expand All @@ -26,9 +28,7 @@ def update_tag(tag_id):
"""
updated_tag_record = request.json
try:
updated_tag_record = tags_service.update_tag(
tag_id, updated_tag_record
)
updated_tag_record = tags_service.update_tag(tag_id, updated_tag_record)
return (
jsonify(
{
Expand All @@ -41,4 +41,4 @@ def update_tag(tag_id):
)
except Exception as e:
error_message = getattr(e, "message", None)
return jsonify({"error": (error_message if error_message else str(e))}), 500
return jsonify({"error": (error_message if error_message else str(e))}), 500
33 changes: 18 additions & 15 deletions backend/app/services/implementations/log_records_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,20 @@ def to_json_list(self, logs):
"attn_to": log[5],
"note": log[6],
"tags": log[7],
"building": log[8],
helenellyx marked this conversation as resolved.
Show resolved Hide resolved
"employee_first_name": log[9],
"employee_last_name": log[10],
"attn_to_first_name": log[11],
"attn_to_last_name": log[12],
"building_id": log[8],
helenellyx marked this conversation as resolved.
Show resolved Hide resolved
connor-bechthold marked this conversation as resolved.
Show resolved Hide resolved
"building": log[9],
"employee_first_name": log[10],
"employee_last_name": log[11],
"attn_to_first_name": log[12],
"attn_to_last_name": log[13],
}
)
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
return f"\nlogs.building_id='{building_id}'"

def filter_by_employee_id(self, employee_id):
if type(employee_id) == list:
Expand Down Expand Up @@ -88,17 +89,17 @@ def filter_by_attn_to(self, attn_to):
def filter_by_date_range(self, date_range):
sql = ""
if len(date_range) > 0:
if (date_range[0] != ""):
if date_range[0] != "":
start_date = datetime.strptime(date_range[0], "%Y-%m-%d").replace(
hour=0, minute=0
)
sql += f"\ndatetime>='{start_date}'"
if (date_range[-1] != ""):
if date_range[-1] != "":
end_date = datetime.strptime(
date_range[len(date_range) - 1], "%Y-%m-%d"
).replace(hour=23, minute=59)
if (sql == ""):

if sql == "":
sql += f"\ndatetime<='{end_date}'"
else:
sql += f"\nAND datetime<='{end_date}'"
Expand All @@ -121,7 +122,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 Down Expand Up @@ -152,15 +153,17 @@ def get_log_records(
logs.attn_to,\n \
logs.note,\n \
logs.tags,\n \
logs.building,\n \
buildings.id AS building_id,\n \
connor-bechthold marked this conversation as resolved.
Show resolved Hide resolved
buildings.name AS building,\n \
employees.first_name AS employee_first_name,\n \
employees.last_name AS employee_last_name,\n \
attn_tos.first_name AS attn_to_first_name,\n \
attn_tos.last_name AS attn_to_last_name\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.filter_log_records(filters)

Expand Down Expand Up @@ -235,7 +238,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
48 changes: 41 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,27 @@ 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
date_left = None
if result[0].date_left:
date_left = result[0].date_left.strftime("%Y-%m-%d")

residents_json_list.append(
{
"id": result[0].id,
"initial": result[0].initial,
"room_num": result[0].room_num,
"date_joined": result[0].date_joined.strftime("%Y-%m-%d"),
"date_left": date_left,
"resident_id": result[0].resident_id,
"building_id": result[0].building_id,
"building": result[1]
}
)
return residents_json_list

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

Expand Down Expand Up @@ -94,20 +116,32 @@ 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
15 changes: 5 additions & 10 deletions backend/app/services/implementations/tags_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from ...models.tag import Tag
from ...models import db


class TagsService(ITagsService):
"""
Tags implementation with tags management methods
Expand All @@ -19,9 +20,7 @@ def __init__(self, logger):
def get_tags(self):
try:
tags_results = Tag.query.all()
tags_results = list(
map(lambda tag: tag.to_dict(), tags_results)
)
tags_results = list(map(lambda tag: tag.to_dict(), tags_results))
return {"tags": tags_results}
except Exception as postgres_error:
raise postgres_error
Expand All @@ -32,14 +31,10 @@ def update_tag(self, tag_id, updated_tag):
if name_check is not None:
raise Exception("Tag name {name} already exists".format(name=updated_name))
create_update_tag = Tag.query.filter_by(tag_id=tag_id).update(
{
{
**updated_tag,
}
)
if not create_update_tag:
raise Exception(
"Tag with id {tag_id} not found".format(
tag_id=tag_id
)
)
db.session.commit()
raise Exception("Tag with id {tag_id} not found".format(tag_id=tag_id))
db.session.commit()
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
4 changes: 2 additions & 2 deletions backend/app/services/interfaces/tags_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def get_tags(self):
Gets tags in json format.
"""
pass

@abstractmethod
def update_tag(self, tag_id, updated_tag):
"""
Expand All @@ -22,4 +22,4 @@ def update_tag(self, tag_id, updated_tag):
tag_id (int): tag ID of the tag that is to be updated
updated_tag (json): json object that contains the info of the tag that is to be updated
"""
pass
pass
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ def upgrade():
sa.Column("room_num", sa.Integer(), nullable=False),
sa.Column("date_joined", sa.DateTime(timezone=True), nullable=False),
sa.Column("date_left", sa.DateTime(timezone=True), nullable=True),
sa.Column(
"building", sa.Enum("144", "402", "362", name="buildings"), nullable=False
sa.Column("building_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["building_id"],
["buildings.id"],
connor-bechthold marked this conversation as resolved.
Show resolved Hide resolved
),
sa.PrimaryKeyConstraint("id"),
)
Expand All @@ -48,7 +50,6 @@ def upgrade():

def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint("check_date_left_valid", "residents", type_="check")
connor-bechthold marked this conversation as resolved.
Show resolved Hide resolved
with op.batch_alter_table("log_records", schema=None) as batch_op:
batch_op.add_column(
sa.Column(
Expand Down
34 changes: 34 additions & 0 deletions backend/migrations/versions/c24644595836_adding_buildings_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""adding buildings table

Revision ID: c24644595836
Revises: a5d22b31faab
Create Date: 2023-06-26 23:12:08.724039

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "c24644595836"
down_revision = "58313513d17f"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"buildings",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("address", sa.String(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("buildings")
# ### end Alembic commands ###
Loading