Skip to content

Commit

Permalink
feat: add functionality to disable voting
Browse files Browse the repository at this point in the history
  • Loading branch information
slashtechno committed Feb 2, 2025
1 parent 872da85 commit c9e8d12
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 8 deletions.
1 change: 1 addition & 0 deletions backend/podium/db/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class EventCreationPayload(BaseModel):

class Event(EventCreationPayload):
id: str
votable: bool = False


# Maybe rename to FullEvent? In the frontend it's OwnedEvent since that's the only time a normal user should see all event information (if they own it)
Expand Down
26 changes: 25 additions & 1 deletion backend/podium/routers/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from requests import HTTPError

from podium.routers.auth import get_current_user
from podium.db.user import CurrentUser
from podium.db.user import CurrentUser, User
from podium import db
from podium.db import (
EventCreationPayload,
Expand Down Expand Up @@ -183,13 +183,16 @@ class Vote(BaseModel):
@model_validator(mode="after")
def validate_projects(self) -> Self:
try:
# TODO: Validate event via Pydantic
self.event = db.events.get(self.event_id)
except HTTPError as e:
raise (
HTTPException(status_code=404, detail="Event not found")
if e.response.status_code == 404
else e
)
if not self.event["fields"].get("votable", False):
raise HTTPException(status_code=400, detail="Event is not votable yet")
if len(self.projects) < 2:
raise HTTPException(
status_code=400, detail="At least 2 projects are required"
Expand Down Expand Up @@ -226,6 +229,27 @@ def validate_projects(self) -> Self:
return self


@router.post("/make-votable")
def make_votable(
event_id: Annotated[str, Query(title="Event ID")],
votable: Annotated[bool, Query(description="Whether the event is votable or not")],
current_user: Annotated[CurrentUser, Depends(get_current_user)],
):
"""
Make an event votable. This means that users can vote for their favorite projects in the event.
"""
user_id = db.user.get_user_record_id_by_email(current_user.email)
if user_id is None:
raise HTTPException(status_code=404, detail="User not found")
user = db.users.get(user_id)
user = User.model_validate({"id": user["id"], **user["fields"]})
# Check if the user is the owner of the event
if event_id not in user.owned_events:
# This also ensures the event exists since it has to exist to be in the user's owned events
raise HTTPException(status_code=403, detail="User is not an owner of the event")

db.events.update(event_id, {"votable": votable})

# @router.post("/{event_id}/vote")
@router.post("/vote")
def vote(vote: Vote, current_user: Annotated[CurrentUser, Depends(get_current_user)]):
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/lib/client/sdk.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createClient, createConfig, type OptionsLegacyParser } from '@hey-api/client-fetch';
import type { RequestLoginRequestLoginPostData, RequestLoginRequestLoginPostError, RequestLoginRequestLoginPostResponse, VerifyTokenVerifyGetData, VerifyTokenVerifyGetError, VerifyTokenVerifyGetResponse, ProtectedRouteProtectedRouteGetError, ProtectedRouteProtectedRouteGetResponse, GetEventEventsEventIdGetData, GetEventEventsEventIdGetError, GetEventEventsEventIdGetResponse, GetAttendingEventsEventsGetError, GetAttendingEventsEventsGetResponse, CreateEventEventsPostData, CreateEventEventsPostError, CreateEventEventsPostResponse, AttendEventEventsAttendPostData, AttendEventEventsAttendPostError, AttendEventEventsAttendPostResponse, VoteEventsVotePostData, VoteEventsVotePostError, VoteEventsVotePostResponse, GetLeaderboardEventsEventIdLeaderboardGetData, GetLeaderboardEventsEventIdLeaderboardGetError, GetLeaderboardEventsEventIdLeaderboardGetResponse, GetEventProjectsEventsEventIdProjectsGetData, GetEventProjectsEventsEventIdProjectsGetError, GetEventProjectsEventsEventIdProjectsGetResponse, GetProjectsProjectsMineGetError, GetProjectsProjectsMineGetResponse, CreateProjectProjectsPostData, CreateProjectProjectsPostError, CreateProjectProjectsPostResponse, JoinProjectProjectsJoinPostData, JoinProjectProjectsJoinPostError, JoinProjectProjectsJoinPostResponse, UpdateProjectProjectsProjectIdPutData, UpdateProjectProjectsProjectIdPutError, UpdateProjectProjectsProjectIdPutResponse, DeleteProjectProjectsProjectIdDeleteData, DeleteProjectProjectsProjectIdDeleteError, DeleteProjectProjectsProjectIdDeleteResponse, GetProjectProjectsProjectIdGetData, GetProjectProjectsProjectIdGetError, GetProjectProjectsProjectIdGetResponse, GetCurrentUserUsersCurrentGetError, GetCurrentUserUsersCurrentGetResponse, CreateUserUsersPostData, CreateUserUsersPostError, CreateUserUsersPostResponse, UserExistsUsersExistsGetData, UserExistsUsersExistsGetError, UserExistsUsersExistsGetResponse } from './types.gen';
import type { RequestLoginRequestLoginPostData, RequestLoginRequestLoginPostError, RequestLoginRequestLoginPostResponse, VerifyTokenVerifyGetData, VerifyTokenVerifyGetError, VerifyTokenVerifyGetResponse, ProtectedRouteProtectedRouteGetError, ProtectedRouteProtectedRouteGetResponse, GetEventEventsEventIdGetData, GetEventEventsEventIdGetError, GetEventEventsEventIdGetResponse, GetAttendingEventsEventsGetError, GetAttendingEventsEventsGetResponse, CreateEventEventsPostData, CreateEventEventsPostError, CreateEventEventsPostResponse, AttendEventEventsAttendPostData, AttendEventEventsAttendPostError, AttendEventEventsAttendPostResponse, MakeVotableEventsMakeVotablePostData, MakeVotableEventsMakeVotablePostError, MakeVotableEventsMakeVotablePostResponse, VoteEventsVotePostData, VoteEventsVotePostError, VoteEventsVotePostResponse, GetLeaderboardEventsEventIdLeaderboardGetData, GetLeaderboardEventsEventIdLeaderboardGetError, GetLeaderboardEventsEventIdLeaderboardGetResponse, GetEventProjectsEventsEventIdProjectsGetData, GetEventProjectsEventsEventIdProjectsGetError, GetEventProjectsEventsEventIdProjectsGetResponse, GetProjectsProjectsMineGetError, GetProjectsProjectsMineGetResponse, CreateProjectProjectsPostData, CreateProjectProjectsPostError, CreateProjectProjectsPostResponse, JoinProjectProjectsJoinPostData, JoinProjectProjectsJoinPostError, JoinProjectProjectsJoinPostResponse, UpdateProjectProjectsProjectIdPutData, UpdateProjectProjectsProjectIdPutError, UpdateProjectProjectsProjectIdPutResponse, DeleteProjectProjectsProjectIdDeleteData, DeleteProjectProjectsProjectIdDeleteError, DeleteProjectProjectsProjectIdDeleteResponse, GetProjectProjectsProjectIdGetData, GetProjectProjectsProjectIdGetError, GetProjectProjectsProjectIdGetResponse, GetCurrentUserUsersCurrentGetError, GetCurrentUserUsersCurrentGetResponse, CreateUserUsersPostData, CreateUserUsersPostError, CreateUserUsersPostResponse, UserExistsUsersExistsGetData, UserExistsUsersExistsGetError, UserExistsUsersExistsGetResponse } from './types.gen';

export const client = createClient(createConfig());

Expand Down Expand Up @@ -85,6 +85,17 @@ export class EventsService {
});
}

