Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • tddd96-grupp11/teknikattan-scoring-system
1 result
Select Git revision
Show changes
Showing
with 542 additions and 100 deletions
import Types from '../actions/types'
import citiesReducer from './citiesReducer'
const initialState = {
cities: [],
total: 0,
count: 0,
}
it('should return the initial state', () => {
expect(citiesReducer(undefined, {} as any)).toEqual(initialState)
})
it('should handle SET_CITIES', () => {
const testCities = [{ name: 'testName', id: 0 }]
expect(
citiesReducer(initialState, {
type: Types.SET_CITIES,
payload: testCities,
})
).toEqual({
cities: testCities,
total: 0,
count: 0,
})
})
it('should handle SET_CITIES_TOTAL', () => {
const testTotal = 123123
expect(
citiesReducer(initialState, {
type: Types.SET_CITIES_TOTAL,
payload: testTotal,
})
).toEqual({
cities: [],
total: testTotal,
count: 0,
})
})
it('should handle SET_CITIES_COUNT', () => {
const testCount = 456456
expect(
citiesReducer(initialState, {
type: Types.SET_CITIES_COUNT,
payload: testCount,
})
).toEqual({
cities: [],
total: 0,
count: testCount,
})
})
import Types from '../actions/types'
import { RichSlide } from '../interfaces/ApiRichModels'
import { Slide } from '../interfaces/Slide'
import presentationReducer from './presentationReducer'
const initialState = {
competition: {
name: '',
id: 0,
city: {
id: 0,
name: '',
},
slides: [],
year: 0,
teams: [],
},
slide: {
competition_id: 0,
id: 0,
order: 0,
timer: 0,
title: '',
},
teams: [],
}
it('should return the initial state', () => {
expect(presentationReducer(undefined, {} as any)).toEqual(initialState)
})
it('should handle SET_PRESENTATION_COMPETITION', () => {
const testCompetition = {
name: 'testCompName',
id: 4,
city: {
id: 3,
name: 'testCityName',
},
slides: [{ id: 20 }],
year: 1999,
teams: [],
}
expect(
presentationReducer(initialState, {
type: Types.SET_PRESENTATION_COMPETITION,
payload: testCompetition,
})
).toEqual({
competition: testCompetition,
slide: testCompetition.slides[0],
teams: [],
})
})
it('should handle SET_PRESENTATION_TEAMS', () => {
const testTeams = [
{
name: 'testTeamName1',
id: 3,
},
{
name: 'testTeamName2',
id: 5,
},
]
expect(
presentationReducer(initialState, {
type: Types.SET_PRESENTATION_TEAMS,
payload: testTeams,
})
).toEqual({
competition: initialState.competition,
slide: initialState.slide,
teams: testTeams,
})
})
it('should handle SET_PRESENTATION_SLIDE', () => {
const testSlide = [
{
competition_id: 20,
id: 4,
order: 3,
timer: 123,
title: 'testSlideTitle',
},
]
expect(
presentationReducer(initialState, {
type: Types.SET_PRESENTATION_SLIDE,
payload: testSlide,
})
).toEqual({
competition: initialState.competition,
slide: testSlide,
teams: initialState.teams,
})
})
describe('should handle SET_PRESENTATION_SLIDE_PREVIOUS', () => {
it('by changing slide to the previous if there is one', () => {
const testPresentationState = {
competition: {
...initialState.competition,
slides: [
{ competition_id: 0, order: 0 },
{ competition_id: 0, order: 1 },
] as RichSlide[],
},
teams: initialState.teams,
slide: { competition_id: 0, order: 1 } as Slide,
}
expect(
presentationReducer(testPresentationState, {
type: Types.SET_PRESENTATION_SLIDE_PREVIOUS,
})
).toEqual({
competition: testPresentationState.competition,
slide: testPresentationState.competition.slides[0],
teams: testPresentationState.teams,
})
})
it('by not changing slide if there is no previous one', () => {
const testPresentationState = {
competition: {
...initialState.competition,
slides: [
{ competition_id: 0, order: 0 },
{ competition_id: 0, order: 1 },
] as RichSlide[],
},
teams: initialState.teams,
slide: { competition_id: 0, order: 0 } as Slide,
}
expect(
presentationReducer(testPresentationState, {
type: Types.SET_PRESENTATION_SLIDE_PREVIOUS,
})
).toEqual({
competition: testPresentationState.competition,
slide: testPresentationState.competition.slides[0],
teams: testPresentationState.teams,
})
})
})
describe('should handle SET_PRESENTATION_SLIDE_NEXT', () => {
it('by changing slide to the next if there is one', () => {
const testPresentationState = {
competition: {
...initialState.competition,
slides: [
{ competition_id: 0, order: 0 },
{ competition_id: 0, order: 1 },
] as RichSlide[],
},
teams: initialState.teams,
slide: { competition_id: 0, order: 0 } as Slide,
}
expect(
presentationReducer(testPresentationState, {
type: Types.SET_PRESENTATION_SLIDE_NEXT,
})
).toEqual({
competition: testPresentationState.competition,
slide: testPresentationState.competition.slides[1],
teams: testPresentationState.teams,
})
})
it('by not changing slide if there is no next one', () => {
const testPresentationState = {
competition: {
...initialState.competition,
slides: [
{ competition_id: 0, order: 0 },
{ competition_id: 0, order: 1 },
] as RichSlide[],
},
teams: initialState.teams,
slide: { competition_id: 0, order: 1 } as Slide,
}
expect(
presentationReducer(testPresentationState, {
type: Types.SET_PRESENTATION_SLIDE_NEXT,
})
).toEqual({
competition: testPresentationState.competition,
slide: testPresentationState.competition.slides[1],
teams: testPresentationState.teams,
})
})
})
import Types from '../actions/types'
import userReducer from './userReducer'
const initialState = {
authenticated: false,
loading: false,
userInfo: null,
}
it('should return the initial state', () => {
expect(userReducer(undefined, {} as any)).toEqual(initialState)
})
it('should handle SET_AUTHENTICATED', () => {
expect(
userReducer(initialState, {
type: Types.SET_AUTHENTICATED,
})
).toEqual({
authenticated: true,
loading: initialState.loading,
userInfo: initialState.userInfo,
})
})
it('should handle SET_UNAUTHENTICATED', () => {
expect(
userReducer(initialState, {
type: Types.SET_UNAUTHENTICATED,
})
).toEqual(initialState)
})
it('should handle SET_USER', () => {
const testUserInfo = {
name: 'testName',
email: 'test@email.com',
role: { id: 0, name: 'roleName' },
city: { id: 0, name: 'cityName' },
id: 0,
}
expect(
userReducer(initialState, {
type: Types.SET_USER,
payload: testUserInfo,
})
).toEqual({
authenticated: true,
loading: false,
userInfo: testUserInfo,
})
})
it('should handle LOADING_USER', () => {
expect(
userReducer(initialState, {
type: Types.LOADING_USER,
})
).toEqual({
loading: true,
authenticated: initialState.authenticated,
userInfo: initialState.userInfo,
})
})
import Types from '../actions/types'
import uiReducer from './uiReducer'
const initialState = {
loading: false,
errors: null,
}
it('should return the initial state', () => {
expect(uiReducer(undefined, {} as any)).toEqual(initialState)
})
it('should handle SET_ERRORS', () => {
const testError = { message: 'errorMessage' }
expect(
uiReducer(initialState, {
type: Types.SET_ERRORS,
payload: testError,
})
).toEqual({
loading: false,
errors: testError,
})
})
it('should handle CLEAR_ERRORS', () => {
expect(
uiReducer(initialState, {
type: Types.CLEAR_ERRORS,
})
).toEqual({
loading: false,
errors: null,
})
})
it('should handle LOADING_UI', () => {
expect(
uiReducer(initialState, {
type: Types.LOADING_UI,
})
).toEqual({
loading: true,
errors: initialState.errors,
})
})
import mockedAxios from 'axios'
import Types from '../actions/types'
import store from '../store'
import { CheckAuthentication } from './checkAuthentication'
it('dispatches correct actions when auth token is ok', async () => {
const userRes: any = {
data: {
name: 'username',
},
}
;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
return Promise.resolve(userRes)
})
const spy = jest.spyOn(store, 'dispatch')
const testToken =
'Bearer eyJ0eXAiOiJeyJ0eXAiOiJKV1QeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSciLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxScKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSc'
localStorage.setItem('token', testToken)
await CheckAuthentication()
expect(spy).toBeCalledWith({ type: Types.LOADING_USER })
expect(spy).toBeCalledWith({ type: Types.SET_AUTHENTICATED })
expect(spy).toBeCalledWith({ type: Types.SET_USER, payload: userRes.data })
expect(spy).toBeCalledTimes(3)
})
it('dispatches correct actions when getting user data fails', async () => {
console.log = jest.fn()
;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => {
return Promise.reject(new Error('failed getting user data'))
})
const spy = jest.spyOn(store, 'dispatch')
const testToken =
'Bearer eyJ0eXAiOiJeyJ0eXAiOiJKV1QeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSceyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSciLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxScKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDQ5MzksImV4cCI6MzI1MTI1MjU3NTcsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJHaXZlbk5hbWUiOiJKb2hubnkiLCJTdXJuYW1lIjoiUm9ja2V0IiwiRW1haWwiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.DrOOcCo5jMR-SNERE0uRp_kolQ2HjX8-hHXYEMnIxSc'
localStorage.setItem('token', testToken)
await CheckAuthentication()
expect(spy).toBeCalledWith({ type: Types.LOADING_USER })
expect(spy).toBeCalledWith({ type: Types.SET_UNAUTHENTICATED })
expect(spy).toBeCalledTimes(2)
expect(console.log).toHaveBeenCalled()
})
it('dispatches no actions when no token exists', async () => {
const spy = jest.spyOn(store, 'dispatch')
await CheckAuthentication()
expect(spy).not.toBeCalled()
})
it('dispatches correct actions when token is expired', async () => {
const testToken =
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2MTgzMDY1MTUsImV4cCI6MTU4Njc3MDUxNSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.R5-oWGGumd-YWPoKyziJmVB8SdX6B9SsV6m7novIfgg'
localStorage.setItem('token', testToken)
const spy = jest.spyOn(store, 'dispatch')
await CheckAuthentication()
expect(spy).toBeCalledWith({ type: Types.SET_UNAUTHENTICATED })
expect(spy).toBeCalledTimes(1)
})
......@@ -15,7 +15,6 @@ export const CheckAuthentication = async () => {
if (decodedToken.exp * 1000 >= Date.now()) {
axios.defaults.headers.common['Authorization'] = authToken
store.dispatch({ type: Types.LOADING_USER })
console.log('loading user')
await axios
.get('/users')
.then((res) => {
......@@ -26,7 +25,7 @@ export const CheckAuthentication = async () => {
})
})
.catch((error) => {
console.error(error)
console.log(error)
UnAuthorized()
})
} else {
......
from flask import Flask, redirect, request
import app.core.models as models
import app.database.models as models
from app.core import bcrypt, db, jwt, ma
......
......@@ -58,3 +58,4 @@ flask_api.add_namespace(comp_ns, path="/api/competitions")
flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides")
flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams")
flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/questions")
#flask_api.add_namespace(question_ns, path="/api/competitions/<CID>/slides/<SID>/question")
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, text_response
from app.core.dto import AuthDTO
from app.core.models import User
from app.core.parsers import create_user_parser, login_parser
from app.database.models import User
from flask_jwt_extended import (
create_access_token,
create_refresh_token,
......@@ -30,14 +30,10 @@ class AuthSignup(Resource):
args = create_user_parser.parse_args(strict=True)
email = args.get("email")
if User.query.filter(User.email == email).count() > 0:
if dbc.get.user_exists(email):
api.abort(codes.BAD_REQUEST, "User already exists")
item_user = dbc.add.user(**args)
# TODO: Clarify when this case is needed or add it to a test
if not item_user:
api.abort(codes.BAD_REQUEST, "User could not be created")
return item_response(schema.dump(item_user))
......@@ -46,10 +42,7 @@ class AuthSignup(Resource):
class AuthDelete(Resource):
@jwt_required
def delete(self, ID):
item_user = User.query.filter(User.id == ID).first()
if not item_user:
api.abort(codes.NOT_FOUND, f"Could not find user with id {ID}.")
item_user = dbc.get.user(ID)
dbc.delete.default(item_user)
if int(ID) == get_jwt_identity():
......@@ -64,7 +57,7 @@ class AuthLogin(Resource):
args = login_parser.parse_args(strict=True)
email = args.get("email")
password = args.get("password")
item_user = User.query.filter_by(email=email).first()
item_user = dbc.get.user_by_email(email, required=False)
if not item_user or not item_user.is_correct_password(password):
api.abort(codes.UNAUTHORIZED, "Invalid email or password")
......@@ -92,7 +85,7 @@ class AuthRefresh(Resource):
def post(self):
old_jti = get_raw_jwt()["jti"]
item_user = User.query.filter_by(id=get_jwt_identity()).first()
item_user = dbc.get.user(get_jwt_identity())
access_token = create_access_token(item_user.id, user_claims=get_user_claims(item_user))
dbc.add.blacklist(old_jti)
response = {"access_token": access_token}
......
import app.core.controller as dbc
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import CompetitionDTO
from app.core.models import Competition
from app.core.parsers import competition_parser, competition_search_parser
from app.database.models import Competition
from flask_jwt_extended import jwt_required
from flask_restx import Resource
......@@ -11,10 +11,6 @@ schema = CompetitionDTO.schema
list_schema = CompetitionDTO.list_schema
def get_comp(CID):
return Competition.query.filter(Competition.id == CID).first()
@api.route("/")
class CompetitionsList(Resource):
@jwt_required
......@@ -34,20 +30,22 @@ class CompetitionsList(Resource):
class Competitions(Resource):
@jwt_required
def get(self, CID):
item = get_comp(CID)
item = dbc.get.competition(CID)
return item_response(schema.dump(item))
@jwt_required
def put(self, CID):
args = competition_parser.parse_args(strict=True)
item = get_comp(CID)
item = dbc.get.competition(CID)
item = dbc.edit.competition(item, **args)
return item_response(schema.dump(item))
@jwt_required
def delete(self, CID):
item = get_comp(CID)
item = dbc.get.competition(CID)
dbc.delete.competition(item)
return "deleted"
......@@ -56,5 +54,5 @@ class CompetitionSearch(Resource):
@jwt_required
def get(self):
args = competition_search_parser.parse_args(strict=True)
items, total = dbc.get.search_competitions(**args)
items, total = dbc.search.competition(**args)
return list_response(list_schema.dump(items), total)
import app.core.controller as dbc
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import MiscDTO
from app.core.models import City, MediaType, QuestionType, Role
from app.database.models import City, MediaType, QuestionType, Role
from flask_jwt_extended import jwt_required
from flask_restx import Resource, reqparse
......
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.controller.add import competition
from app.core.dto import QuestionDTO
from app.core.models import Question
from app.core.parsers import question_parser
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource
from app.database.models import Question
from flask_jwt_extended import jwt_required
from flask_restx import Resource
api = QuestionDTO.api
schema = QuestionDTO.schema
......@@ -18,8 +17,8 @@ list_schema = QuestionDTO.list_schema
class QuestionsList(Resource):
@jwt_required
def get(self, CID):
items, total = dbc.get.search_questions(competition_id=CID)
return list_response(list_schema.dump(items), total)
items = dbc.get.question_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
......@@ -41,25 +40,14 @@ class QuestionsList(Resource):
class Questions(Resource):
@jwt_required
def get(self, CID, QID):
item_question = Question.query.filter(Question.id == QID).first()
if item_question is None:
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID}.")
if item_question.slide.competition.id != int(CID):
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID} in competition with id {CID}.")
item_question = dbc.get.question(CID, QID)
return item_response(schema.dump(item_question))
@jwt_required
def put(self, CID, QID):
args = question_parser.parse_args(strict=True)
print(f"questions 54: {args=}")
item_question = Question.query.filter(Question.id == QID).first()
if item_question.slide.competition.id != int(CID):
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID} in competition with id {CID}.")
item_question = dbc.get.question(CID, QID)
item_question = dbc.edit.question(item_question, **args)
return item_response(schema.dump(item_question))
......@@ -67,8 +55,5 @@ class Questions(Resource):
@jwt_required
def delete(self, CID, QID):
item_question = dbc.get.question(CID, QID)
if not item_question:
return {"response": "No content found"}, codes.NOT_FOUND
dbc.delete.question(item_question)
return {}, codes.NO_CONTENT
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import SlideDTO
from app.core.models import Competition, Slide
from app.core.parsers import slide_parser
from app.database.models import Competition, Slide
from flask_jwt_extended import jwt_required
from flask_restx import Resource
......@@ -12,22 +12,19 @@ schema = SlideDTO.schema
list_schema = SlideDTO.list_schema
def get_comp(CID):
return Competition.query.filter(Competition.id == CID).first()
@api.route("/")
@api.param("CID")
class SlidesList(Resource):
@jwt_required
def get(self, CID):
item_comp = get_comp(CID)
return list_response(list_schema.dump(item_comp.slides))
items = dbc.get.slide_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
item_comp = get_comp(CID)
dbc.add.slide(item_comp)
item_comp = dbc.get.competition(CID)
item_slide = dbc.add.slide(item_comp)
dbc.add.question(f"Fråga {item_slide.order + 1}", 10, 0, item_slide)
dbc.refresh(item_comp)
return list_response(list_schema.dump(item_comp.slides))
......@@ -54,8 +51,6 @@ class Slides(Resource):
@jwt_required
def delete(self, CID, SID):
item_slide = dbc.get.slide(CID, SID)
if not item_slide:
return {"response": "No content found"}, codes.NOT_FOUND
dbc.delete.slide(item_slide)
return {}, codes.NO_CONTENT
......@@ -72,10 +67,10 @@ class SlidesOrder(Resource):
item_slide = dbc.get.slide(CID, SID)
if order == item_slide.order:
api.abort(codes.BAD_REQUEST)
return item_response(schema.dump(item_slide))
# clamp order between 0 and max
order_count = Slide.query.filter(Slide.competition_id == item_slide.competition_id).count()
order_count = dbc.get.slide_count(CID)
if order < 0:
order = 0
elif order >= order_count - 1:
......
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import TeamDTO
from app.core.models import Competition, Team
from app.core.parsers import team_parser
from app.database.models import Competition, Team
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource, reqparse
......@@ -12,22 +12,18 @@ schema = TeamDTO.schema
list_schema = TeamDTO.list_schema
def get_comp(CID):
return Competition.query.filter(Competition.id == CID).first()
@api.route("/")
@api.param("CID")
class TeamsList(Resource):
@jwt_required
def get(self, CID):
item_comp = get_comp(CID)
return list_response(list_schema.dump(item_comp.teams))
items = dbc.get.team_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
args = team_parser.parse_args(strict=True)
item_comp = get_comp(CID)
item_comp = dbc.get.competition(CID)
item_team = dbc.add.team(args["name"], item_comp)
return item_response(schema.dump(item_team))
......@@ -43,8 +39,6 @@ class Teams(Resource):
@jwt_required
def delete(self, CID, TID):
item_team = dbc.get.team(CID, TID)
if not item_team:
api.abort(codes.NOT_FOUND, f"Could not find team with id {TID} in competition with id {CID}.")
dbc.delete.team(item_team)
return {}, codes.NO_CONTENT
......@@ -55,8 +49,6 @@ class Teams(Resource):
name = args.get("name")
item_team = dbc.get.team(CID, TID)
if not item_team:
api.abort(codes.NOT_FOUND, f"Could not find team with id {TID} in competition with id {CID}.")
item_team = dbc.edit.team(item_team, name=name, competition_id=CID)
return item_response(schema.dump(item_team))
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import UserDTO
from app.core.models import User
from app.core.parsers import user_parser, user_search_parser
from app.database.models import User
from flask import request
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource
......@@ -26,13 +26,13 @@ def edit_user(item_user, args):
class UsersList(Resource):
@jwt_required
def get(self):
item = User.query.filter(User.id == get_jwt_identity()).first()
item = dbc.get.user(get_jwt_identity())
return item_response(schema.dump(item))
@jwt_required
def put(self):
args = user_parser.parse_args(strict=True)
item = User.query.filter(User.id == get_jwt_identity()).first()
item = dbc.get.user(get_jwt_identity())
item = edit_user(item, args)
return item_response(schema.dump(item))
......@@ -42,13 +42,13 @@ class UsersList(Resource):
class Users(Resource):
@jwt_required
def get(self, ID):
item = User.query.filter(User.id == ID).first()
item = dbc.get.user(ID)
return item_response(schema.dump(item))
@jwt_required
def put(self, ID):
args = user_parser.parse_args(strict=True)
item = User.query.filter(User.id == ID).first()
item = dbc.get.user(ID)
item = edit_user(item, args)
return item_response(schema.dump(item))
......@@ -58,5 +58,5 @@ class UserSearch(Resource):
@jwt_required
def get(self):
args = user_search_parser.parse_args(strict=True)
items, total = dbc.get.search_user(**args)
items, total = dbc.search.user(**args)
return list_response(list_schema.dump(items), total)
import sqlalchemy as sa
from app.database.base import Base, ExtendedQuery
from flask_bcrypt import Bcrypt
from flask_jwt_extended.jwt_manager import JWTManager
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import Model
from sqlalchemy.sql import func
class Base(Model):
__abstract__ = True
_created = sa.Column(sa.DateTime(timezone=True), server_default=func.now())
_updated = sa.Column(sa.DateTime(timezone=True), onupdate=func.now())
db = SQLAlchemy(model_class=Base)
db = SQLAlchemy(model_class=Base, query_class=ExtendedQuery)
bcrypt = Bcrypt()
jwt = JWTManager()
ma = Marshmallow()
import app.core.models as models
import app.core.schemas as schemas
import app.database.models as models
from app.core import ma
from marshmallow_sqlalchemy import fields
......@@ -29,8 +29,30 @@ class QuestionSchemaRich(RichSchema):
id = ma.auto_field()
name = ma.auto_field()
total_score = ma.auto_field()
slide_id = ma.auto_field()
type = fields.Nested(schemas.QuestionTypeSchema, many=False)
slide = fields.Nested(schemas.SlideSchema, many=False)
class TeamSchemaRich(RichSchema):
class Meta(RichSchema.Meta):
model = models.Team
id = ma.auto_field()
name = ma.auto_field()
competition_id = ma.auto_field()
question_answers = fields.Nested(schemas.QuestionAnswerSchema, many=True)
class SlideSchemaRich(RichSchema):
class Meta(RichSchema.Meta):
model = models.Slide
id = ma.auto_field()
order = ma.auto_field()
title = ma.auto_field()
timer = ma.auto_field()
competition_id = ma.auto_field()
questions = fields.Nested(QuestionSchemaRich, many=True)
class CompetitionSchemaRich(RichSchema):
......@@ -40,5 +62,9 @@ class CompetitionSchemaRich(RichSchema):
id = ma.auto_field()
name = ma.auto_field()
year = ma.auto_field()
slides = fields.Nested(schemas.SlideSchema, many=True)
city = fields.Nested(schemas.CitySchema, many=False)
slides = fields.Nested(
SlideSchemaRich,
many=True,
)
teams = fields.Nested(TeamSchemaRich, many=True)
import app.core.models as models
import app.database.models as models
from app.core import ma
from marshmallow_sqlalchemy import fields
......@@ -29,6 +29,17 @@ class QuestionSchema(BaseSchema):
slide_id = ma.auto_field()
class QuestionAnswerSchema(BaseSchema):
class Meta(BaseSchema.Meta):
model = models.QuestionAnswer
id = ma.auto_field()
data = ma.auto_field()
score = ma.auto_field()
question_id = ma.auto_field()
team_id = ma.auto_field()
class MediaTypeSchema(BaseSchema):
class Meta(BaseSchema.Meta):
model = models.MediaType
......
import app.core.http_codes as codes
import sqlalchemy as sa
from flask_restx import abort
from flask_sqlalchemy import BaseQuery, SQLAlchemy
from flask_sqlalchemy.model import Model
from sqlalchemy.sql import func
class Base(Model):
__abstract__ = True
_created = sa.Column(sa.DateTime(timezone=True), server_default=func.now())
_updated = sa.Column(sa.DateTime(timezone=True), onupdate=func.now())
class ExtendedQuery(BaseQuery):
def first_extended(self, required=True, error_message=None, error_code=codes.NOT_FOUND):
item = self.first()
if required and not item:
if not error_message:
error_message = "Object not found"
abort(error_code, error_message)
return item
def pagination(self, page=0, page_size=15, order_column=None, order=1):
query = self
if order_column:
if order == 1:
query = query.order_by(order_column)
else:
query = query.order_by(order_column.desc())
total = query.count()
query = query.limit(page_size).offset(page * page_size)
items = query.all()
return items, total