Skip to content

Commit

Permalink
Merge pull request #39 from DSAV-Dodeka/feat/ranking-metadata
Browse files Browse the repository at this point in the history
feat: ranking metadata
  • Loading branch information
tiptenbrink authored Jan 4, 2024
2 parents b2af3fe + 20e84e4 commit 26dd5f2
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 27 deletions.
8 changes: 4 additions & 4 deletions backend/actions/local_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ async def test_add_classification(local_dsrc):
@pytest.mark.asyncio
async def test_update_points(local_dsrc):
async with get_conn(local_dsrc) as conn:
training_class = await data.classifications.most_recent_class_of_type(
training_class = (await data.classifications.most_recent_class_of_type(
conn, "training"
)
points_class = await data.classifications.most_recent_class_of_type(
))[0]
points_class = (await data.classifications.most_recent_class_of_type(
conn, "points"
)
))[0]
await update_class_points(conn, training_class.classification_id)
await update_class_points(conn, points_class.classification_id)

Expand Down
8 changes: 6 additions & 2 deletions backend/src/apiserver/app/ops/startup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from apiserver.app.error import AppError, ErrorKeys
from apiserver.data.special import update_class_points
from loguru import logger
from asyncio import sleep
from datetime import date
Expand Down Expand Up @@ -199,8 +200,11 @@ async def initial_population(dsrc: Source, config: Config) -> None:
user_id = await data.user.insert_return_user_id(conn, fake_user)
assert user_id == "1_fakerecord"

await insert_classification(conn, "training")
await insert_classification(conn, "points")
new_training_id = await insert_classification(conn, "training")
new_points_id = await insert_classification(conn, "points")

await update_class_points(conn, new_training_id, False)
await update_class_points(conn, new_points_id, False)