/**
* Make Votable
* Make an event votable. This means that users can vote for their favorite projects in the event.
*/
public static makeVotableEventsMakeVotablePost<ThrowOnError extends boolean = false>(options: OptionsLegacyParser<MakeVotableEventsMakeVotablePostData, ThrowOnError>) {
return (options?.client ?? client).post<MakeVotableEventsMakeVotablePostResponse, MakeVotableEventsMakeVotablePostError, ThrowOnError>({
...options,
url: '/events/make-votable'
});
}

/**
* Vote
* Vote for the top 3 projects in an event. The client must provide the event ID and a list of the top 3 projects. If there are less than 20 projects in the event, only the top 2 projects are required.
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/lib/client/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ export type ComplexEvent = {
description?: (string | null);
join_code: string;
id: string;
votable?: boolean;
attendees?: Array<(string)>;
};

export type Event = {
name: string;
description?: (string | null);
id: string;
votable?: boolean;
};

export type EventCreationPayload = {
Expand Down Expand Up @@ -259,6 +261,20 @@ export type AttendEventEventsAttendPostResponse = (unknown);

export type AttendEventEventsAttendPostError = (HTTPValidationError);

export type MakeVotableEventsMakeVotablePostData = {
query: {
event_id: string;
/**
* Whether the event is votable or not
*/
votable: boolean;
};
};

