Skip to content

Commit

Permalink
Bug squashing (#225)
Browse files Browse the repository at this point in the history
* fix email verification bug

* fix overflow and flagged checkbox bugs

* show employee full name in log record modals

* increase z index on navbar so it doesn't overlap with filter components

* fix date bugs with home page and csv convertor

* touch up signup, login, and user flows for non-admin users

* touchup spinner and employee create/edit form limitations

* run ze linter

* fix employee name display bug
  • Loading branch information
Connor Bechthold authored Jan 24, 2024
1 parent 3fe92d3 commit 5b73871
Show file tree
Hide file tree
Showing 41 changed files with 762 additions and 789 deletions.
2 changes: 1 addition & 1 deletion backend/app/rest/tags_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


@blueprint.route("/", methods=["GET"], strict_slashes=False)
@require_authorization_by_role({"Admin"})
@require_authorization_by_role({"Relief Staff", "Regular Staff", "Admin"})
def get_tags():
"""
Get tags.
Expand Down
12 changes: 5 additions & 7 deletions backend/app/rest/user_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ..services.implementations.user_service import UserService
from ..utilities.csv_utils import generate_csv_from_list
from ..utilities.exceptions.auth_exceptions import UserNotInvitedException
from ..utilities.exceptions.duplicate_entity_exceptions import DuplicateUserException


user_service = UserService(current_app.logger)
Expand Down Expand Up @@ -136,15 +137,12 @@ def create_user():
user = CreateInvitedUserDTO(**request.json)
created_user = user_service.create_invited_user(user)
return jsonify(created_user.__dict__), 201
except DuplicateUserException as e:
error_message = getattr(e, "message", None)
return jsonify({"error": (error_message if error_message else str(e))}), 409
except Exception as e:
error_message = getattr(e, "message", None)
status_code = None
if str(e) == "User already exists":
status_code = 409

return jsonify({"error": (error_message if error_message else str(e))}), (
status_code if status_code else 500
)
return jsonify({"error": (error_message if error_message else str(e))}), 500


@blueprint.route("/activate-user", methods=["POST"], strict_slashes=False)
Expand Down
32 changes: 15 additions & 17 deletions backend/app/services/implementations/log_records_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ...models.tags import Tag
from ...models import db
from datetime import datetime
from pytz import timezone
from pytz import timezone, utc
from sqlalchemy import text


Expand All @@ -32,6 +32,10 @@ def add_record(self, log_record):
del new_log_record["residents"]
del new_log_record["tags"]

new_log_record["datetime"] = datetime.fromisoformat(
new_log_record["datetime"].replace("Z", "+00:00")
).replace(tzinfo=utc)

try:
new_log_record = LogRecords(**new_log_record)
self.construct_residents(new_log_record, residents)
Expand Down Expand Up @@ -85,7 +89,7 @@ def to_json_list(self, logs):
"tags": log[10] if log[10] else [],
"note": log[11],
"flagged": log[12],
"datetime": str(log[13].astimezone(timezone("US/Eastern"))),
"datetime": log[13].isoformat(),
}
)
return logs_list
Expand Down Expand Up @@ -128,21 +132,15 @@ def filter_by_attn_tos(self, attn_tos):

def filter_by_date_range(self, date_range):
sql = ""
if len(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] != "":
end_date = datetime.strptime(
date_range[len(date_range) - 1], "%Y-%m-%d"
).replace(hour=23, minute=59)

if sql == "":
sql += f"\ndatetime<='{end_date}'"
else:
sql += f"\nAND datetime<='{end_date}'"
if date_range[0] is not None:
start_date = date_range[0].replace("Z", "+00:00")
sql += f"\ndatetime>='{start_date}'"
if date_range[-1] is not None:
end_date = date_range[-1].replace("Z", "+00:00")
if sql == "":
sql += f"\ndatetime<='{end_date}'"
else:
sql += f"\nAND datetime<='{end_date}'"
return sql

def filter_by_tags(self, tags):
Expand Down
10 changes: 7 additions & 3 deletions backend/app/services/implementations/residents_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,15 @@ def get_residents(self, return_all, page_number, results_per_page, filters=None)
)

if not return_all:
residents_results = residents_results.order_by(Residents.last_modified.desc()).limit(results_per_page).offset(
(page_number - 1) * results_per_page
residents_results = (
residents_results.order_by(Residents.last_modified.desc())
.limit(results_per_page)
.offset((page_number - 1) * results_per_page)
)
else:
residents_results = residents_results.order_by(Residents.last_modified.desc()).all()
residents_results = residents_results.order_by(
Residents.last_modified.desc()
).all()

return {
"residents": self.to_residents_json_list(
Expand Down
6 changes: 4 additions & 2 deletions backend/app/services/implementations/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
UserNotInvitedException,
EmailAlreadyInUseException,
)
from ...utilities.exceptions.duplicate_entity_exceptions import DuplicateUserException


class UserService(IUserService):
Expand Down Expand Up @@ -197,10 +198,11 @@ def create_invited_user(self, user):
db.session.add(user_entry)
db.session.commit()
else:
raise Exception("User already exists")
user_dict = UserService.__user_to_dict_and_remove_auth_id(user_entry)
raise DuplicateUserException(user.email)

user_dict = UserService.__user_to_dict_and_remove_auth_id(user_entry)
return UserDTO(**user_dict)

except Exception as e:
db.session.rollback()
reason = getattr(e, "message", None)
Expand Down
10 changes: 10 additions & 0 deletions backend/app/utilities/exceptions/duplicate_entity_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ class DuplicateTagException(Exception):
def __init__(self, tag_name):
message = f"Tag with name {tag_name} already exists."
super().__init__(message)


class DuplicateUserException(Exception):
"""
Raised when an duplicate user is encountered
"""

def __init__(self, email):
message = f"User with email {email} already exists."
super().__init__(message)
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,42 @@


# revision identifiers, used by Alembic.
revision = 'a1f05c8f324c'
down_revision = '117790caec65'
revision = "a1f05c8f324c"
down_revision = "117790caec65"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('log_record_tag', schema=None) as batch_op:
batch_op.drop_constraint('log_record_tag_tag_id_fkey', type_='foreignkey')
batch_op.create_foreign_key(None, 'tags', ['tag_id'], ['tag_id'], ondelete='CASCADE')

with op.batch_alter_table('residents', schema=None) as batch_op:
batch_op.add_column(sa.Column('last_modified', sa.DateTime(), server_default=sa.text('now()'), nullable=False))
with op.batch_alter_table("log_record_tag", schema=None) as batch_op:
batch_op.drop_constraint("log_record_tag_tag_id_fkey", type_="foreignkey")
batch_op.create_foreign_key(
None, "tags", ["tag_id"], ["tag_id"], ondelete="CASCADE"
)

with op.batch_alter_table("residents", schema=None) as batch_op:
batch_op.add_column(
sa.Column(
"last_modified",
sa.DateTime(),
server_default=sa.text("now()"),
nullable=False,
)
)

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('residents', schema=None) as batch_op:
batch_op.drop_column('last_modified')

with op.batch_alter_table('log_record_tag', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.create_foreign_key('log_record_tag_tag_id_fkey', 'tags', ['tag_id'], ['tag_id'])
with op.batch_alter_table("residents", schema=None) as batch_op:
batch_op.drop_column("last_modified")

with op.batch_alter_table("log_record_tag", schema=None) as batch_op:
batch_op.drop_constraint(None, type_="foreignkey")
batch_op.create_foreign_key(
"log_record_tag_tag_id_fkey", "tags", ["tag_id"], ["tag_id"]
)

# ### end Alembic commands ###
7 changes: 5 additions & 2 deletions frontend/src/APIClients/AuthAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const login = async (
}
return {
errCode: 500,
errMessage: "Error logging in. Please try again.",
errMessage: "Unable to login. Please try again.",
};
}
};
Expand Down Expand Up @@ -112,7 +112,10 @@ const register = async (
errMessage: getAuthErrMessage(axiosErr.response, "SIGNUP"),
};
}
return null;
return {
errCode: 500,
errMessage: "Error signing up. Please try again.",
};
}
};

Expand Down
65 changes: 0 additions & 65 deletions frontend/src/APIClients/CommonAPIClient.ts

This file was deleted.

76 changes: 76 additions & 0 deletions frontend/src/APIClients/UserAPIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import {
CountUsersResponse,
UpdateUserParams,
UserStatus,
GetUserStatusResponse,
} from "../types/UserTypes";
import { ErrorResponse } from "../types/ErrorTypes";

const getUsers = async ({
returnAll = false,
Expand Down Expand Up @@ -124,10 +126,84 @@ const deleteUser = async (userId: number): Promise<number> => {
}
};

const inviteUser = async (
email: string,
role: string,
firstName: string,
lastName: string,
): Promise<boolean | ErrorResponse> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;
await baseAPIClient.post(
"/users/invite-user",
{ email, role, firstName, lastName },
{ headers: { Authorization: bearerToken } },
);
return true;
} catch (error) {
const axiosErr = (error as any) as AxiosError;

if (axiosErr.response && axiosErr.response.status === 409) {
return {
errMessage:
axiosErr.response.data.error ??
"User with the specified email already exists.",
};
}
return false;
}
};

const getUserStatus = async (
email: string,
): Promise<UserStatus | ErrorResponse> => {
try {
const bearerToken = `Bearer ${getLocalStorageObjProperty(
AUTHENTICATED_USER_KEY,
"accessToken",
)}`;
const { data } = await baseAPIClient.get<GetUserStatusResponse>(
"/users/user-status",
{
params: {
email,
},
headers: { Authorization: bearerToken },
},
);
if (data.email === email) {
return data.userStatus;
}
return {
errMessage:
"This email address has not been invited. Please try again with a different email.",
};
} catch (error) {
const axiosErr = (error as any) as AxiosError;

if (axiosErr.response && axiosErr.response.status === 403) {
return {
errMessage:
axiosErr.response.data.error ??
"This email address has not been invited. Please try again with a different email.",
};
}

return {
errMessage: "Unable to get status of this user.",
};
}
};

export default {
getUsers,
countUsers,
updateUser,
updateUserStatus,
deleteUser,
inviteUser,
getUserStatus,
};
Loading

0 comments on commit 5b73871

Please sign in to comment.