diff --git a/client/package.json b/client/package.json index 7c354ece5569314b108c67617fecf9e0f9d637e0..8733d1d45b7c2e4c2349167573e7411aa66afd45 100644 --- a/client/package.json +++ b/client/package.json @@ -96,5 +96,5 @@ "html" ] }, - "proxy": "http://localhost:5000/api/" + "proxy": "http://localhost:5000/" } diff --git a/client/src/actions/cities.ts b/client/src/actions/cities.ts index 9226aee57b2f477d85d9d5fa781174d097773afc..594a43c43355f1c63d9248a2f98d28b4c243c820 100644 --- a/client/src/actions/cities.ts +++ b/client/src/actions/cities.ts @@ -4,7 +4,7 @@ import Types from './types' export const getCities = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/cities') + .get('/api/misc/cities') .then((res) => { dispatch({ type: Types.SET_CITIES, diff --git a/client/src/actions/competitions.ts b/client/src/actions/competitions.ts index 7d248e46c05df64e948b483c5092616c4a828f35..1a9d789c09d6646657b622de1d4c43d0c9be1dbd 100644 --- a/client/src/actions/competitions.ts +++ b/client/src/actions/competitions.ts @@ -15,7 +15,7 @@ export const getCompetitions = () => async (dispatch: AppDispatch, getState: () year: currentParams.year, } await axios - .get('/competitions/search', { params }) + .get('/api/competitions/search', { params }) .then((res) => { dispatch({ type: Types.SET_COMPETITIONS, diff --git a/client/src/actions/editor.ts b/client/src/actions/editor.ts index 903dfa7bda973511d3eb5b13e31ac02a8f7bc2ee..45fbce986abae8df3da0c987be6faa4212cac2bf 100644 --- a/client/src/actions/editor.ts +++ b/client/src/actions/editor.ts @@ -4,7 +4,7 @@ import Types from './types' export const getEditorCompetition = (id: string) => async (dispatch: AppDispatch, getState: () => RootState) => { await axios - .get(`/competitions/${id}`) + .get(`/api/competitions/${id}`) .then((res) => { dispatch({ type: Types.SET_EDITOR_COMPETITION, diff --git a/client/src/actions/presentation.ts b/client/src/actions/presentation.ts index c967131f903809ac8af8e11c0218fb614de75227..97c611dfb27015f37fa33693ee90c6f8a1159ef3 100644 --- a/client/src/actions/presentation.ts +++ b/client/src/actions/presentation.ts @@ -6,7 +6,7 @@ import Types from './types' export const getPresentationCompetition = (id: string) => async (dispatch: AppDispatch) => { await axios - .get(`/competitions/${id}`) + .get(`/api/competitions/${id}`) .then((res) => { dispatch({ type: Types.SET_PRESENTATION_COMPETITION, @@ -20,7 +20,7 @@ export const getPresentationCompetition = (id: string) => async (dispatch: AppDi export const getPresentationTeams = (id: string) => async (dispatch: AppDispatch) => { await axios - .get(`/competitions/${id}/teams`) + .get(`/api/competitions/${id}/teams`) .then((res) => { dispatch({ type: Types.SET_PRESENTATION_TEAMS, diff --git a/client/src/actions/roles.ts b/client/src/actions/roles.ts index 0160353b184755b33f003592ad127880af2803d7..19a148c7a01d9dc4ccd6552f06dbf954509f16dc 100644 --- a/client/src/actions/roles.ts +++ b/client/src/actions/roles.ts @@ -4,7 +4,7 @@ import Types from './types' export const getRoles = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/roles') + .get('/api/misc/roles') .then((res) => { dispatch({ type: Types.SET_ROLES, diff --git a/client/src/actions/searchUser.ts b/client/src/actions/searchUser.ts index c6211056bda8250f1b5c849450d018ef4bd42819..a3ea55c7ce7ef5afc1468de7cc1d350303b3674a 100644 --- a/client/src/actions/searchUser.ts +++ b/client/src/actions/searchUser.ts @@ -15,7 +15,7 @@ export const getSearchUsers = () => async (dispatch: AppDispatch, getState: () = email: currentParams.email, } await axios - .get('/users/search', { params }) + .get('/api/users/search', { params }) .then((res) => { dispatch({ type: Types.SET_SEARCH_USERS, diff --git a/client/src/actions/statistics.ts b/client/src/actions/statistics.ts new file mode 100644 index 0000000000000000000000000000000000000000..a32dce3553ba56ed47410b1499aeca57f7490f75 --- /dev/null +++ b/client/src/actions/statistics.ts @@ -0,0 +1,15 @@ +import axios from 'axios' +import { AppDispatch } from './../store' +import Types from './types' + +export const getStatistics = () => async (dispatch: AppDispatch) => { + await axios + .get('/api/misc/statistics') + .then((res) => { + dispatch({ + type: Types.SET_STATISTICS, + payload: res.data, + }) + }) + .catch((err) => console.log(err)) +} diff --git a/client/src/actions/types.ts b/client/src/actions/types.ts index a417f6afc797d8a69bd81d2b259283e98ed364e3..189bf16ca5f3baa63bdf8d0ad5d5ae0fa6805d10 100644 --- a/client/src/actions/types.ts +++ b/client/src/actions/types.ts @@ -29,4 +29,5 @@ export default { SET_CITIES_TOTAL: 'SET_CITIES_TOTAL', SET_CITIES_COUNT: 'SET_CITIES_COUNT', SET_TYPES: 'SET_TYPES', + SET_STATISTICS: 'SET_STATISTICS', } diff --git a/client/src/actions/typesAction.ts b/client/src/actions/typesAction.ts index 4fcde4f55ee72cb9f72f6d79f0e5f1db6995fa4b..32a212b0c5fc270d62dd6528efbea51824acdb8e 100644 --- a/client/src/actions/typesAction.ts +++ b/client/src/actions/typesAction.ts @@ -4,7 +4,7 @@ import Types from './types' export const getTypes = () => async (dispatch: AppDispatch) => { await axios - .get('/misc/types') + .get('/api/misc/types') .then((res) => { dispatch({ type: Types.SET_TYPES, diff --git a/client/src/actions/user.ts b/client/src/actions/user.ts index 371dcaeb6b9426c4235e9fc837257f5bbc6625f9..e2ad88acf776490ee49189236f2f921297c33d5c 100644 --- a/client/src/actions/user.ts +++ b/client/src/actions/user.ts @@ -7,7 +7,7 @@ import Types from './types' export const loginUser = (userData: AccountLoginModel, history: History) => async (dispatch: AppDispatch) => { dispatch({ type: Types.LOADING_UI }) await axios - .post('/auth/login', userData) + .post('/api/auth/login', userData) .then((res) => { const token = `Bearer ${res.data.access_token}` localStorage.setItem('token', token) //setting token to local storage @@ -28,7 +28,7 @@ export const loginUser = (userData: AccountLoginModel, history: History) => asyn export const getUserData = () => async (dispatch: AppDispatch) => { dispatch({ type: Types.LOADING_USER }) await axios - .get('/users') + .get('/api/users') .then((res) => { dispatch({ type: Types.SET_USER, @@ -42,7 +42,7 @@ export const getUserData = () => async (dispatch: AppDispatch) => { export const logoutUser = () => async (dispatch: AppDispatch) => { localStorage.removeItem('token') - await axios.post('/auth/logout').then(() => { + await axios.post('/api/auth/logout').then(() => { delete axios.defaults.headers.common['Authorization'] dispatch({ type: Types.SET_UNAUTHENTICATED, diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx index 30cfe656c4bb336d82d3056849008c2c71bbac3d..1b543083da7fbfb1e5c1a48d44a40a2d7fdcc17e 100644 --- a/client/src/pages/admin/AdminPage.tsx +++ b/client/src/pages/admin/AdminPage.tsx @@ -20,6 +20,7 @@ import React, { useEffect } from 'react' import { Link, Route, Switch, useRouteMatch } from 'react-router-dom' import { getCities } from '../../actions/cities' import { getRoles } from '../../actions/roles' +import { getStatistics } from '../../actions/statistics' import { getTypes } from '../../actions/typesAction' import { logoutUser } from '../../actions/user' import { useAppDispatch, useAppSelector } from '../../hooks' @@ -71,6 +72,7 @@ const AdminView: React.FC = () => { dispatch(getCities()) dispatch(getRoles()) dispatch(getTypes()) + dispatch(getStatistics()) }, []) const menuAdminItems = [ diff --git a/client/src/pages/admin/competitions/AddCompetition.tsx b/client/src/pages/admin/competitions/AddCompetition.tsx index 0ce9a6ffa4601cf543383e7efa3bdf55eee259f8..6053f2f8de9c3d13cde3403ec1a62317e477d047 100644 --- a/client/src/pages/admin/competitions/AddCompetition.tsx +++ b/client/src/pages/admin/competitions/AddCompetition.tsx @@ -63,7 +63,7 @@ const AddCompetition: React.FC = (props: any) => { } await axios - .post('/competitions', params) // send to database + .post('/api/competitions', params) // send to database .then(() => { actions.resetForm() // reset the form setAnchorEl(null) diff --git a/client/src/pages/admin/competitions/CompetitionManager.tsx b/client/src/pages/admin/competitions/CompetitionManager.tsx index 929d3145d33fedd061f426af6e85461c1a355b77..83f399b95a0c083727333c2f842a1c61a8eca2d0 100644 --- a/client/src/pages/admin/competitions/CompetitionManager.tsx +++ b/client/src/pages/admin/competitions/CompetitionManager.tsx @@ -82,7 +82,7 @@ const CompetitionManager: React.FC = (props: any) => { const handleDeleteCompetition = async () => { if (activeId) { await axios - .delete(`/competitions/${activeId}`) + .delete(`/api/competitions/${activeId}`) .then(() => { setAnchorEl(null) dispatch(getCompetitions()) // refresh the competition list @@ -101,7 +101,7 @@ const CompetitionManager: React.FC = (props: any) => { const handleDuplicateCompetition = async () => { if (activeId) { await axios - .post(`/competitions/${activeId}/copy`) + .post(`/api/competitions/${activeId}/copy`) .then(() => { setAnchorEl(null) dispatch(getCompetitions()) diff --git a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx index 87d8272ae035ce56d509dd99380bdb1ed17426a6..eb667ebd690f6013c184ac13e2038ce1a7726de9 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx @@ -1,24 +1,19 @@ import { Box, Typography } from '@material-ui/core' -import React, { useEffect } from 'react' -import { getCompetitions } from '../../../../actions/competitions' -import { useAppDispatch, useAppSelector } from '../../../../hooks' +import React from 'react' +import { useAppSelector } from '../../../../hooks' const NumberOfCompetitions: React.FC = () => { - const competitions = useAppSelector((state) => state.competitions.competitions) - const dispatch = useAppDispatch() + const competitions = useAppSelector((state) => state.statistics.competitions) const handleCount = () => { - if (competitions.length >= 1000000) { - ;<div>{competitions.length / 1000000 + 'M'}</div> - } else if (competitions.length >= 1000) { - ;<div>{competitions.length / 1000 + 'K'}</div> + if (competitions >= 1000000) { + ;<div>{competitions / 1000000 + 'M'}</div> + } else if (competitions >= 1000) { + ;<div>{competitions / 1000 + 'K'}</div> } - return <div>{competitions.length}</div> + return <div>{competitions}</div> } - useEffect(() => { - dispatch(getCompetitions()) - }, []) return ( <div> <Box width="100%" height="100%"> diff --git a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx index 360b3663b0e45e9a24018f855ede3023c95bf39a..f3195f4c18969ed2ad1e4b185b2125ac2c15f782 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx @@ -4,7 +4,7 @@ import { getCities } from '../../../../actions/cities' import { useAppDispatch, useAppSelector } from '../../../../hooks' const NumberOfRegions: React.FC = () => { - const regions = useAppSelector((state) => state.cities.total) + const regions = useAppSelector((state) => state.statistics.regions) const dispatch = useAppDispatch() const handleCount = () => { diff --git a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx index af0f97677565aa264a61269c274cf890eec774d8..0e75cf1b1069814801fadc010f30ac393dfe3b25 100644 --- a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx +++ b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx @@ -4,7 +4,7 @@ import { getSearchUsers } from '../../../../actions/searchUser' import { useAppDispatch, useAppSelector } from '../../../../hooks' const NumberOfUsers: React.FC = () => { - const usersTotal = useAppSelector((state) => state.searchUsers.total) + const usersTotal = useAppSelector((state) => state.statistics.users) const dispatch = useAppDispatch() const handleCount = () => { diff --git a/client/src/pages/admin/regions/AddRegion.tsx b/client/src/pages/admin/regions/AddRegion.tsx index c8ae412e680bda652327193af44cb728e36c1621..b00961384f73246365e60023af04ed7567adf311 100644 --- a/client/src/pages/admin/regions/AddRegion.tsx +++ b/client/src/pages/admin/regions/AddRegion.tsx @@ -51,7 +51,7 @@ const AddRegion: React.FC = (props: any) => { name: values.model.name, } await axios - .post('/misc/cities', params) + .post('/api/misc/cities', params) .then(() => { actions.resetForm() dispatch(getCities()) diff --git a/client/src/pages/admin/regions/Regions.tsx b/client/src/pages/admin/regions/Regions.tsx index eb79575ea7857658742f30f02d5f96a5e87e7f6d..436da6f69429ca6711452d7babf477f8da593eee 100644 --- a/client/src/pages/admin/regions/Regions.tsx +++ b/client/src/pages/admin/regions/Regions.tsx @@ -45,7 +45,7 @@ const RegionManager: React.FC = (props: any) => { const handleDeleteCity = async () => { if (activeId) { await axios - .delete(`/misc/cities/${activeId}`) + .delete(`/api/misc/cities/${activeId}`) .then(() => { setAnchorEl(null) dispatch(getCities()) @@ -63,7 +63,7 @@ const RegionManager: React.FC = (props: any) => { const handleAddCity = async () => { await axios - .post(`/misc/cities`, { name: newCity }) + .post(`/api/misc/cities`, { name: newCity }) .then(() => { setAnchorEl(null) dispatch(getCities()) diff --git a/client/src/pages/admin/users/AddUser.tsx b/client/src/pages/admin/users/AddUser.tsx index d58a5d5dc153463618015e9c4195958f5160bbcb..9f1511eac460c661ebd35dc995f6606e66080359 100644 --- a/client/src/pages/admin/users/AddUser.tsx +++ b/client/src/pages/admin/users/AddUser.tsx @@ -59,7 +59,7 @@ const AddUser: React.FC = (props: any) => { role_id: selectedRole?.id as number, } await axios - .post('/auth/signup', params) + .post('/api/auth/signup', params) .then(() => { actions.resetForm() setAnchorEl(null) diff --git a/client/src/pages/admin/users/EditUser.tsx b/client/src/pages/admin/users/EditUser.tsx index 7db5aeecd2be0c3f3f94c22a0c459a71cbc47aa4..9b4a5d1bd6aecd92a03f44221c2cb40d40e2ab70 100644 --- a/client/src/pages/admin/users/EditUser.tsx +++ b/client/src/pages/admin/users/EditUser.tsx @@ -110,7 +110,7 @@ const EditUser = ({ user }: UserIdProps) => { const handleDeleteUsers = async () => { setOpen(false) await axios - .delete(`/auth/delete/${user.id}`) + .delete(`/api/auth/delete/${user.id}`) .then(() => { setAnchorEl(null) dispatch(getSearchUsers()) @@ -141,7 +141,7 @@ const EditUser = ({ user }: UserIdProps) => { req['role_id'] = params.role_id } await axios - .put('/users/' + user.id, req) + .put('/api/users/' + user.id, req) .then((res) => { setAnchorEl(null) dispatch(getSearchUsers()) diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx index 956d0b7212baf5a3eed212c86e9946550b66e72f..c645724e3c7fabda69a2ad2434f4bd7b587c8c2e 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx @@ -28,7 +28,7 @@ it('renders presentation editor', () => { }, } ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => { - if (path.startsWith('/competitions')) return Promise.resolve(competitionRes) + if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes) return Promise.resolve(citiesRes) }) render( diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx index 4087d6c798d17522dbaffe3f8c7041b57795338f..da18cd52fd6222b4a37c7a88bc111f0c4974ecc9 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx @@ -104,7 +104,7 @@ const PresentationEditorPage: React.FC = () => { } const createNewSlide = async () => { - await axios.post(`/competitions/${id}/slides`, { title: 'new slide' }) + await axios.post(`/api/competitions/${id}/slides`, { title: 'new slide' }) dispatch(getEditorCompetition(id)) } @@ -128,13 +128,13 @@ const PresentationEditorPage: React.FC = () => { } const handleRemoveSlide = async () => { - await axios.delete(`/competitions/${id}/slides/${contextState.slideId}`) + await axios.delete(`/api/competitions/${id}/slides/${contextState.slideId}`) dispatch(getEditorCompetition(id)) setContextState(initialState) } const handleDuplicateSlide = async () => { - await axios.post(`/competitions/${id}/slides/${contextState.slideId}/copy`) + await axios.post(`/api/competitions/${id}/slides/${contextState.slideId}/copy`) dispatch(getEditorCompetition(id)) setContextState(initialState) } diff --git a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx index 75d418403d7ec51e13563adf3c9afcf2ef15152f..53c2ecdb4dd23cc81df0140bd2bede66dfe18b88 100644 --- a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx +++ b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx @@ -77,7 +77,7 @@ const CompetitionSettings: React.FC = () => { const competition = useAppSelector((state) => state.editor.competition) const updateCompetitionName = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { await axios - .put(`/competitions/${id}`, { name: event.target.value }) + .put(`/api/competitions/${id}`, { name: event.target.value }) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -87,7 +87,7 @@ const CompetitionSettings: React.FC = () => { const cities = useAppSelector((state) => state.cities.cities) const updateCompetitionCity = async (city: City) => { await axios - .put(`/competitions/${id}`, { city_id: city.id }) + .put(`/api/competitions/${id}`, { city_id: city.id }) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -104,7 +104,7 @@ const CompetitionSettings: React.FC = () => { const removeTeam = async (tid: number) => { await axios - .delete(`/competitions/${id}/teams/${tid}`) + .delete(`/api/competitions/${id}/teams/${tid}`) .then(() => { dispatch(getEditorCompetition(id)) }) @@ -113,7 +113,7 @@ const CompetitionSettings: React.FC = () => { const addTeam = async () => { setAddTeamOpen(false) await axios - .post(`/competitions/${id}/teams`, { name: selectedTeamName }) + .post(`/api/competitions/${id}/teams`, { name: selectedTeamName }) .then(() => { dispatch(getEditorCompetition(id)) }) diff --git a/client/src/pages/presentationEditor/components/Images.tsx b/client/src/pages/presentationEditor/components/Images.tsx index ba55b89bd8c68ce1e48631071f58fb05def097d9..199787e48d915de2e41ed77c1b1dd3f5ef223419 100644 --- a/client/src/pages/presentationEditor/components/Images.tsx +++ b/client/src/pages/presentationEditor/components/Images.tsx @@ -1,4 +1,4 @@ -import { List, ListItem, ListItemText, Typography } from '@material-ui/core' +import { ListItem, ListItemText, Typography } from '@material-ui/core' import CloseIcon from '@material-ui/icons/Close' import React, { useState } from 'react' import { Center, HiddenInput, SettingsList, AddImageButton, ImportedImage } from './styled' diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx index 890e51601ec20901e66ad76b199719478d6c53e3..8142c1a79dc5fe23cc88635af7521f4811bff542 100644 --- a/client/src/pages/presentationEditor/components/RndComponent.tsx +++ b/client/src/pages/presentationEditor/components/RndComponent.tsx @@ -20,13 +20,13 @@ const RndComponent = ({ component }: ImageComponentProps) => { const competitionId = useAppSelector((state) => state.editor.competition.id) const slideId = useAppSelector((state) => state.editor.activeSlideId) const handleUpdatePos = (pos: Position) => { - axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { + axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { x: pos.x, y: pos.y, }) } const handleUpdateSize = (size: Size) => { - axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { + axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { w: size.w, h: size.h, }) @@ -40,7 +40,9 @@ const RndComponent = ({ component }: ImageComponentProps) => { return ( <TextComponentContainer hover={hover} - dangerouslySetInnerHTML={{ __html: (component as TextComponent).data.text }} + dangerouslySetInnerHTML={{ + __html: `<div style="font-size: 24px;"> ${(component as TextComponent).data.text} </div>`, + }} /> ) case ComponentTypes.Image: diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx index 0b425dea75ed681ed934431973cb613d9e9b0dbc..2a64d7ac5f77809e66ca2cfd8331e91789c6c26e 100644 --- a/client/src/pages/presentationEditor/components/SlideSettings.tsx +++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx @@ -1,17 +1,11 @@ import { Divider, List, ListItem, ListItemText, TextField, Typography } from '@material-ui/core' -import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' -import CloseIcon from '@material-ui/icons/Close' import React, { useState } from 'react' import { useParams } from 'react-router-dom' import { useAppSelector } from '../../../hooks' -import { TextComponent } from '../../../interfaces/ApiModels' -import axios from 'axios' import Alternatives from './Alternatives' import SlideType from './SlideType' -import { Center, HiddenInput, ImportedImage, SettingsList, SlidePanel, TextCard, WhiteBackground } from './styled' +import { Center, ImportedImage, SettingsList, SlidePanel } from './styled' import Timer from './Timer' -import { getEditorCompetition } from '../../../actions/editor' -import { useDispatch } from 'react-redux' import Images from './Images' import Texts from './Texts' @@ -21,71 +15,17 @@ interface CompetitionParams { const SlideSettings: React.FC = () => { const { id }: CompetitionParams = useParams() - const dispatch = useDispatch() const activeSlide = useAppSelector((state) => state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId) ) - const updateMaxScore = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { - const questionId = activeSlide?.questions[0].id - if (activeSlide) { - await axios - .put(`/competitions/${id}/slides/${activeSlide.id}/questions/${questionId}`, { - total_score: event.target.value, - }) - .then(() => { - dispatch(getEditorCompetition(id)) - }) - .catch(console.log) - } - } - - const updateQuestionName = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => { - const questionId = activeSlide?.questions[0].id - if (activeSlide && questionId) { - await axios - .put(`/competitions/${id}/slides/${activeSlide.id}/questions/${questionId}`, { name: event.target.value }) - .then(() => { - dispatch(getEditorCompetition(id)) - }) - .catch(console.log) - } - } - return ( <SlidePanel> <SettingsList> {activeSlide && <SlideType activeSlide={activeSlide} competitionId={id} />} <Divider /> {activeSlide && <Timer activeSlide={activeSlide} competitionId={id} />} - <Divider /> - <WhiteBackground> - <ListItem> - <TextField - id="standard-number" - fullWidth={true} - variant="outlined" - label="Maxpoäng" - type="number" - onChange={updateMaxScore} - defaultValue={(activeSlide?.questions[0] && activeSlide?.questions[0].total_score) || ''} - /> - </ListItem> - </WhiteBackground> - <Divider /> - <WhiteBackground> - <ListItem> - <TextField - label="Frågetitel" - fullWidth={true} - defaultValue={activeSlide?.questions[0] && activeSlide?.questions[0].name} - onChange={updateQuestionName} - variant="outlined" - /> - <Divider /> - </ListItem> - </WhiteBackground> </SettingsList> {activeSlide && <Alternatives activeSlide={activeSlide} competitionId={id} />} diff --git a/client/src/pages/presentationEditor/components/SlideType.tsx b/client/src/pages/presentationEditor/components/SlideType.tsx index 928a63922b29884488e0db9bf21d6391dddde770..e5b904208e0b94d0e3ff8cba2bbcf5bd7ac27d49 100644 --- a/client/src/pages/presentationEditor/components/SlideType.tsx +++ b/client/src/pages/presentationEditor/components/SlideType.tsx @@ -24,6 +24,7 @@ type SlideTypeProps = { const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { const dispatch = useAppDispatch() + // For "slide type" dialog const [selectedSlideType, setSelectedSlideType] = useState(0) const [slideTypeDialog, setSlideTypeDialog] = useState(false) @@ -34,6 +35,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { const closeSlideTypeDialog = () => { setSlideTypeDialog(false) } + const updateSlideType = async () => { closeSlideTypeDialog() if (activeSlide) { @@ -41,7 +43,9 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { if (selectedSlideType === 0) { // Change slide type from a question type to information await axios - .delete(`/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) + .delete( + `/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}` + ) .then(() => { dispatch(getEditorCompetition(competitionId)) }) @@ -49,10 +53,12 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { } else { // Change slide type from question type to another question type await axios - .delete(`/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}`) + .delete( + `/api/competitions/${competitionId}/slides/${activeSlide.id}/questions/${activeSlide.questions[0].id}` + ) .catch(console.log) await axios - .post(`/competitions/${competitionId}/slides/${activeSlide.id}/questions`, { + .post(`/api/competitions/${competitionId}/slides/${activeSlide.id}/questions`, { name: 'Ny fråga', total_score: 0, type_id: selectedSlideType, @@ -65,7 +71,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { } else if (selectedSlideType !== 0) { // Change slide type from information to a question type await axios - .post(`/competitions/${competitionId}/slides/${activeSlide.id}/questions`, { + .post(`/api/competitions/${competitionId}/slides/${activeSlide.id}/questions`, { name: 'Ny fråga', total_score: 0, type_id: selectedSlideType, @@ -81,7 +87,7 @@ const SlideType = ({ activeSlide, competitionId }: SlideTypeProps) => { <WhiteBackground> <FormControlDropdown variant="outlined"> <InputLabel>Sidtyp</InputLabel> - <Select value={activeSlide?.questions[0]?.type_id || 0} label="Sidtyp"> + <Select fullWidth={true} value={activeSlide?.questions[0]?.type_id || 0} label="Sidtyp"> <MenuItem value={0}> <Typography variant="button" onClick={() => openSlideTypeDialog(0)}> Informationssida diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx index e0f0d5b475b87f2db7f71337c1c4978716fdb5e1..0a5c8522447c3111629ac179ce27532048d55c7a 100644 --- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx +++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx @@ -37,7 +37,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { setTimerHandle( window.setTimeout(async () => { console.log('Content was updated on server. id: ', component.id) - await axios.put(`/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, { + await axios.put(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${component.id}`, { data: { ...component.data, text: a }, }) dispatch(getEditorCompetition(id)) @@ -46,7 +46,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { } const handleDeleteText = async (componentId: number) => { - await axios.delete(`/competitions/${id}/slides/${activeSlideId}/components/${componentId}`) + await axios.delete(`/api/competitions/${id}/slides/${activeSlideId}/components/${componentId}`) dispatch(getEditorCompetition(id)) } @@ -57,15 +57,17 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => { init={{ height: '300px', menubar: false, + fontsize_formats: '8pt 9pt 10pt 11pt 12pt 14pt 18pt 24pt 30pt 36pt 48pt 60pt 72pt 96pt 120pt 144pt', + content_style: 'body {font-size: 24pt;}', plugins: [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks code fullscreen', 'insertdatetime media table paste code help wordcount', ], toolbar: - 'undo redo save | fontselect | formatselect | bold italic backcolor | \ - alignleft aligncenter alignright alignjustify | \ - bullist numlist outdent indent | removeformat | help', + 'fontsizeselect | bold italic backcolor | help | \ + fontselect | formatselect | undo redo | \ + alignleft aligncenter alignright alignjustify bullist numlist outdent indent | removeformat |', }} onEditorChange={(a, e) => handleSaveText(a)} /> diff --git a/client/src/pages/presentationEditor/components/Texts.tsx b/client/src/pages/presentationEditor/components/Texts.tsx index 8a5b03703806842d2ce5562ee6c0332618740502..22cde214c92bb64788b4f69d9993977c85fe981d 100644 --- a/client/src/pages/presentationEditor/components/Texts.tsx +++ b/client/src/pages/presentationEditor/components/Texts.tsx @@ -25,7 +25,7 @@ const Texts = ({ activeSlide, competitionId }: TextsProps) => { const dispatch = useDispatch() const handleAddText = async () => { if (activeSlide) { - await axios.post(`/competitions/${competitionId}/slides/${activeSlide?.id}/components`, { + await axios.post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, { type_id: 1, data: { text: 'Ny text' }, w: 315, diff --git a/client/src/pages/presentationEditor/components/Timer.tsx b/client/src/pages/presentationEditor/components/Timer.tsx index 97179da41a5338f06abb12e28477b862d89d6d42..124635f423df9d0bfcca20a9a02309c646cb3dca 100644 --- a/client/src/pages/presentationEditor/components/Timer.tsx +++ b/client/src/pages/presentationEditor/components/Timer.tsx @@ -17,7 +17,7 @@ const Timer = ({ activeSlide, competitionId }: TimerProps) => { setTimer(+event.target.value) if (activeSlide) { await axios - .put(`/competitions/${competitionId}/slides/${activeSlide.order}`, { timer: event.target.value }) + .put(`/api/competitions/${competitionId}/slides/${activeSlide.id}`, { timer: event.target.value }) .then(() => { dispatch(getEditorCompetition(competitionId)) }) diff --git a/client/src/reducers/allReducers.ts b/client/src/reducers/allReducers.ts index 2aee46972cb6f0d33f2b128aeb6d19a9d77f106f..c23384c2a2a21dfba86c60550154985a5c879782 100644 --- a/client/src/reducers/allReducers.ts +++ b/client/src/reducers/allReducers.ts @@ -7,6 +7,7 @@ import editorReducer from './editorReducer' import presentationReducer from './presentationReducer' import rolesReducer from './rolesReducer' import searchUserReducer from './searchUserReducer' +import statisticsReducer from './statisticsReducer' import typesReducer from './typesReducer' import uiReducer from './uiReducer' import userReducer from './userReducer' @@ -22,5 +23,6 @@ const allReducers = combineReducers({ roles: rolesReducer, searchUsers: searchUserReducer, types: typesReducer, + statistics: statisticsReducer, }) export default allReducers diff --git a/client/src/reducers/statisticsReducer.ts b/client/src/reducers/statisticsReducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..78a06e1157f6428c10ea17073afbdf46cf8609e5 --- /dev/null +++ b/client/src/reducers/statisticsReducer.ts @@ -0,0 +1,24 @@ +import { AnyAction } from 'redux' +import Types from '../actions/types' + +interface StatisticsState { + users: number + competitions: number + regions: number +} + +const initialState: StatisticsState = { + users: 0, + competitions: 0, + regions: 0, +} + +export default function (state = initialState, action: AnyAction) { + switch (action.type) { + case Types.SET_STATISTICS: + state = action.payload as StatisticsState + return state + default: + return state + } +} diff --git a/client/src/utils/checkAuthentication.ts b/client/src/utils/checkAuthentication.ts index a782ea10f79a65998ed7d1a9e091361aa6a48982..9225aa29858d2f9ea58f8ac118dd2feed01d1cc5 100644 --- a/client/src/utils/checkAuthentication.ts +++ b/client/src/utils/checkAuthentication.ts @@ -16,7 +16,7 @@ export const CheckAuthentication = async () => { axios.defaults.headers.common['Authorization'] = authToken store.dispatch({ type: Types.LOADING_USER }) await axios - .get('/users') + .get('/api/users') .then((res) => { store.dispatch({ type: Types.SET_AUTHENTICATED }) store.dispatch({ diff --git a/server/app/__init__.py b/server/app/__init__.py index 9215563a9538bf1d33aecabc2b1abbc32779950a..2add65446ea6ca429a204a1564813d9fe19e25d0 100644 --- a/server/app/__init__.py +++ b/server/app/__init__.py @@ -34,7 +34,7 @@ def create_app(config_name="configmodule.DevelopmentConfig"): return redirect(rp[:-1]) @app.after_request - def set_core(response): + def set_corse(response): header = response.headers header["Access-Control-Allow-Origin"] = "*" return response diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index d56a2f8ba0163a9187aa451eac45a416890052c6..ce7b4e7d3bb8a550aa52cbc0b2492c1118999876 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -13,7 +13,7 @@ schema = QuestionAlternativeDTO.schema list_schema = QuestionAlternativeDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id, slide_id, question_id") class QuestionAlternativeList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 308b1001de9fa325b7ff01c4e518f7db9e3c10eb..0ef3003931d52d304d66cff1cedb36b0c8cfa777 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -13,7 +13,7 @@ schema = QuestionAnswerDTO.schema list_schema = QuestionAnswerDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id, team_id") class QuestionAnswerList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py index c761420fabab70254973a7d352ce6f9b7833ba25..2a2eea5c55c6e46cf516ff35dd79ed3d96f58679 100644 --- a/server/app/apis/codes.py +++ b/server/app/apis/codes.py @@ -12,7 +12,7 @@ schema = CodeDTO.schema list_schema = CodeDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class CodesList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index 386d4051c5e32f852a9a165810e3303812e58440..4a6bf79d6bf17d132e3a5fed6a403b21762bbc13 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -15,7 +15,7 @@ rich_schema = CompetitionDTO.rich_schema list_schema = CompetitionDTO.list_schema -@api.route("/") +@api.route("") class CompetitionsList(Resource): @check_jwt(editor=True) def post(self): diff --git a/server/app/apis/components.py b/server/app/apis/components.py index c895f02322d16c8f603b97c7c60644db4ea36233..23d250256120f4bf600b10cd010b5de9aa67e0dc 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -35,7 +35,7 @@ class ComponentByID(Resource): return {}, codes.NO_CONTENT -@api.route("/") +@api.route("") @api.param("competition_id, slide_id") class ComponentList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py index 5364a5e44d4e309c327bf91121bc1f8d95e40afa..8b3bd5e57e5b877fdaed85f3e6a372b4984d8ef8 100644 --- a/server/app/apis/misc.py +++ b/server/app/apis/misc.py @@ -1,7 +1,8 @@ import app.database.controller as dbc from app.apis import check_jwt, item_response, list_response +from app.core import http_codes from app.core.dto import MiscDTO -from app.database.models import City, ComponentType, MediaType, QuestionType, Role, ViewType +from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType from flask_jwt_extended import jwt_required from flask_restx import Resource, reqparse @@ -72,3 +73,13 @@ class Cities(Resource): dbc.delete.default(item) items = dbc.get.all(City) return list_response(city_schema.dump(items)) + + +@api.route("/statistics") +class Statistics(Resource): + @check_jwt(editor=True) + def get(self): + user_count = User.query.count() + competition_count = Competition.query.count() + region_count = City.query.count() + return {"users": user_count, "competitions": competition_count, "regions": region_count}, http_codes.OK diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index ef8cf89c463519b8d60d78ec37bc440c494b8018..9ca4a5f229488e03931ec544dd3ccf43406e5b9e 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -12,7 +12,7 @@ schema = SlideDTO.schema list_schema = SlideDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class SlidesList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py index 6596244c9085b850106e567a6974ced5428413aa..514748ae27c7ff685d2b1099d1bf710610928860 100644 --- a/server/app/apis/teams.py +++ b/server/app/apis/teams.py @@ -12,7 +12,7 @@ schema = TeamDTO.schema list_schema = TeamDTO.list_schema -@api.route("/") +@api.route("") @api.param("competition_id") class TeamsList(Resource): @check_jwt(editor=True) diff --git a/server/app/apis/users.py b/server/app/apis/users.py index 767f01cb441553e717aea58a202a03f0b50ece64..1bae000bc6821ff7c7ac3dde82474bd351c2fe4d 100644 --- a/server/app/apis/users.py +++ b/server/app/apis/users.py @@ -27,7 +27,7 @@ def edit_user(item_user, args): return dbc.edit.default(item_user, **args) -@api.route("/") +@api.route("") class UsersList(Resource): @check_jwt(editor=True) def get(self): diff --git a/server/app/core/sockets.py b/server/app/core/sockets.py index f4909531961078663f4e9d014311fec126afd560..04099ff2fc3268301d6a1e5cc130f30774a8bc16 100644 --- a/server/app/core/sockets.py +++ b/server/app/core/sockets.py @@ -1,11 +1,18 @@ import app.database.controller as dbc from app.core import db -from app.database.models import Competition, Slide, Team, ViewType +from app.database.models import Competition, Slide, Team, ViewType, Code from flask.globals import request from flask_socketio import SocketIO, emit, join_room +import logging -# Presentation is an active competition +logger = logging.getLogger(__name__) +logger.propagate = False +logger.setLevel(logging.INFO) +formatter = logging.Formatter('[%(levelname)s] %(funcName)s: %(message)s') +stream_handler = logging.StreamHandler() +stream_handler.setFormatter(formatter) +logger.addHandler(stream_handler) sio = SocketIO(cors_allowed_origins="http://localhost:3000") @@ -14,7 +21,7 @@ presentations = {} @sio.on("connect") def connect(): - print(f"[Connected]: {request.sid}") + logger.info(f"Client '{request.sid}' connected") @sio.on("disconnect") @@ -22,23 +29,22 @@ def disconnect(): for competition_id, presentation in presentations.items(): if request.sid in presentation["clients"]: del presentation["clients"][request.sid] + logger.debug(f"Client '{request.sid}' left presentation '{competition_id}'") break if presentations and not presentations[competition_id]["clients"]: del presentations[competition_id] + logger.info(f"No people left in presentation '{competition_id}', ended presentation") - print(f"{presentations=}") - - print(f"[Disconnected]: {request.sid}") + logger.info(f"Client '{request.sid}' disconnected") @sio.on("start_presentation") def start_presentation(data): competition_id = data["competition_id"] - # TODO: Do proper error handling if competition_id in presentations: - print("THAT PRESENTATION IS ALREADY ACTIVE") + logger.error(f"Client '{request.sid}' failed to start competition '{competition_id}', presentation already active") return presentations[competition_id] = { @@ -47,40 +53,44 @@ def start_presentation(data): "timer": {"enabled": False, "start_value": None, "value": None}, } - print(f"{presentations=}") - join_room(competition_id) - print(f"[start_presentation]: {request.sid} -> {competition_id}.") + logger.debug(f"Client '{request.sid}' joined room {competition_id}") + logger.info(f"Client '{request.sid}' started competition '{competition_id}'") @sio.on("end_presentation") def end_presentation(data): competition_id = data["competition_id"] if competition_id not in presentations: - print("NO PRESENTATION WITH THAT NAME EXISTS") + logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', no such presentation exists") + return + + if request.sid not in presentations[competition_id]["clients"]: + logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', client not in presentation") return if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator": - print("YOU DONT HAVE ACCESS TO DO THAT") + logger.error(f"Client '{request.sid}' failed to end presentation '{competition_id}', client is not operator") return del presentations[competition_id] - - print(f"{presentations=}") + logger.debug(f"Deleted presentation {competition_id}") emit("end_presentation", room=competition_id, include_self=True) + logger.debug(f"Emitting event 'end_presentation' to room {competition_id} including self") + + logger.info(f"Client '{request.sid}' ended presentation '{competition_id}'") @sio.on("join_presentation") def join_presentation(data): team_view_id = 1 code = data["code"] - item_code = dbc.get.code_by_code(code) + item_code = db.session.query(Code).filter(Code.code == code).first() - # TODO: Do proper error handling if not item_code: - print("CODE DOES NOT EXIST") + logger.error(f"Client '{request.sid}' failed to join presentation with code '{code}', no such code exists") return competition_id = ( @@ -90,22 +100,22 @@ def join_presentation(data): ) if competition_id not in presentations: - print("THAT COMPETITION IS CURRENTLY NOT ACTIVE") + logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', no such presentation exists") return if request.sid in presentations[competition_id]["clients"]: - print("CLIENT ALREADY IN COMPETITION") + logger.error(f"Client '{request.sid}' failed to join presentation '{competition_id}', client already in presentation") return # TODO: Write function in database controller to do this view_type_name = db.session.query(ViewType).filter(ViewType.id == item_code.view_type_id).one().name presentations[competition_id]["clients"][request.sid] = {"view_type": view_type_name} - join_room(competition_id) - print(f"{presentations=}") + join_room(competition_id) + logger.debug(f"Client '{request.sid}' joined room {competition_id}") - print(f"[Join presentation]: {request.sid} -> {competition_id}. {view_type_name=}") + logger.info(f"Client '{request.sid}' joined competition '{competition_id}'") @sio.on("set_slide") @@ -114,43 +124,52 @@ def set_slide(data): slide_order = data["slide_order"] if competition_id not in presentations: - print("CANT SET SLIDE IN NON ACTIVE COMPETITION") + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists") + return + + if request.sid not in presentations[competition_id]["clients"]: + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation") return if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator": - print("YOU DONT HAVE ACCESS TO DO THAT") + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator") return num_slides = db.session.query(Slide).filter(Slide.competition_id == competition_id).count() if not (0 <= slide_order < num_slides): - print("CANT CHANGE TO NON EXISTENT SLIDE") + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', slide number {slide_order} does not exist") return presentations[competition_id]["slide"] = slide_order - print(f"{presentations=}") - emit("set_slide", {"slide_order": slide_order}, room=competition_id, include_self=True) - print(f"[Set slide]: {slide_order} -> {competition_id}") + logger.debug(f"Emitting event 'set_slide' to room {competition_id} including self") + + logger.info(f"Client '{request.sid}' set slide '{slide_order}' in competition '{competition_id}'") @sio.on("set_timer") -def sync_timer(data): +def set_timer(data): competition_id = data["competition_id"] timer = data["timer"] if competition_id not in presentations: - print("CANT SET TIMER IN NON EXISTENT COMPETITION") + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', no such presentation exists") + return + + if request.sid not in presentations[competition_id]["clients"]: + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client not in presentation") return if presentations[competition_id]["clients"][request.sid]["view_type"] != "Operator": - print("YOU DONT HAVE ACCESS TO DO THAT") + logger.error(f"Client '{request.sid}' failed to set slide in presentation '{competition_id}', client is not operator") return # TODO: Save timer in presentation, maybe? - print(f"{presentations=}") - emit("set_timer", {"timer": timer}, room=competition_id, include_self=True) - print(f"[Set timer]: {timer=}, {competition_id=}") + logger.debug(f"Emitting event 'set_timer' to room {competition_id} including self") + + logger.info(f"Client '{request.sid}' set timer '{timer}' in presentation '{competition_id}'") + diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py index 15e908ef401c7e63b99c5767bcc5440ea5e7a820..6787c92c1a2dd873d6ae4c5304daca9b0201409c 100644 --- a/server/app/database/controller/get.py +++ b/server/app/database/controller/get.py @@ -39,13 +39,10 @@ def code_by_code(code): def code_list(competition_id): """ Gets a list of all code objects associated with a the provided competition. """ - - team_view_id = 1 - join_filters = (Code.view_type_id == team_view_id) & (Team.id == Code.pointer) - filters = ((Code.view_type_id != team_view_id) & (Code.pointer == competition_id))( - (Code.view_type_id == team_view_id) & (competition_id == Team.competition_id) - ) - return Code.query.join(Team, join_filters, isouter=True).filter(filters).all() + # team_view_id = 1 + join_competition = Competition.id == Code.pointer + filters = Competition.id == competition_id + return Code.query.join(Competition, join_competition).filter(filters).all() ### Users ### @@ -89,6 +86,12 @@ def slide_count(competition_id): return Slide.query.filter(Slide.competition_id == competition_id).count() +def slide_count(competition_id): + """ Gets the number of slides in the provided competition. """ + + return Slide.query.filter(Slide.competition_id == competition_id).count() + + ### Teams ### def team(competition_id, team_id): """ Gets the team object associated with the provided id and competition id. """ @@ -106,6 +109,8 @@ def team_list(competition_id): return Team.query.join(Competition, join_competition).filter(filters).all() + return Team.query.join(Competition, join_competition).filter(filters).all() + ### Questions ### def question(competition_id, slide_id, question_id): @@ -176,6 +181,14 @@ def question_alternative_list(competition_id, slide_id, question_id): .all() ) + return ( + QuestionAlternative.query.join(Competition, join_competition) + .join(Slide, join_slide) + .join(Question, join_question) + .filter(filters) + .all() + ) + ### Question Answers ### def question_answer(competition_id, team_id, answer_id):