export type MakeVotableEventsMakeVotablePostResponse = (unknown);

export type MakeVotableEventsMakeVotablePostError = (HTTPValidationError);

export type VoteEventsVotePostData = {
body: Vote;
};
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import CreateProject from "$lib/components/CreateProject.svelte";
import AttendEvent from "$lib/components/AttendEvent.svelte";
import { user, signOut } from "$lib/user.svelte";
import Page from "./events/+page.svelte";
</script>

<div class="space-y-8 p-4">
Expand Down Expand Up @@ -44,10 +43,10 @@
</div>
</section>

<section>
<!-- <section>
<h2 class="text-xl font-semibold mb-4">Attend Event</h2>
<AttendEvent />
</section>
</section> -->
{/if}
</div>

Expand Down
41 changes: 38 additions & 3 deletions frontend/src/routes/events/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
<svelte:options runes />

<script lang="ts">
import AttendEvent from "$lib/components/AttendEvent.svelte";
import AttendEvent from "$lib/components/AttendEvent.svelte";
import CreateEvent from "$lib/components/CreateEvent.svelte";
import { EventsService } from "$lib/client";
import type { PageData } from "./$types";
import { handleError } from "$lib/misc";
let { data }: { data: PageData } = $props();
async function onVotableCheck(value: boolean, eventId: string) {
try {
console.log(value, eventId);
await EventsService.makeVotableEventsMakeVotablePost({
query: { votable: value,
event_id: eventId
},
throwOnError: true,
});
} catch (err) {
handleError(err);
}
}
</script>

<div class="space-y-8 p-4">
<section>
<h2 class="text-xl font-semibold mb-4">Create Event</h2>
<CreateEvent />
<CreateEvent />
</section>
<section>
<h2>Events you own</h2>
Expand All @@ -21,6 +37,7 @@
<th>Event Name</th>
<th>Description</th>
<th>Join Code</th>
<th>Votable</th>
</tr>
</thead>
<tbody>
Expand All @@ -29,10 +46,27 @@
<td><a href={`/events/${event.id}`}>{event.name}</a></td>
<td>{event.description}</td>
<!-- <span class="ml-4 bg-base-300 p-1 rounded"></span> -->
<td><a href={`/events/?join_code=${event.join_code}`} data-sveltekit-noscroll>{event.join_code}</a></td>
<td
><a
href={`/events/?join_code=${event.join_code}`}
data-sveltekit-noscroll>{event.join_code}</a
></td
>
<td
><input
type="checkbox"
class="checkbox"
checked={event.votable}
onchange={(e) => onVotableCheck(
(e.target as HTMLInputElement).checked,
event.id
)}
/></td
>
</tr>
{/each}
</tbody>
</table>
</section>
<section>
<h2>Events you are attending</h2>
Expand All @@ -51,6 +85,7 @@
</tr>
{/each}
</tbody>
</table>
</section>
<section>
<h2 class="text-xl font-semibold mb-4">Attend Event</h2>
Expand Down

0 comments on commit c9e8d12

Please sign in to comment.