async def get_keystate(dsrc: Source) -> KeyState:
Expand Down
80 changes: 78 additions & 2 deletions backend/src/apiserver/app/routers/ranking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,30 @@
mod_user_events_in_class,
)
from apiserver.app.response import RawJSONResponse
from apiserver.data.api.classifications import get_event_user_points
from apiserver.data.api.classifications import (
get_event_user_points,
remove_classification,
)
from apiserver.data import Source
from apiserver.data.context.app_context import RankingContext, conn_wrap
from apiserver.data.context.ranking import (
add_new_event,
add_new_training,
context_modify_class,
context_most_recent_class_points,
context_new_classes,
most_recent_classes,
sync_publish_ranking,
)
from apiserver.lib.logic.ranking import is_rank_type
from apiserver.lib.model.entities import (
ClassEvent,
ClassMetaList,
ClassUpdate,
ClassView,
NewEvent,
NewTrainingEvent,
RankingInfo,
UserEvent,
UserPointsNames,
UserPointsNamesList,
Expand Down Expand Up @@ -75,10 +85,48 @@ async def get_classification(
debug_key="bad_ranking",
)

user_points = await context_most_recent_class_points(ctx, dsrc, rank_type, admin)
user_points = (
await context_most_recent_class_points(ctx, dsrc, rank_type, admin)
).points
return RawJSONResponse(UserPointsNamesList.dump_json(user_points))


async def get_classification_with_info(
dsrc: Source, ctx: RankingContext, rank_type: str, admin: bool = False
) -> RawJSONResponse:
if not is_rank_type(rank_type):
reason = f"Ranking {rank_type} is unknown!"
raise ErrorResponse(
status_code=400,
err_type="invalid_ranking",
err_desc=reason,
debug_key="bad_ranking",
)

ranking_info = await context_most_recent_class_points(ctx, dsrc, rank_type, admin)
return RawJSONResponse(RankingInfo.model_dump_json(ranking_info).encode())


@ranking_members_router.get("/get_with_info/{rank_type}/", response_model=RankingInfo)
async def member_classification_with_info(
rank_type: str, dsrc: SourceDep, app_context: AppContext
) -> RawJSONResponse:
return await get_classification_with_info(
dsrc, app_context.rank_ctx, rank_type, False
)


@ranking_admin_router.get("/get_meta/{recent_number}/", response_model=list[ClassView])
async def get_classifications(
recent_number: int, dsrc: SourceDep, app_context: AppContext
) -> RawJSONResponse:
recent_classes = await most_recent_classes(
app_context.rank_ctx, dsrc, recent_number
)

return RawJSONResponse(ClassMetaList.dump_json(recent_classes))


@ranking_members_router.get("/get/{rank_type}/", response_model=list[UserPointsNames])
async def member_classification(
rank_type: str, dsrc: SourceDep, app_context: AppContext
Expand Down Expand Up @@ -168,3 +216,31 @@ async def get_event_users(
)

return RawJSONResponse(UserPointsNamesList.dump_json(event_users))


@ranking_admin_router.post("/new/")
async def new_classes(
dsrc: SourceDep,
app_context: AppContext,
) -> None:
await context_new_classes(app_context.rank_ctx, dsrc)


@ranking_admin_router.post("/modify/")
async def modify_class(
updated_class: ClassUpdate,
dsrc: SourceDep,
app_context: AppContext,
) -> None:
await context_modify_class(app_context.rank_ctx, dsrc, updated_class)


@ranking_admin_router.post("/remove/{class_id}/")
async def remove_class(
class_id: int,
dsrc: SourceDep,
app_context: AppContext,
) -> None:
await ctxlize_wrap(remove_classification, conn_wrap)(
app_context.rank_ctx, dsrc, class_id
)
46 changes: 38 additions & 8 deletions backend/src/apiserver/data/api/classifications.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from datetime import date, timedelta
from typing import Literal
from schema.model.model import CLASS_END_DATE

from sqlalchemy import RowMapping
from sqlalchemy.ext.asyncio import AsyncConnection

from apiserver.lib.model.entities import (
ClassEvent,
ClassMeta,
ClassMetaList,
ClassUpdate,
Classification,
ClassView,
EventDate,
UserPoints,
UserPointsNames,
Expand Down Expand Up @@ -40,12 +43,15 @@
)
from store.db import (
LiteralDict,
delete_by_column,
get_largest_where,
insert,
insert_many,
insert_return_col,
lit_model,
select_some_join_where,
select_some_where,
update_by_unique,
update_column_by_unique,
)
from store.error import DataError, NoDataError, DbError, DbErrors
Expand All @@ -61,7 +67,7 @@ def parse_user_points(user_points: list[RowMapping]) -> list[UserPointsNames]:

async def insert_classification(
conn: AsyncConnection, class_type: str, start_date: date | None = None
) -> None:
) -> int:
if start_date is None:
start_date = date.today()
new_classification = Classification(
Expand All @@ -71,12 +77,15 @@ async def insert_classification(
end_date=start_date + timedelta(days=30 * 5),
hidden_date=start_date + timedelta(days=30 * 4),
)
await insert(conn, CLASSIFICATION_TABLE, lit_model(new_classification))
return_id: int = await insert_return_col(
conn, CLASSIFICATION_TABLE, lit_model(new_classification), CLASS_ID
)
return return_id


async def most_recent_class_of_type(
conn: AsyncConnection, class_type: Literal["training", "points"]
) -> ClassView:
conn: AsyncConnection, class_type: Literal["training", "points"], amount: int = 1
) -> list[ClassMeta]:
if class_type == "training":
query_class_type = "training"
elif class_type == "points":
Expand All @@ -90,19 +99,26 @@ async def most_recent_class_of_type(
largest_class_list = await get_largest_where(
conn,
CLASSIFICATION_TABLE,
{CLASS_ID, CLASS_LAST_UPDATED, CLASS_START_DATE, CLASS_HIDDEN_DATE},
{
CLASS_ID,
CLASS_TYPE,
CLASS_LAST_UPDATED,
CLASS_START_DATE,
CLASS_HIDDEN_DATE,
CLASS_END_DATE,
},
CLASS_TYPE,
query_class_type,
CLASS_START_DATE,
1,
amount,
)
if len(largest_class_list) == 0:
raise NoDataError(
"No most recent training classification found!",
"no_most_recent_training_class",
)

return ClassView.model_validate(largest_class_list[0])
return ClassMetaList.validate_python(largest_class_list)


async def all_points_in_class(
Expand Down Expand Up @@ -260,3 +276,17 @@ async def class_update_last_updated(
return await update_column_by_unique(
conn, CLASSIFICATION_TABLE, CLASS_LAST_UPDATED, date, CLASS_ID, class_id
)


async def update_classification(conn: AsyncConnection, class_view: ClassUpdate) -> None:
await update_by_unique(
conn,
CLASSIFICATION_TABLE,
lit_model(class_view),
"classification_id",
class_view.classification_id,
)


async def remove_classification(conn: AsyncConnection, class_id: int) -> None:
await delete_by_column(conn, CLASSIFICATION_TABLE, "classification_id", class_id)
21 changes: 20 additions & 1 deletion backend/src/apiserver/data/context/app_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
from apiserver.data import Source
from apiserver.lib.model.entities import (
ClassEvent,
ClassMeta,
ClassUpdate,
NewEvent,
RankingInfo,
UserData,
User,
UserEvent,
Expand Down Expand Up @@ -76,7 +79,7 @@ async def context_most_recent_class_id_of_type(
@classmethod
async def context_most_recent_class_points(
cls, dsrc: Source, rank_type: Literal["points", "training"], is_admin: bool
) -> list[UserPointsNames]:
) -> RankingInfo:
raise ContextNotImpl()

@classmethod
Expand All @@ -101,6 +104,22 @@ async def context_get_event_users(
) -> list[UserPointsNames]:
raise ContextNotImpl()

@classmethod
async def most_recent_classes(
cls, dsrc: Source, amount: int = 10
) -> list[ClassMeta]:
raise ContextNotImpl()

@classmethod
async def context_new_classes(cls, dsrc: Source) -> None:
raise ContextNotImpl()

@classmethod
async def context_modify_class(
cls, dsrc: Source, class_update: ClassUpdate
) -> None:
raise ContextNotImpl()


class AuthorizeAppContext(Context):
@classmethod
Expand Down
Loading

0 comments on commit 26dd5f2

Please sign in to comment.