From a3b5530cd92d2ecbf12f30059b6c44a70a7d0301 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:40:13 -0800 Subject: [PATCH 01/22] added admin/confirm-attendance and tests --- apps/api/src/routers/admin.py | 39 +++++++++++++++++++++++++++++++ apps/api/src/utils/user_record.py | 2 ++ apps/api/tests/test_admin.py | 33 ++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 0c47cb24..80eff79c 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -129,7 +129,34 @@ async def release_decisions() -> None: await asyncio.gather( *(_process_batch(batch, decision) for batch in batched(group, 100)) ) + +@router.post("/confirm-attendance", dependencies=[Depends(require_role([Role.DIRECTOR]))]) +async def confirm_attendance() -> None: + """Update applicant status to void or attending based on their current status.""" + records = await mongodb_handler.retrieve( + Collection.USERS, + {"role": Role.APPLICANT}, + ["_id", "status"], + ) + + for record in records: + _set_confirmations(record) + + for status in (Status.ATTENDING, Status.VOID): + + group = [record for record in records if record["status"] == status] + await asyncio.gather( + *(_process_status(batch, status) for batch in batched(group, 100)) + ) + +async def _process_status(batch: tuple[dict[str, Any], ...], status: Decision) -> None: + uids: list[str] = [record["_id"] for record in batch] + ok = await mongodb_handler.update( + Collection.USERS, {"_id": {"$in": uids}}, {"status": status} + ) + if not ok: + raise RuntimeError("gg wp") async def _process_batch(batch: tuple[dict[str, Any], ...], decision: Decision) -> None: uids: list[str] = [record["_id"] for record in batch] @@ -165,3 +192,15 @@ def _include_review_decision(applicant_record: dict[str, Any]) -> None: """Sets the applicant's decision as the last submitted review decision or None.""" reviews = applicant_record["application_data"]["reviews"] applicant_record["decision"] = reviews[-1][2] if reviews else None + + +def _set_confirmations(applicant_record: dict[str, Any]) -> None: + """Sets the applicant's status based on their RSVP status""" + + status = applicant_record["status"] + if status == Status.CONFIRMED: + applicant_record["status"] = Status.ATTENDING + elif status == Status.WAIVER_SIGNED: + applicant_record["status"] = Status.VOID + elif status == Decision.ACCEPTED: + applicant_record["status"] = Status.VOID \ No newline at end of file diff --git a/apps/api/src/utils/user_record.py b/apps/api/src/utils/user_record.py index 6c8b4956..05f52d37 100644 --- a/apps/api/src/utils/user_record.py +++ b/apps/api/src/utils/user_record.py @@ -22,6 +22,8 @@ class Status(str, Enum): REVIEWED = "REVIEWED" WAIVER_SIGNED = "WAIVER_SIGNED" CONFIRMED = "CONFIRMED" + ATTENDING = "ATTENDING" + VOID = "VOID" class UserRecord(BaseRecord): diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 70ba53b3..3daa1019 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -127,6 +127,39 @@ def test_no_decision_from_no_reviews() -> None: admin._include_review_decision(record) assert record["decision"] is None +def test_confirm_attendance_when_status_confirmed() -> None: + """Test that a decision is None for an applicant with no reviews.""" + record = { + "_id": "edu.uci.Hey", + "status": "CONFIRMED", + "decision": "ACCEPTED", + } + + admin._set_confirmations(record) + assert record["status"] == "ATTENDING" + +def test_confirm_attendance_when_status_accepted() -> None: + """Test that a decision is None for an applicant with no reviews.""" + record = { + "_id": "edu.uci.Vsauce", + "status": "ACCEPTED", + "decision": "ACCEPTED", + } + + admin._set_confirmations(record) + assert record["status"] == "VOID" + +def test_confirm_attendance_when_status_waiver_signed() -> None: + """Test that a decision is None for an applicant with no reviews.""" + record = { + "_id": "edu.uci.Michael", + "status": "WAIVER_SIGNED", + "decision": "ACCEPTED", + } + + admin._set_confirmations(record) + assert record["status"] == "VOID" + @patch("services.mongodb_handler.raw_update_one", autospec=True) @patch("services.mongodb_handler.retrieve_one", autospec=True) From 5f78b30f4568bfeb332ef62fb621f7e357ed3a6f Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:56:57 -0800 Subject: [PATCH 02/22] formatting changes --- apps/api/src/routers/admin.py | 21 ++++++++++++--------- apps/api/tests/test_admin.py | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 80eff79c..609e120e 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -125,12 +125,15 @@ async def release_decisions() -> None: group = [record for record in records if record["decision"] == decision] if not group: continue - await asyncio.gather( *(_process_batch(batch, decision) for batch in batched(group, 100)) ) - -@router.post("/confirm-attendance", dependencies=[Depends(require_role([Role.DIRECTOR]))]) + + +@router.post( + "/confirm-attendance", dependencies= + [Depends(require_role([Role.DIRECTOR]))] + ) async def confirm_attendance() -> None: """Update applicant status to void or attending based on their current status.""" records = await mongodb_handler.retrieve( @@ -142,15 +145,14 @@ async def confirm_attendance() -> None: for record in records: _set_confirmations(record) - for status in (Status.ATTENDING, Status.VOID): - - group = [record for record in records if record["status"] == status] - + for cur_status in (Status.ATTENDING, Status.VOID): + group = [record for record in records if record["status"] == cur_status] await asyncio.gather( - *(_process_status(batch, status) for batch in batched(group, 100)) + *(_process_status(batch, cur_status) for batch in batched(group, 100)) ) -async def _process_status(batch: tuple[dict[str, Any], ...], status: Decision) -> None: + +async def _process_status(batch: tuple[dict[str, Any], ...], status: Status) -> None: uids: list[str] = [record["_id"] for record in batch] ok = await mongodb_handler.update( Collection.USERS, {"_id": {"$in": uids}}, {"status": status} @@ -158,6 +160,7 @@ async def _process_status(batch: tuple[dict[str, Any], ...], status: Decision) - if not ok: raise RuntimeError("gg wp") + async def _process_batch(batch: tuple[dict[str, Any], ...], decision: Decision) -> None: uids: list[str] = [record["_id"] for record in batch] log.info(f"Setting {','.join(uids)} as {decision}") diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 3daa1019..77fc31dc 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -127,6 +127,7 @@ def test_no_decision_from_no_reviews() -> None: admin._include_review_decision(record) assert record["decision"] is None + def test_confirm_attendance_when_status_confirmed() -> None: """Test that a decision is None for an applicant with no reviews.""" record = { @@ -138,6 +139,7 @@ def test_confirm_attendance_when_status_confirmed() -> None: admin._set_confirmations(record) assert record["status"] == "ATTENDING" + def test_confirm_attendance_when_status_accepted() -> None: """Test that a decision is None for an applicant with no reviews.""" record = { @@ -149,6 +151,7 @@ def test_confirm_attendance_when_status_accepted() -> None: admin._set_confirmations(record) assert record["status"] == "VOID" + def test_confirm_attendance_when_status_waiver_signed() -> None: """Test that a decision is None for an applicant with no reviews.""" record = { From 057993ca1c66355aaa4e288ea612e390e5253021 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:58:42 -0800 Subject: [PATCH 03/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 609e120e..04aa9b78 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -131,9 +131,8 @@ async def release_decisions() -> None: @router.post( - "/confirm-attendance", dependencies= - [Depends(require_role([Role.DIRECTOR]))] - ) + "/confirm-attendance", dependencies=[Depends(require_role([Role.DIRECTOR]))] +) async def confirm_attendance() -> None: """Update applicant status to void or attending based on their current status.""" records = await mongodb_handler.retrieve( From 0a955805e92ade5fc945d98a7af516a1965a8387 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 00:30:12 -0800 Subject: [PATCH 04/22] simplified or logic --- apps/api/src/routers/admin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 04aa9b78..07b73554 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -202,7 +202,5 @@ def _set_confirmations(applicant_record: dict[str, Any]) -> None: status = applicant_record["status"] if status == Status.CONFIRMED: applicant_record["status"] = Status.ATTENDING - elif status == Status.WAIVER_SIGNED: - applicant_record["status"] = Status.VOID - elif status == Decision.ACCEPTED: + elif status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): applicant_record["status"] = Status.VOID \ No newline at end of file From 732660fe81d5f1cf6bc9de552ccdb8fa44df5073 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:10:31 -0800 Subject: [PATCH 05/22] only update now for needed values --- apps/api/src/routers/admin.py | 43 +++++++++++++++------- apps/api/tests/test_admin.py | 68 +++++++++++++++++------------------ 2 files changed, 65 insertions(+), 46 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 17506579..5978ba3f 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -141,14 +141,33 @@ async def confirm_attendance() -> None: ["_id", "status"], ) - for record in records: - _set_confirmations(record) + confirmed_record = [record for record in records if record["status"] == Status.CONFIRMED] + for record in confirmed_record: + record["status"] = Status.ATTENDING + + await asyncio.gather( + *(_process_status(batch, Status.ATTENDING) for batch in batched(confirmed_record, 100)) + ) + + for initial_status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): + void_record = [record for record in records if record["status"] == initial_status] - for cur_status in (Status.ATTENDING, Status.VOID): - group = [record for record in records if record["status"] == cur_status] + for record in void_record: + record["status"] = Status.VOID + await asyncio.gather( - *(_process_status(batch, cur_status) for batch in batched(group, 100)) + *(_process_status(batch, Status.VOID) for batch in batched(void_record, 100)) ) + + + # for record in records: + # _set_confirmations(record) + + # for cur_status in (Status.ATTENDING, Status.VOID): + # group = [record for record in records if record["status"] == cur_status] + # await asyncio.gather( + # *(_process_status(batch, cur_status) for batch in batched(group, 100)) + # ) async def _process_status(batch: tuple[dict[str, Any], ...], status: Status) -> None: @@ -197,11 +216,11 @@ def _include_review_decision(applicant_record: dict[str, Any]) -> None: applicant_record["decision"] = reviews[-1][2] if reviews else None -def _set_confirmations(applicant_record: dict[str, Any]) -> None: - """Sets the applicant's status based on their RSVP status""" +# def _set_confirmations(applicant_record: dict[str, Any]) -> None: +# """Sets the applicant's status based on their RSVP status""" - status = applicant_record["status"] - if status == Status.CONFIRMED: - applicant_record["status"] = Status.ATTENDING - elif status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): - applicant_record["status"] = Status.VOID \ No newline at end of file +# status = applicant_record["status"] +# if status == Status.CONFIRMED: +# applicant_record["status"] = Status.ATTENDING +# elif status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): +# applicant_record["status"] = Status.VOID \ No newline at end of file diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 77fc31dc..3867ce1e 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -128,40 +128,40 @@ def test_no_decision_from_no_reviews() -> None: assert record["decision"] is None -def test_confirm_attendance_when_status_confirmed() -> None: - """Test that a decision is None for an applicant with no reviews.""" - record = { - "_id": "edu.uci.Hey", - "status": "CONFIRMED", - "decision": "ACCEPTED", - } - - admin._set_confirmations(record) - assert record["status"] == "ATTENDING" - - -def test_confirm_attendance_when_status_accepted() -> None: - """Test that a decision is None for an applicant with no reviews.""" - record = { - "_id": "edu.uci.Vsauce", - "status": "ACCEPTED", - "decision": "ACCEPTED", - } - - admin._set_confirmations(record) - assert record["status"] == "VOID" - - -def test_confirm_attendance_when_status_waiver_signed() -> None: - """Test that a decision is None for an applicant with no reviews.""" - record = { - "_id": "edu.uci.Michael", - "status": "WAIVER_SIGNED", - "decision": "ACCEPTED", - } - - admin._set_confirmations(record) - assert record["status"] == "VOID" +# def test_confirm_attendance_when_status_confirmed() -> None: +# """Test that a decision is None for an applicant with no reviews.""" +# record = { +# "_id": "edu.uci.Hey", +# "status": "CONFIRMED", +# "decision": "ACCEPTED", +# } + +# admin._set_confirmations(record) +# assert record["status"] == "ATTENDING" + + +# def test_confirm_attendance_when_status_accepted() -> None: +# """Test that a decision is None for an applicant with no reviews.""" +# record = { +# "_id": "edu.uci.Vsauce", +# "status": "ACCEPTED", +# "decision": "ACCEPTED", +# } + +# admin._set_confirmations(record) +# assert record["status"] == "VOID" + + +# def test_confirm_attendance_when_status_waiver_signed() -> None: +# """Test that a decision is None for an applicant with no reviews.""" +# record = { +# "_id": "edu.uci.Michael", +# "status": "WAIVER_SIGNED", +# "decision": "ACCEPTED", +# } + +# admin._set_confirmations(record) +# assert record["status"] == "VOID" @patch("services.mongodb_handler.raw_update_one", autospec=True) From 60cd61519667ad329118de2fd56a0b9ad939cf73 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:13:16 -0800 Subject: [PATCH 06/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 5978ba3f..eb61f335 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -141,7 +141,9 @@ async def confirm_attendance() -> None: ["_id", "status"], ) - confirmed_record = [record for record in records if record["status"] == Status.CONFIRMED] + confirmed_record = [ + record for record in records if record["status"] == Status.CONFIRMED + ] for record in confirmed_record: record["status"] = Status.ATTENDING From fd89c4853052761856a9d131ce049b6c12e66b2b Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:13:23 -0800 Subject: [PATCH 07/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index eb61f335..f0d92e28 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -148,9 +148,12 @@ async def confirm_attendance() -> None: record["status"] = Status.ATTENDING await asyncio.gather( - *(_process_status(batch, Status.ATTENDING) for batch in batched(confirmed_record, 100)) + *( + _process_status(batch, Status.ATTENDING) + for batch in batched(confirmed_record, 100) + ) ) - + for initial_status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): void_record = [record for record in records if record["status"] == initial_status] From 8d098eac25bdbe71c0e6036d10a6bb759c9a769f Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:13:33 -0800 Subject: [PATCH 08/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index f0d92e28..8cf296d1 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -155,7 +155,9 @@ async def confirm_attendance() -> None: ) for initial_status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): - void_record = [record for record in records if record["status"] == initial_status] + void_record = [ + record for record in records if record["status"] == initial_status + ] for record in void_record: record["status"] = Status.VOID From d02388ea5183b88a623e68b38cc4706ebd5edf86 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:13:39 -0800 Subject: [PATCH 09/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 8cf296d1..5ed61784 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -161,7 +161,6 @@ async def confirm_attendance() -> None: for record in void_record: record["status"] = Status.VOID - await asyncio.gather( *(_process_status(batch, Status.VOID) for batch in batched(void_record, 100)) ) From fed9e0fbe8a036305ee25e9255da89c95d5d6be7 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:13:54 -0800 Subject: [PATCH 10/22] Update apps/api/src/routers/admin.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/api/src/routers/admin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 5ed61784..1dc4ae7d 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -162,10 +162,12 @@ async def confirm_attendance() -> None: for record in void_record: record["status"] = Status.VOID await asyncio.gather( - *(_process_status(batch, Status.VOID) for batch in batched(void_record, 100)) + *( + _process_status(batch, Status.VOID) + for batch in batched(void_record, 100) + ) ) - - + # for record in records: # _set_confirmations(record) From 627faf284318d173dcdc09e27661eeb03b95a112 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:21:39 -0800 Subject: [PATCH 11/22] incomplete testing --- apps/api/src/routers/admin.py | 2 +- apps/api/tests/test_admin.py | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 1dc4ae7d..7d42c5be 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -231,4 +231,4 @@ def _include_review_decision(applicant_record: dict[str, Any]) -> None: # if status == Status.CONFIRMED: # applicant_record["status"] = Status.ATTENDING # elif status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): -# applicant_record["status"] = Status.VOID \ No newline at end of file +# applicant_record["status"] = Status.VOID diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 3867ce1e..4ae972d1 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -30,11 +30,26 @@ "role": "reviewer", } +USER_DIRECTOR = NativeUser( + ucinetid="dir", + display_name="Dir", + email="dir@uci.edu", + affiliations=["student"], +) + +DIRECTOR_IDENTITY = { + "_id": "edu.uci.dir", + "role": "director", + "status": "CONFIRMED" +} + app = FastAPI() app.include_router(admin.router) reviewer_client = UserTestClient(USER_REVIEWER, app) +director_client = UserTestClient(USER_DIRECTOR, app) + @patch("services.mongodb_handler.retrieve_one", autospec=True) def test_restricted_admin_route_is_forbidden( @@ -128,6 +143,35 @@ def test_no_decision_from_no_reviews() -> None: assert record["decision"] is None +@patch("services.mongodb_handler.update", autospec=True) +@patch("services.mongodb_handler.retrieve", autospec=True) +def test_confirm_attendance_when_status_confirmed( + mock_mongodb_handler_retrieve_one: AsyncMock, + mock_mongodb_handler_retrieve: AsyncMock, +) -> None: + """Test that confirmed status changes to attending.""" + + mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY + mock_mongodb_handler_retrieve.return_value = { + "_id": "edu.uc.dir", + "role": "director", + "status": "CONFIRMED", + } + + res = director_client.post("/confirm-attendance") + + # # mock_mongodb_handler_retrieve.assert_awaited_once() + assert res.status_code == 200 + data = res.json() + print(data) + assert data == [ + { + "_id": "edu.uci.dir", + "status": "ATTENDING", + }, + ] + + # def test_confirm_attendance_when_status_confirmed() -> None: # """Test that a decision is None for an applicant with no reviews.""" # record = { From b46da838fc10e46cb8bd7709e5f19cf5b4420384 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:55:09 -0800 Subject: [PATCH 12/22] new testing method --- apps/api/tests/test_admin.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 4ae972d1..75d168b2 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -8,6 +8,7 @@ from models.ApplicationData import Decision from routers import admin from services.mongodb_handler import Collection +from utils.user_record import Status user_identity.JWT_SECRET = "not a good idea" @@ -144,32 +145,34 @@ def test_no_decision_from_no_reviews() -> None: @patch("services.mongodb_handler.update", autospec=True) +@patch("services.mongodb_handler.retrieve_one", autospec=True) @patch("services.mongodb_handler.retrieve", autospec=True) def test_confirm_attendance_when_status_confirmed( - mock_mongodb_handler_retrieve_one: AsyncMock, mock_mongodb_handler_retrieve: AsyncMock, + mock_mongodb_handler_retrieve_one: AsyncMock, + mock_mognodb_handler_update: AsyncMock, ) -> None: """Test that confirmed status changes to attending.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = { - "_id": "edu.uc.dir", - "role": "director", - "status": "CONFIRMED", - } + mock_mongodb_handler_retrieve.return_value = [ + { + "_id": "edu.uc.tester", + "role": "applicant", + "status": Status.CONFIRMED, + } + ] res = director_client.post("/confirm-attendance") - # # mock_mongodb_handler_retrieve.assert_awaited_once() + mock_mongodb_handler_retrieve.assert_awaited_once() + mock_mognodb_handler_update.assert_awaited_once_with( + Collection.USERS, + {"_id": {"$in": ["edu.uc.tester"]}}, + {"status": Status.ATTENDING}, + ) + assert res.status_code == 200 - data = res.json() - print(data) - assert data == [ - { - "_id": "edu.uci.dir", - "status": "ATTENDING", - }, - ] # def test_confirm_attendance_when_status_confirmed() -> None: From 56d43c378b56bc967fd88de34568ea2a3c2d3cc8 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:15:09 -0800 Subject: [PATCH 13/22] optimized logic, removed comments --- apps/api/src/routers/admin.py | 56 ++++++++++------------------------- apps/api/tests/test_admin.py | 42 +------------------------- 2 files changed, 16 insertions(+), 82 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 7d42c5be..df0ac5f3 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -141,45 +141,29 @@ async def confirm_attendance() -> None: ["_id", "status"], ) - confirmed_record = [ - record for record in records if record["status"] == Status.CONFIRMED - ] - for record in confirmed_record: - record["status"] = Status.ATTENDING - - await asyncio.gather( - *( - _process_status(batch, Status.ATTENDING) - for batch in batched(confirmed_record, 100) - ) - ) - - for initial_status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): - void_record = [ - record for record in records if record["status"] == initial_status + statuses = { + Status.CONFIRMED: Status.ATTENDING, + Decision.ACCEPTED: Status.VOID, + Status.WAIVER_SIGNED: Status.VOID, + } + + for status_from, status_to in statuses.items(): + current_record = [ + record for record in records if record["status"] == status_from ] - for record in void_record: - record["status"] = Status.VOID + for record in current_record: + record["status"] = status_to + await asyncio.gather( *( - _process_status(batch, Status.VOID) - for batch in batched(void_record, 100) + _process_status(batch, status_to) + for batch in batched([record["_id"] for record in current_record], 100) ) ) - # for record in records: - # _set_confirmations(record) - - # for cur_status in (Status.ATTENDING, Status.VOID): - # group = [record for record in records if record["status"] == cur_status] - # await asyncio.gather( - # *(_process_status(batch, cur_status) for batch in batched(group, 100)) - # ) - -async def _process_status(batch: tuple[dict[str, Any], ...], status: Status) -> None: - uids: list[str] = [record["_id"] for record in batch] +async def _process_status(uids: tuple[str, ...], status: Status) -> None: ok = await mongodb_handler.update( Collection.USERS, {"_id": {"$in": uids}}, {"status": status} ) @@ -222,13 +206,3 @@ def _include_review_decision(applicant_record: dict[str, Any]) -> None: """Sets the applicant's decision as the last submitted review decision or None.""" reviews = applicant_record["application_data"]["reviews"] applicant_record["decision"] = reviews[-1][2] if reviews else None - - -# def _set_confirmations(applicant_record: dict[str, Any]) -> None: -# """Sets the applicant's status based on their RSVP status""" - -# status = applicant_record["status"] -# if status == Status.CONFIRMED: -# applicant_record["status"] = Status.ATTENDING -# elif status in (Status.WAIVER_SIGNED, Decision.ACCEPTED): -# applicant_record["status"] = Status.VOID diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 75d168b2..a559551f 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -38,11 +38,7 @@ affiliations=["student"], ) -DIRECTOR_IDENTITY = { - "_id": "edu.uci.dir", - "role": "director", - "status": "CONFIRMED" -} +DIRECTOR_IDENTITY = {"_id": "edu.uci.dir", "role": "director", "status": "CONFIRMED"} app = FastAPI() app.include_router(admin.router) @@ -175,42 +171,6 @@ def test_confirm_attendance_when_status_confirmed( assert res.status_code == 200 -# def test_confirm_attendance_when_status_confirmed() -> None: -# """Test that a decision is None for an applicant with no reviews.""" -# record = { -# "_id": "edu.uci.Hey", -# "status": "CONFIRMED", -# "decision": "ACCEPTED", -# } - -# admin._set_confirmations(record) -# assert record["status"] == "ATTENDING" - - -# def test_confirm_attendance_when_status_accepted() -> None: -# """Test that a decision is None for an applicant with no reviews.""" -# record = { -# "_id": "edu.uci.Vsauce", -# "status": "ACCEPTED", -# "decision": "ACCEPTED", -# } - -# admin._set_confirmations(record) -# assert record["status"] == "VOID" - - -# def test_confirm_attendance_when_status_waiver_signed() -> None: -# """Test that a decision is None for an applicant with no reviews.""" -# record = { -# "_id": "edu.uci.Michael", -# "status": "WAIVER_SIGNED", -# "decision": "ACCEPTED", -# } - -# admin._set_confirmations(record) -# assert record["status"] == "VOID" - - @patch("services.mongodb_handler.raw_update_one", autospec=True) @patch("services.mongodb_handler.retrieve_one", autospec=True) def test_can_submit_review( From f2bd56ec535e4fc26d345e1d7a75e51506a32fe2 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:34:48 -0800 Subject: [PATCH 14/22] updated typing --- apps/api/src/routers/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index df0ac5f3..4558eb69 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -163,7 +163,7 @@ async def confirm_attendance() -> None: ) -async def _process_status(uids: tuple[str, ...], status: Status) -> None: +async def _process_status(uids: tuple[object, ...], status: Status) -> None: ok = await mongodb_handler.update( Collection.USERS, {"_id": {"$in": uids}}, {"status": status} ) From 946f302bd0a81c478fda03b71be46979b970addf Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:38:13 -0800 Subject: [PATCH 15/22] updated tests for 3 cases --- apps/api/tests/test_admin.py | 64 +++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index a559551f..36b49231 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -164,13 +164,75 @@ def test_confirm_attendance_when_status_confirmed( mock_mongodb_handler_retrieve.assert_awaited_once() mock_mognodb_handler_update.assert_awaited_once_with( Collection.USERS, - {"_id": {"$in": ["edu.uc.tester"]}}, + {"_id": {"$in": ("edu.uc.tester",)}}, {"status": Status.ATTENDING}, ) assert res.status_code == 200 +@patch("services.mongodb_handler.update", autospec=True) +@patch("services.mongodb_handler.retrieve_one", autospec=True) +@patch("services.mongodb_handler.retrieve", autospec=True) +def test_confirm_attendance_when_status_waiver_signed( + mock_mongodb_handler_retrieve: AsyncMock, + mock_mongodb_handler_retrieve_one: AsyncMock, + mock_mognodb_handler_update: AsyncMock, +) -> None: + """Test that confirmed status changes to attending.""" + + mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY + mock_mongodb_handler_retrieve.return_value = [ + { + "_id": "edu.uc.tester", + "role": "applicant", + "status": Status.WAIVER_SIGNED, + } + ] + + res = director_client.post("/confirm-attendance") + + mock_mongodb_handler_retrieve.assert_awaited_once() + mock_mognodb_handler_update.assert_awaited_once_with( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester",)}}, + {"status": Status.VOID}, + ) + + assert res.status_code == 200 + + +@patch("services.mongodb_handler.update", autospec=True) +@patch("services.mongodb_handler.retrieve_one", autospec=True) +@patch("services.mongodb_handler.retrieve", autospec=True) +def test_confirm_attendance_when_status_accepted( + mock_mongodb_handler_retrieve: AsyncMock, + mock_mongodb_handler_retrieve_one: AsyncMock, + mock_mognodb_handler_update: AsyncMock, +) -> None: + """Test that confirmed status changes to attending.""" + + mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY + mock_mongodb_handler_retrieve.return_value = [ + { + "_id": "edu.uc.tester", + "role": "applicant", + "status": Decision.ACCEPTED, + } + ] + + res = director_client.post("/confirm-attendance") + + mock_mongodb_handler_retrieve.assert_awaited_once() + mock_mognodb_handler_update.assert_awaited_once_with( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester",)}}, + {"status": Status.VOID}, + ) + + assert res.status_code == 200 + + @patch("services.mongodb_handler.raw_update_one", autospec=True) @patch("services.mongodb_handler.retrieve_one", autospec=True) def test_can_submit_review( From a182b45cd58314a15a7759fa627f941c5f1987ef Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:39:34 -0800 Subject: [PATCH 16/22] test description fix --- apps/api/tests/test_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 36b49231..1af2a209 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -179,7 +179,7 @@ def test_confirm_attendance_when_status_waiver_signed( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to attending.""" + """Test that confirmed status changes to void.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ @@ -210,7 +210,7 @@ def test_confirm_attendance_when_status_accepted( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to attending.""" + """Test that confirmed status changes to void.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ From d797660636536bd47b310c71fcd3647fcc5b74b5 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:42:31 -0800 Subject: [PATCH 17/22] changed test messages --- apps/api/tests/test_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 1af2a209..41bc4b07 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -148,7 +148,7 @@ def test_confirm_attendance_when_status_confirmed( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to attending.""" + """Test that confirmed status changes to attending with confirmed.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ @@ -179,7 +179,7 @@ def test_confirm_attendance_when_status_waiver_signed( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to void.""" + """Test that confirmed status changes to void with waiver signed.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ @@ -210,7 +210,7 @@ def test_confirm_attendance_when_status_accepted( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to void.""" + """Test that confirmed status changes to void with accepted.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ From de356ae608de381676264adf878720f00adad5d0 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 22:51:33 -0800 Subject: [PATCH 18/22] updated typing to Sequence --- apps/api/src/routers/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 3cd584dc..1efc85a4 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -1,7 +1,7 @@ import asyncio from datetime import datetime from logging import getLogger -from typing import Any, Optional +from typing import Any, Optional, Sequence from fastapi import APIRouter, Body, Depends, HTTPException, status from pydantic import BaseModel, EmailStr, Field, TypeAdapter, ValidationError @@ -165,7 +165,7 @@ async def confirm_attendance() -> None: ) -async def _process_status(uids: tuple[object, ...], status: Status) -> None: +async def _process_status(uids: Sequence[str], status: Status) -> None: ok = await mongodb_handler.update( Collection.USERS, {"_id": {"$in": uids}}, {"status": status} ) From b766dec0689919692b2d120e1314b880d5fec498 Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 23:08:03 -0800 Subject: [PATCH 19/22] fixed typing for Sequence --- apps/api/src/routers/admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 1efc85a4..7bf6c51b 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -160,7 +160,9 @@ async def confirm_attendance() -> None: await asyncio.gather( *( _process_status(batch, status_to) - for batch in batched([record["_id"] for record in current_record], 100) + for batch in batched( + [str(record["_id"]) for record in current_record], 100 + ) ) ) From 2b6eb17aea26f240c2da530f3056820ac29e1b7b Mon Sep 17 00:00:00 2001 From: Bl20052005 <88120481+Bl20052005@users.noreply.github.com> Date: Sat, 20 Jan 2024 23:21:26 -0800 Subject: [PATCH 20/22] combined all tests into one --- apps/api/tests/test_admin.py | 105 +++++++++-------------------------- 1 file changed, 26 insertions(+), 79 deletions(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 41bc4b07..bf42efa6 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -148,89 +148,36 @@ def test_confirm_attendance_when_status_confirmed( mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to attending with confirmed.""" - - mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = [ - { - "_id": "edu.uc.tester", - "role": "applicant", - "status": Status.CONFIRMED, - } - ] - - res = director_client.post("/confirm-attendance") - - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.ATTENDING}, - ) - - assert res.status_code == 200 - - -@patch("services.mongodb_handler.update", autospec=True) -@patch("services.mongodb_handler.retrieve_one", autospec=True) -@patch("services.mongodb_handler.retrieve", autospec=True) -def test_confirm_attendance_when_status_waiver_signed( - mock_mongodb_handler_retrieve: AsyncMock, - mock_mongodb_handler_retrieve_one: AsyncMock, - mock_mognodb_handler_update: AsyncMock, -) -> None: - """Test that confirmed status changes to void with waiver signed.""" - - mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = [ - { - "_id": "edu.uc.tester", - "role": "applicant", - "status": Status.WAIVER_SIGNED, - } - ] - - res = director_client.post("/confirm-attendance") - - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.VOID}, - ) - - assert res.status_code == 200 - - -@patch("services.mongodb_handler.update", autospec=True) -@patch("services.mongodb_handler.retrieve_one", autospec=True) -@patch("services.mongodb_handler.retrieve", autospec=True) -def test_confirm_attendance_when_status_accepted( - mock_mongodb_handler_retrieve: AsyncMock, - mock_mongodb_handler_retrieve_one: AsyncMock, - mock_mognodb_handler_update: AsyncMock, -) -> None: - """Test that confirmed status changes to void with accepted.""" + """Test that confirmed statuses change according to the rules set + that confirmed will become attending, accepted and waiver signed + will become void.""" + + statuses = { + Status.CONFIRMED: Status.ATTENDING, + Decision.ACCEPTED: Status.VOID, + Status.WAIVER_SIGNED: Status.VOID, + } - mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = [ - { - "_id": "edu.uc.tester", - "role": "applicant", - "status": Decision.ACCEPTED, - } - ] + for current_status in (Status.CONFIRMED, Status.WAIVER_SIGNED, Decision.ACCEPTED): + mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY + mock_mongodb_handler_retrieve.return_value = [ + { + "_id": "edu.uc.tester", + "role": "applicant", + "status": current_status, + }, + ] - res = director_client.post("/confirm-attendance") + res = director_client.post("/confirm-attendance") - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.VOID}, - ) + mock_mognodb_handler_update.assert_awaited() + mock_mognodb_handler_update.assert_awaited_with( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester",)}}, + {"status": statuses[current_status]}, + ) - assert res.status_code == 200 + assert res.status_code == 200 @patch("services.mongodb_handler.raw_update_one", autospec=True) From dd1c0f79a0f2afa51c2bab390988fc3fffd3bff7 Mon Sep 17 00:00:00 2001 From: Sam Der Date: Sat, 20 Jan 2024 23:45:13 -0800 Subject: [PATCH 21/22] pytest refactoring --- apps/api/tests/test_admin.py | 104 +++++++++++++---------------------- 1 file changed, 37 insertions(+), 67 deletions(-) diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index 41bc4b07..f4dbd2df 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -1,5 +1,5 @@ from datetime import datetime -from unittest.mock import ANY, AsyncMock, patch +from unittest.mock import ANY, AsyncMock, call, patch from fastapi import FastAPI @@ -143,91 +143,61 @@ def test_no_decision_from_no_reviews() -> None: @patch("services.mongodb_handler.update", autospec=True) @patch("services.mongodb_handler.retrieve_one", autospec=True) @patch("services.mongodb_handler.retrieve", autospec=True) -def test_confirm_attendance_when_status_confirmed( +def test_confirm_attendance_route( mock_mongodb_handler_retrieve: AsyncMock, mock_mongodb_handler_retrieve_one: AsyncMock, mock_mognodb_handler_update: AsyncMock, ) -> None: - """Test that confirmed status changes to attending with confirmed.""" + """Test that confirmed status changes to void with accepted.""" mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY mock_mongodb_handler_retrieve.return_value = [ { "_id": "edu.uc.tester", "role": "applicant", - "status": Status.CONFIRMED, - } - ] - - res = director_client.post("/confirm-attendance") - - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.ATTENDING}, - ) - - assert res.status_code == 200 - - -@patch("services.mongodb_handler.update", autospec=True) -@patch("services.mongodb_handler.retrieve_one", autospec=True) -@patch("services.mongodb_handler.retrieve", autospec=True) -def test_confirm_attendance_when_status_waiver_signed( - mock_mongodb_handler_retrieve: AsyncMock, - mock_mongodb_handler_retrieve_one: AsyncMock, - mock_mognodb_handler_update: AsyncMock, -) -> None: - """Test that confirmed status changes to void with waiver signed.""" - - mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = [ + "status": Decision.ACCEPTED, + }, { - "_id": "edu.uc.tester", + "_id": "edu.uc.tester2", "role": "applicant", "status": Status.WAIVER_SIGNED, - } - ] - - res = director_client.post("/confirm-attendance") - - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.VOID}, - ) - - assert res.status_code == 200 - - -@patch("services.mongodb_handler.update", autospec=True) -@patch("services.mongodb_handler.retrieve_one", autospec=True) -@patch("services.mongodb_handler.retrieve", autospec=True) -def test_confirm_attendance_when_status_accepted( - mock_mongodb_handler_retrieve: AsyncMock, - mock_mongodb_handler_retrieve_one: AsyncMock, - mock_mognodb_handler_update: AsyncMock, -) -> None: - """Test that confirmed status changes to void with accepted.""" - - mock_mongodb_handler_retrieve_one.return_value = DIRECTOR_IDENTITY - mock_mongodb_handler_retrieve.return_value = [ + }, { - "_id": "edu.uc.tester", + "_id": "edu.uc.tester3", "role": "applicant", - "status": Decision.ACCEPTED, - } + "status": Status.CONFIRMED, + }, + { + "_id": "edu.uc.tester4", + "role": "applicant", + "status": Decision.WAITLISTED, + }, ] res = director_client.post("/confirm-attendance") - mock_mongodb_handler_retrieve.assert_awaited_once() - mock_mognodb_handler_update.assert_awaited_once_with( - Collection.USERS, - {"_id": {"$in": ("edu.uc.tester",)}}, - {"status": Status.VOID}, + mock_mongodb_handler_retrieve.assert_awaited() + mock_mognodb_handler_update.assert_has_calls( + [ + call( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester3",)}}, + {"status": Status.ATTENDING}, + ), + call().__bool__(), + call( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester",)}}, + {"status": Status.VOID}, + ), + call().__bool__(), + call( + Collection.USERS, + {"_id": {"$in": ("edu.uc.tester2",)}}, + {"status": Status.VOID}, + ), + call().__bool__(), + ] ) assert res.status_code == 200 From 456cf53d22a7ac7787d61807ca48b61f58d75755 Mon Sep 17 00:00:00 2001 From: Sam Der Date: Sat, 20 Jan 2024 23:58:22 -0800 Subject: [PATCH 22/22] reorder test and route --- apps/api/src/routers/admin.py | 66 +++++++++++++++++------------------ apps/api/tests/test_admin.py | 58 +++++++++++++++--------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/apps/api/src/routers/admin.py b/apps/api/src/routers/admin.py index 7bf6c51b..c92a0488 100644 --- a/apps/api/src/routers/admin.py +++ b/apps/api/src/routers/admin.py @@ -132,6 +132,39 @@ async def release_decisions() -> None: ) +@router.post("/rsvp-reminder", dependencies=[Depends(require_role([Role.DIRECTOR]))]) +async def rsvp_reminder() -> None: + """Send email to applicants who have a status of ACCEPTED or WAIVER_SIGNED + reminding them to RSVP.""" + # TODO: Consider using Pydantic model validation instead of type annotations + not_yet_rsvpd: list[dict[str, Any]] = await mongodb_handler.retrieve( + Collection.USERS, + {"status": {"$in": [Decision.ACCEPTED, Status.WAIVER_SIGNED]}}, + ["_id", "application_data.first_name"], + ) + + personalizations = [] + for record in not_yet_rsvpd: + if "application_data" not in record: + continue + personalizations.append( + ApplicationUpdatePersonalization( + email=_recover_email_from_uid(record["_id"]), + first_name=record["application_data"]["first_name"], + ) + ) + + log.info(f"Sending RSVP reminder emails to {len(not_yet_rsvpd)} applicants") + + await sendgrid_handler.send_email( + Template.RSVP_REMINDER, + IH_SENDER, + personalizations, + True, + reply_to=REPLY_TO_HACK_AT_UCI, + ) + + @router.post( "/confirm-attendance", dependencies=[Depends(require_role([Role.DIRECTOR]))] ) @@ -175,39 +208,6 @@ async def _process_status(uids: Sequence[str], status: Status) -> None: raise RuntimeError("gg wp") -@router.post("/rsvp-reminder", dependencies=[Depends(require_role([Role.DIRECTOR]))]) -async def rsvp_reminder() -> None: - """Send email to applicants who have a status of ACCEPTED or WAIVER_SIGNED - reminding them to RSVP.""" - # TODO: Consider using Pydantic model validation instead of type annotations - not_yet_rsvpd: list[dict[str, Any]] = await mongodb_handler.retrieve( - Collection.USERS, - {"status": {"$in": [Decision.ACCEPTED, Status.WAIVER_SIGNED]}}, - ["_id", "application_data.first_name"], - ) - - personalizations = [] - for record in not_yet_rsvpd: - if "application_data" not in record: - continue - personalizations.append( - ApplicationUpdatePersonalization( - email=_recover_email_from_uid(record["_id"]), - first_name=record["application_data"]["first_name"], - ) - ) - - log.info(f"Sending RSVP reminder emails to {len(not_yet_rsvpd)} applicants") - - await sendgrid_handler.send_email( - Template.RSVP_REMINDER, - IH_SENDER, - personalizations, - True, - reply_to=REPLY_TO_HACK_AT_UCI, - ) - - async def _process_batch(batch: tuple[dict[str, Any], ...], decision: Decision) -> None: uids: list[str] = [record["_id"] for record in batch] log.info(f"Setting {','.join(uids)} as {decision}") diff --git a/apps/api/tests/test_admin.py b/apps/api/tests/test_admin.py index f4dbd2df..43abcc6d 100644 --- a/apps/api/tests/test_admin.py +++ b/apps/api/tests/test_admin.py @@ -140,6 +140,35 @@ def test_no_decision_from_no_reviews() -> None: assert record["decision"] is None +@patch("services.mongodb_handler.raw_update_one", autospec=True) +@patch("services.mongodb_handler.retrieve_one", autospec=True) +def test_can_submit_review( + mock_mongodb_handler_retrieve_one: AsyncMock, + mock_mongodb_handler_raw_update_one: AsyncMock, +) -> None: + """Test that a user can properly submit an applicant review.""" + + mock_mongodb_handler_retrieve_one.return_value = REVIEWER_IDENTITY + + res = reviewer_client.post( + "/review", + json={"applicant": "edu.uci.applicant", "decision": Decision.ACCEPTED}, + ) + + mock_mongodb_handler_retrieve_one.assert_awaited_once() + mock_mongodb_handler_raw_update_one.assert_awaited_once_with( + Collection.USERS, + {"_id": "edu.uci.applicant"}, + { + "$push": { + "application_data.reviews": (ANY, "edu.uci.alicia", Decision.ACCEPTED) + }, + "$set": {"status": "REVIEWED"}, + }, + ) + assert res.status_code == 200 + + @patch("services.mongodb_handler.update", autospec=True) @patch("services.mongodb_handler.retrieve_one", autospec=True) @patch("services.mongodb_handler.retrieve", autospec=True) @@ -201,32 +230,3 @@ def test_confirm_attendance_route( ) assert res.status_code == 200 - - -@patch("services.mongodb_handler.raw_update_one", autospec=True) -@patch("services.mongodb_handler.retrieve_one", autospec=True) -def test_can_submit_review( - mock_mongodb_handler_retrieve_one: AsyncMock, - mock_mongodb_handler_raw_update_one: AsyncMock, -) -> None: - """Test that a user can properly submit an applicant review.""" - - mock_mongodb_handler_retrieve_one.return_value = REVIEWER_IDENTITY - - res = reviewer_client.post( - "/review", - json={"applicant": "edu.uci.applicant", "decision": Decision.ACCEPTED}, - ) - - mock_mongodb_handler_retrieve_one.assert_awaited_once() - mock_mongodb_handler_raw_update_one.assert_awaited_once_with( - Collection.USERS, - {"_id": "edu.uci.applicant"}, - { - "$push": { - "application_data.reviews": (ANY, "edu.uci.alicia", Decision.ACCEPTED) - }, - "$set": {"status": "REVIEWED"}, - }, - ) - assert res.status_code == 200