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

feat: new API route to apply status changes after RSVP deadline #288

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a3b5530
added admin/confirm-attendance and tests
Bl20052005 Jan 20, 2024
5f78b30
formatting changes
Bl20052005 Jan 20, 2024
057993c
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
0a95580
simplified or logic
Bl20052005 Jan 20, 2024
89ee79a
Merge branch 'main' of https://github.com/HackAtUCI/irvinehacks-site-…
Bl20052005 Jan 20, 2024
732660f
only update now for needed values
Bl20052005 Jan 20, 2024
60cd615
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
fd89c48
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
8d098ea
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
d02388e
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
fed9e0f
Update apps/api/src/routers/admin.py
Bl20052005 Jan 20, 2024
627faf2
incomplete testing
Bl20052005 Jan 21, 2024
b46da83
new testing method
Bl20052005 Jan 21, 2024
56d43c3
optimized logic, removed comments
Bl20052005 Jan 21, 2024
f2bd56e
updated typing
Bl20052005 Jan 21, 2024
946f302
updated tests for 3 cases
Bl20052005 Jan 21, 2024
a182b45
test description fix
Bl20052005 Jan 21, 2024
d797660
changed test messages
Bl20052005 Jan 21, 2024
e330935
Merge branch 'main' of https://github.com/HackAtUCI/irvinehacks-site-…
Bl20052005 Jan 21, 2024
de356ae
updated typing to Sequence
Bl20052005 Jan 21, 2024
b766dec
fixed typing for Sequence
Bl20052005 Jan 21, 2024
2b6eb17
combined all tests into one
Bl20052005 Jan 21, 2024
dd1c0f7
pytest refactoring
samderanova Jan 21, 2024
aac96e9
Merge branch '275-create-new-admin-route-to-apply-status-changes-afte…
samderanova Jan 21, 2024
456cf53
reorder test and route
samderanova Jan 21, 2024
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
46 changes: 44 additions & 2 deletions apps/api/src/routers/admin.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -127,7 +127,6 @@ 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))
)
Expand Down Expand Up @@ -166,6 +165,49 @@ async def rsvp_reminder() -> None:
)


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

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
]

Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
for record in current_record:
record["status"] = status_to

await asyncio.gather(
*(
_process_status(batch, status_to)
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
for batch in batched(
[str(record["_id"]) for record in current_record], 100
)
)
)

Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

async def _process_status(uids: Sequence[str], status: Status) -> None:
ok = await mongodb_handler.update(
Collection.USERS, {"_id": {"$in": uids}}, {"status": status}
)
if not ok:
raise RuntimeError("gg wp")

Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

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}")
Expand Down
77 changes: 76 additions & 1 deletion apps/api/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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"

Expand All @@ -30,11 +31,22 @@
"role": "reviewer",
}

USER_DIRECTOR = NativeUser(
ucinetid="dir",
display_name="Dir",
email="[email protected]",
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(
Expand Down Expand Up @@ -155,3 +167,66 @@ def test_can_submit_review(
},
)
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_route(
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",
"role": "applicant",
"status": Decision.ACCEPTED,
},
{
"_id": "edu.uc.tester2",
"role": "applicant",
"status": Status.WAIVER_SIGNED,
},
{
"_id": "edu.uc.tester3",
"role": "applicant",
"status": Status.CONFIRMED,
},
{
"_id": "edu.uc.tester4",
"role": "applicant",
"status": Decision.WAITLISTED,
},
]

res = director_client.post("/confirm-attendance")

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