Add events in frontend
parent
2c5cf3f4ca
commit
eee3241cae
|
@ -13,8 +13,11 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||||
export type { AbstractTeamIntegrationSchema } from './models/AbstractTeamIntegrationSchema';
|
export type { AbstractTeamIntegrationSchema } from './models/AbstractTeamIntegrationSchema';
|
||||||
export type { AddPlayerJson } from './models/AddPlayerJson';
|
export type { AddPlayerJson } from './models/AddPlayerJson';
|
||||||
export type { AvailabilitySchema } from './models/AvailabilitySchema';
|
export type { AvailabilitySchema } from './models/AvailabilitySchema';
|
||||||
|
export type { CreateEventJson } from './models/CreateEventJson';
|
||||||
export type { CreateTeamJson } from './models/CreateTeamJson';
|
export type { CreateTeamJson } from './models/CreateTeamJson';
|
||||||
export type { EditMemberRolesJson } from './models/EditMemberRolesJson';
|
export type { EditMemberRolesJson } from './models/EditMemberRolesJson';
|
||||||
|
export type { EventSchema } from './models/EventSchema';
|
||||||
|
export type { EventSchemaList } from './models/EventSchemaList';
|
||||||
export type { PlayerSchema } from './models/PlayerSchema';
|
export type { PlayerSchema } from './models/PlayerSchema';
|
||||||
export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvailabilityRoleSchema';
|
export type { PlayerTeamAvailabilityRoleSchema } from './models/PlayerTeamAvailabilityRoleSchema';
|
||||||
export type { PutScheduleForm } from './models/PutScheduleForm';
|
export type { PutScheduleForm } from './models/PutScheduleForm';
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type CreateEventJson = {
|
||||||
|
description: string;
|
||||||
|
name: string;
|
||||||
|
playerIds: Array<number>;
|
||||||
|
startTime: string;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export type EventSchema = {
|
||||||
|
createdAt: string;
|
||||||
|
description: string;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
startTime: string;
|
||||||
|
teamId: number;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* generated using openapi-typescript-codegen -- do not edit */
|
||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { EventSchema } from './EventSchema';
|
||||||
|
export type EventSchemaList = Array<EventSchema>;
|
|
@ -4,8 +4,11 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import type { AbstractTeamIntegrationSchema } from '../models/AbstractTeamIntegrationSchema';
|
import type { AbstractTeamIntegrationSchema } from '../models/AbstractTeamIntegrationSchema';
|
||||||
import type { AddPlayerJson } from '../models/AddPlayerJson';
|
import type { AddPlayerJson } from '../models/AddPlayerJson';
|
||||||
|
import type { CreateEventJson } from '../models/CreateEventJson';
|
||||||
import type { CreateTeamJson } from '../models/CreateTeamJson';
|
import type { CreateTeamJson } from '../models/CreateTeamJson';
|
||||||
import type { EditMemberRolesJson } from '../models/EditMemberRolesJson';
|
import type { EditMemberRolesJson } from '../models/EditMemberRolesJson';
|
||||||
|
import type { EventSchema } from '../models/EventSchema';
|
||||||
|
import type { EventSchemaList } from '../models/EventSchemaList';
|
||||||
import type { PlayerSchema } from '../models/PlayerSchema';
|
import type { PlayerSchema } from '../models/PlayerSchema';
|
||||||
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
import type { PutScheduleForm } from '../models/PutScheduleForm';
|
||||||
import type { SetUsernameJson } from '../models/SetUsernameJson';
|
import type { SetUsernameJson } from '../models/SetUsernameJson';
|
||||||
|
@ -45,6 +48,104 @@ export class DefaultService {
|
||||||
url: '/api/debug/set-cookie',
|
url: '/api/debug/set-cookie',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* get_team_events <GET>
|
||||||
|
* @param teamId
|
||||||
|
* @returns EventSchemaList OK
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public getTeamEvents(
|
||||||
|
teamId: number,
|
||||||
|
): CancelablePromise<EventSchemaList> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/events/team/id/{team_id}',
|
||||||
|
path: {
|
||||||
|
'team_id': teamId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Unprocessable Entity`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* create_event <POST>
|
||||||
|
* @param teamId
|
||||||
|
* @param requestBody
|
||||||
|
* @returns EventSchema OK
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public postApiEventsTeamIdTeamId(
|
||||||
|
teamId: number,
|
||||||
|
requestBody?: CreateEventJson,
|
||||||
|
): CancelablePromise<EventSchema> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/events/team/id/{team_id}',
|
||||||
|
path: {
|
||||||
|
'team_id': teamId,
|
||||||
|
},
|
||||||
|
body: requestBody,
|
||||||
|
mediaType: 'application/json',
|
||||||
|
errors: {
|
||||||
|
422: `Unprocessable Entity`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get_user_events <GET>
|
||||||
|
* @param userId
|
||||||
|
* @returns void
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public getApiEventsUserIdUserId(
|
||||||
|
userId: number,
|
||||||
|
): CancelablePromise<void> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/events/user/id/{user_id}',
|
||||||
|
path: {
|
||||||
|
'user_id': userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get_event <GET>
|
||||||
|
* @param eventId
|
||||||
|
* @returns EventSchema OK
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public getEvent(
|
||||||
|
eventId: number,
|
||||||
|
): CancelablePromise<EventSchema> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/events/{event_id}',
|
||||||
|
path: {
|
||||||
|
'event_id': eventId,
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
422: `Unprocessable Entity`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* set_event_players <PATCH>
|
||||||
|
* @param eventId
|
||||||
|
* @returns void
|
||||||
|
* @throws ApiError
|
||||||
|
*/
|
||||||
|
public patchApiEventsEventIdPlayers(
|
||||||
|
eventId: number,
|
||||||
|
): CancelablePromise<void> {
|
||||||
|
return this.httpRequest.request({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: '/api/events/{event_id}/players',
|
||||||
|
path: {
|
||||||
|
'event_id': eventId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* logout <DELETE>
|
* logout <DELETE>
|
||||||
* @returns void
|
* @returns void
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { EventSchema } from "@/client";
|
||||||
|
import { useTeamsStore } from "@/stores/teams";
|
||||||
|
import moment from "moment";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const teamsStore = useTeamsStore();
|
||||||
|
|
||||||
|
const date = computed(() => moment(props.event.startTime));
|
||||||
|
|
||||||
|
const formattedTime = computed(() => {
|
||||||
|
const team = teamsStore.teams[props.event.teamId];
|
||||||
|
const offsetDate = date.value.clone().tz(team.tzTimezone);
|
||||||
|
return `${date.value.format("LT")} (${offsetDate.format("LT z")})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const day = computed(() => {
|
||||||
|
return date.value.format("D");
|
||||||
|
});
|
||||||
|
|
||||||
|
const shortMonth = computed(() => {
|
||||||
|
return date.value.format("MMM");
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
event: EventSchema;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="event-card">
|
||||||
|
<div class="date">
|
||||||
|
<span class="month">
|
||||||
|
{{ shortMonth }}
|
||||||
|
</span>
|
||||||
|
<span class="day">
|
||||||
|
{{ day }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<div>
|
||||||
|
<h3>{{ event.name }}</h3>
|
||||||
|
<div>
|
||||||
|
<span v-if="event.description">{{ event.description }}</span>
|
||||||
|
<em v-else class="subtext">No description provided.</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="subdetails">
|
||||||
|
<span>
|
||||||
|
<i class="bi bi-clock-fill margin" />
|
||||||
|
{{ formattedTime }}
|
||||||
|
</span>
|
||||||
|
<span class="class-info">
|
||||||
|
<i class="tf2class tf2-PocketScout margin" />
|
||||||
|
Pocket Scout
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.event-card {
|
||||||
|
display: flex;
|
||||||
|
padding: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
/*background-color: white;*/
|
||||||
|
border: 1px solid var(--text);
|
||||||
|
border-radius: 8px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1;
|
||||||
|
flex-basis: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date .month {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date .day {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subdetails {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subdetails .margin {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subdetails i.tf2class {
|
||||||
|
line-height: 1em;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { EventSchema } from "@/client";
|
||||||
|
import EventCard from "./EventCard.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
events: EventSchema[];
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>Upcoming Events</h2>
|
||||||
|
<div class="events-list">
|
||||||
|
<EventCard v-for="event in props.events" :key="event.id" :event="event" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.events-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -34,9 +34,7 @@ function leaveTeam() {
|
||||||
Members
|
Members
|
||||||
</h2>
|
</h2>
|
||||||
<em class="aside" v-if="teamMembers">
|
<em class="aside" v-if="teamMembers">
|
||||||
{{ teamMembers?.length }} member(s),
|
{{ teamMembers?.length }} member(s)
|
||||||
{{ availableMembers?.length }} currently available,
|
|
||||||
{{ availableMembersNextHour?.length }} available in the next hour
|
|
||||||
</em>
|
</em>
|
||||||
<div class="team-details-button-group">
|
<div class="team-details-button-group">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import type { EventSchema } from "@/client";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { computed, reactive, ref } from "vue";
|
||||||
|
import { useClientStore } from "./client";
|
||||||
|
|
||||||
|
export const useEventsStore = defineStore("events", () => {
|
||||||
|
const clientStore = useClientStore();
|
||||||
|
const client = clientStore.client;
|
||||||
|
|
||||||
|
const events = ref<EventSchema[]>([ ]);
|
||||||
|
|
||||||
|
const eventsById = computed(() => {
|
||||||
|
return events.value
|
||||||
|
.reduce((acc, event) => {
|
||||||
|
return { ...acc, [event.id]: event };
|
||||||
|
}, { } as { [id: number]: EventSchema });
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetchEvent(id: number) {
|
||||||
|
return clientStore.call(
|
||||||
|
fetchEvent.name,
|
||||||
|
() => client.default.getEvent(id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
events,
|
||||||
|
eventsById,
|
||||||
|
fetchEvent,
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { createPinia, setActivePinia } from "pinia";
|
||||||
|
import { beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { useEventsStore } from "../events";
|
||||||
|
import { useTeamsEventsStore } from "./events";
|
||||||
|
|
||||||
|
describe("Team events store", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setActivePinia(createPinia());
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should reflect the same events as the events store", () => {
|
||||||
|
const eventsStore = useEventsStore();
|
||||||
|
eventsStore.events = [
|
||||||
|
{
|
||||||
|
createdAt: "",
|
||||||
|
description: "",
|
||||||
|
id: 0,
|
||||||
|
name: "test",
|
||||||
|
startTime: "",
|
||||||
|
teamId: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
createdAt: "",
|
||||||
|
description: "",
|
||||||
|
id: 2,
|
||||||
|
name: "test",
|
||||||
|
startTime: "",
|
||||||
|
teamId: 5,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const teamEventsStore = useTeamsEventsStore();
|
||||||
|
const teamEvents = teamEventsStore.teamEvents[5];
|
||||||
|
|
||||||
|
expect(teamEvents.length).toEqual(eventsStore.events.length);
|
||||||
|
expect(teamEvents).toEqual(eventsStore.events);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { useClientStore } from "../client";
|
||||||
|
import type { EventSchema, EventSchemaList } from "@/client";
|
||||||
|
import { useEventsStore } from "../events";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
export const useTeamsEventsStore = defineStore("teamsEvents", () => {
|
||||||
|
const clientStore = useClientStore();
|
||||||
|
const client = clientStore.client;
|
||||||
|
const eventsStore = useEventsStore();
|
||||||
|
|
||||||
|
const teamEvents = computed(() => {
|
||||||
|
console.log("Recomputing teamEvents");
|
||||||
|
|
||||||
|
// map events to objects with teamId as key, and array of events as value
|
||||||
|
return eventsStore.events
|
||||||
|
.reduce((acc, event) => {
|
||||||
|
if (!acc[event.teamId]) {
|
||||||
|
acc[event.teamId] = [];
|
||||||
|
}
|
||||||
|
acc[event.teamId].push(event);
|
||||||
|
return acc;
|
||||||
|
}, { } as { [teamId: number]: EventSchema[] });
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetchTeamEvents(teamId: number) {
|
||||||
|
return clientStore.call(
|
||||||
|
fetchTeamEvents.name,
|
||||||
|
() => client.default.getTeamEvents(teamId),
|
||||||
|
(result: EventSchemaList) => {
|
||||||
|
result.forEach((event) => {
|
||||||
|
// insert into event store
|
||||||
|
//eventsStore.events[event.id] = event;
|
||||||
|
eventsStore.events.push(event);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
teamEvents,
|
||||||
|
fetchTeamEvents,
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,14 +1,17 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRoute, useRouter, RouterLink, RouterView } from "vue-router";
|
import { useRoute, useRouter, RouterLink, RouterView } from "vue-router";
|
||||||
import { useTeamsStore } from "../stores/teams";
|
import { useTeamsStore } from "@/stores/teams";
|
||||||
|
import { useInvitesStore } from "@/stores/teams/invites";
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import { useTeamDetails } from "../composables/team-details";
|
import { useTeamDetails } from "@/composables/team-details";
|
||||||
import MembersList from "../components/MembersList.vue";
|
import MembersList from "@/components/MembersList.vue";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import EventList from "@/components/EventList.vue";
|
||||||
|
import { useTeamsEventsStore } from "@/stores/teams/events";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
|
||||||
const teamsStore = useTeamsStore();
|
const teamsStore = useTeamsStore();
|
||||||
|
const invitesStore = useInvitesStore();
|
||||||
const { team, teamId } = useTeamDetails();
|
const { team, teamId } = useTeamDetails();
|
||||||
|
|
||||||
const creationDate = computed(() => {
|
const creationDate = computed(() => {
|
||||||
|
@ -19,15 +22,20 @@ const creationDate = computed(() => {
|
||||||
|
|
||||||
const key = computed(() => route.query.key);
|
const key = computed(() => route.query.key);
|
||||||
|
|
||||||
|
const teamsEventsStore = useTeamsEventsStore();
|
||||||
|
const events = computed(() => teamsEventsStore.teamEvents[teamId.value]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let doFetchTeam = () => {
|
let doFetchTeam = () => {
|
||||||
teamsStore.fetchTeam(teamId.value)
|
teamsStore.fetchTeam(teamId.value)
|
||||||
.then(() => teamsStore.fetchTeamMembers(teamId.value))
|
.then(() => {
|
||||||
.then(() => teamsStore.getInvites(teamId.value));
|
teamsStore.fetchTeamMembers(teamId.value);
|
||||||
|
teamsEventsStore.fetchTeamEvents(teamId.value);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (key.value) {
|
if (key.value) {
|
||||||
teamsStore.consumeInvite(teamId.value, key.value.toString())
|
invitesStore.consumeInvite(teamId.value, key.value.toString())
|
||||||
.finally(doFetchTeam);
|
.finally(doFetchTeam);
|
||||||
} else {
|
} else {
|
||||||
doFetchTeam();
|
doFetchTeam();
|
||||||
|
@ -58,12 +66,32 @@ onMounted(() => {
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</center>
|
</center>
|
||||||
<MembersList />
|
<div class="content-container">
|
||||||
|
<div class="left">
|
||||||
|
<MembersList />
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<EventList :events="events" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.content-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container > div.left {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container > div.right {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.margin {
|
.margin {
|
||||||
margin: 4em;
|
margin: 4em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import schedule
|
||||||
import team
|
import team
|
||||||
from spec import spec
|
from spec import spec
|
||||||
import user
|
import user
|
||||||
|
import events
|
||||||
|
|
||||||
connect_db_with_app()
|
connect_db_with_app()
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ api.register_blueprint(login.api_login)
|
||||||
api.register_blueprint(schedule.api_schedule)
|
api.register_blueprint(schedule.api_schedule)
|
||||||
api.register_blueprint(team.api_team)
|
api.register_blueprint(team.api_team)
|
||||||
api.register_blueprint(user.api_user)
|
api.register_blueprint(user.api_user)
|
||||||
|
api.register_blueprint(events.api_events)
|
||||||
|
|
||||||
@api.get("/debug/set-cookie")
|
@api.get("/debug/set-cookie")
|
||||||
@api.post("/debug/set-cookie")
|
@api.post("/debug/set-cookie")
|
||||||
|
|
|
@ -48,6 +48,8 @@ def get_team_events(team_id: int):
|
||||||
Event
|
Event
|
||||||
).filter(
|
).filter(
|
||||||
Event.team_id == team_id
|
Event.team_id == team_id
|
||||||
|
).order_by(
|
||||||
|
Event.start_time
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
def map_to_schema(event: Event):
|
def map_to_schema(event: Event):
|
||||||
|
|
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||||
from sqlalchemy.orm import mapped_column, relationship
|
from sqlalchemy.orm import mapped_column, relationship
|
||||||
from sqlalchemy.orm.attributes import Mapped
|
from sqlalchemy.orm.attributes import Mapped
|
||||||
from sqlalchemy.orm.properties import ForeignKey
|
from sqlalchemy.orm.properties import ForeignKey
|
||||||
|
from sqlalchemy.schema import UniqueConstraint
|
||||||
from sqlalchemy.types import TIMESTAMP, Integer, String, Text
|
from sqlalchemy.types import TIMESTAMP, Integer, String, Text
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
from sqlalchemy_utc import UtcDateTime
|
from sqlalchemy_utc import UtcDateTime
|
||||||
|
@ -12,21 +13,29 @@ import spec
|
||||||
class Event(app_db.BaseModel):
|
class Event(app_db.BaseModel):
|
||||||
__tablename__ = "events"
|
__tablename__ = "events"
|
||||||
|
|
||||||
|
# surrogate key
|
||||||
id: Mapped[int] = mapped_column(Integer, autoincrement=True, primary_key=True)
|
id: Mapped[int] = mapped_column(Integer, autoincrement=True, primary_key=True)
|
||||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
||||||
description: Mapped[str] = mapped_column(Text, nullable=True)
|
# primary key
|
||||||
start_time: Mapped[datetime] = mapped_column(UtcDateTime, nullable=False)
|
|
||||||
team_id: Mapped[int] = mapped_column(ForeignKey("teams.id"), nullable=False)
|
team_id: Mapped[int] = mapped_column(ForeignKey("teams.id"), nullable=False)
|
||||||
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||||
|
start_time: Mapped[datetime] = mapped_column(UtcDateTime, nullable=False)
|
||||||
|
|
||||||
|
description: Mapped[str] = mapped_column(Text, nullable=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
|
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
|
||||||
|
|
||||||
team: Mapped["Team"] = relationship("Team", back_populates="events")
|
team: Mapped["Team"] = relationship("Team", back_populates="events")
|
||||||
players: Mapped["PlayerEvent"] = relationship("PlayerEvent", back_populates="event")
|
players: Mapped["PlayerEvent"] = relationship("PlayerEvent", back_populates="event")
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint("team_id", "name", "start_time"),
|
||||||
|
)
|
||||||
|
|
||||||
class EventSchema(spec.BaseModel):
|
class EventSchema(spec.BaseModel):
|
||||||
id: int
|
id: int
|
||||||
team_id: int
|
team_id: int
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str | None
|
||||||
start_time: datetime
|
start_time: datetime
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
|
@ -41,5 +50,6 @@ class EventSchema(spec.BaseModel):
|
||||||
created_at=model.created_at,
|
created_at=model.created_at,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from models.team import Team
|
from models.team import Team
|
||||||
from models.player_event import PlayerEvent
|
from models.player_event import PlayerEvent
|
||||||
|
|
Loading…
Reference in New Issue