diff --git a/client/src/pages/admin/competitions/AddCompetition.tsx b/client/src/pages/admin/competitions/AddCompetition.tsx index e2bb126ee72462da94433b3b15c64ae1783dc228..c4a0e78bd4afeb673876d1f7c361af224226762f 100644 --- a/client/src/pages/admin/competitions/AddCompetition.tsx +++ b/client/src/pages/admin/competitions/AddCompetition.tsx @@ -75,7 +75,10 @@ const AddCompetition: React.FC = (props: any) => { .catch(({ response }) => { console.warn(response.data) if (response?.status === 409) - actions.setFieldError('error', 'En tävling med det namnet finns redan, välj ett nytt namn och försök igen') + actions.setFieldError( + 'error', + 'Denna tävling finns redan, välj ett nytt namn, region eller år och försök igen' + ) else if (response.data && response.data.message) actions.setFieldError('error', response.data && response.data.message) else actions.setFieldError('error', 'Någonting gick fel, försök igen') diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py index 86b6e4b6875dfd66cbe33c04b7a5595bc70b7686..1f5ebca00eeaa96d4bdd722819cba2aa79a8f79f 100644 --- a/server/app/database/controller/add.py +++ b/server/app/database/controller/add.py @@ -116,11 +116,11 @@ def team(name, competition_id): return item -def slide(competition_id): +def slide(competition_id, order=None): """ Adds a slide to the provided competition. """ - - # Get the last order from given competition - order = dbc.utils.count(Slide, {"competition_id": competition_id}) + if not order: + # Get the last order from given competition + order = dbc.utils.count(Slide, {"competition_id": competition_id}) # Add slide item_slide = db_add(Slide(order, competition_id)) diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py index f2a06fac5a990d5857abff7526c3aaa2344ddf24..d64d8a5bb43b91e3eff9935df74ed6d3a48901fe 100644 --- a/server/app/database/controller/copy.py +++ b/server/app/database/controller/copy.py @@ -4,16 +4,15 @@ This file contains functionality to copy and duplicate data to the database. from app.database.controller import add, get, search, utils from app.database.models import Question -from app.database.types import (IMAGE_COMPONENT_ID, QUESTION_COMPONENT_ID, - TEXT_COMPONENT_ID) +from app.database.types import IMAGE_COMPONENT_ID, QUESTION_COMPONENT_ID, TEXT_COMPONENT_ID -def _alternative(item_old, question_id): +def _alternative(item_alternative_old, question_id): """ Internal function. Makes a copy of the provided question alternative. """ - return add.question_alternative(item_old.text, item_old.value, question_id) + return add.question_alternative(item_alternative_old.alternative, item_alternative_old.correct, question_id) def _question(item_question_old, slide_id): @@ -91,7 +90,7 @@ def slide_to_competition(item_slide_old, item_competition): Does not copy team, question answers. """ - item_slide_new = add.slide(item_competition.id) + item_slide_new = add.slide(item_competition.id, item_slide_old.order) # Copy all fields item_slide_new.title = item_slide_old.title diff --git a/server/app/database/models.py b/server/app/database/models.py index 7596ab6b99b3bacc22c8f80f03578a5dede09f6f..7371bc773c9e66ef2c89be55e23e2501a43e11b0 100644 --- a/server/app/database/models.py +++ b/server/app/database/models.py @@ -5,8 +5,7 @@ each other. """ from app.core import bcrypt, db -from app.database.types import (IMAGE_COMPONENT_ID, QUESTION_COMPONENT_ID, - TEXT_COMPONENT_ID) +from app.database.types import IMAGE_COMPONENT_ID, QUESTION_COMPONENT_ID, TEXT_COMPONENT_ID from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property from sqlalchemy.orm import backref @@ -145,8 +144,9 @@ class Competition(db.Model): Depend on table: Media, City. """ + __table_args__ = (db.UniqueConstraint("name", "year", "city_id"),) id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(STRING_SIZE), unique=True) + name = db.Column(db.String(STRING_SIZE), nullable=False) year = db.Column(db.Integer, nullable=False, default=2020) font = db.Column(db.String(STRING_SIZE), nullable=False) city_id = db.Column(db.Integer, db.ForeignKey("city.id"), nullable=False) @@ -242,6 +242,7 @@ class QuestionAlternative(db.Model): Depend on table: Question. """ + __table_args__ = ( db.UniqueConstraint("question_id", "alternative_order"), db.UniqueConstraint("question_id", "correct_order"), diff --git a/server/populate.py b/server/populate.py index 9ea096cdf49c3821f30a34903bbffc408faa0ba4..71aba44a517e05cfcc7bacbaeb0b3ab445cd976f 100644 --- a/server/populate.py +++ b/server/populate.py @@ -16,7 +16,17 @@ def create_default_items(): view_types = ["Team", "Judge", "Audience", "Operator"] roles = ["Admin", "Editor"] - cities = ["Linköping", "Stockholm", "Norrköping", "Örkelljunga"] + cities = [ + "Linköping", + "Stockholm", + "Norrköping", + "Örkelljunga", + "Västerås", + "Falun", + "Sundsvall", + "Göteborg", + "Malmö", + ] teams = ["Högstadie A", "Högstadie B", "Högstadie C"] for name in media_types: @@ -46,11 +56,27 @@ def create_default_items(): dbc.add.user("admin@test.se", "password", admin_id, city_id, "Admina Denfina") dbc.add.user("test@test.se", "password", editor_id, city_id, "Test Osteron") + dbc.add.user("sven@test.se", "password", editor_id, 1, "Sven Mattson") + dbc.add.user("erika@test.se", "password", editor_id, 2, "Erika Malmberg") + dbc.add.user("anette@test.se", "password", editor_id, 3, "Anette Frisk") + dbc.add.user("emil@test.se", "password", editor_id, 4, "Emil Svensson") + dbc.add.user("david@test.se", "password", editor_id, 5, "David Ek") + + dbc.add.competition(f"Regionfinal", 2012, 1) + dbc.add.competition(f"Regionfinal", 2012, 2) + dbc.add.competition(f"Regionfinal", 2012, 3) + dbc.add.competition(f"Regionfinal", 2012, 4) + dbc.add.competition(f"Regionfinal", 2012, 5) + dbc.add.competition(f"Rikssemifinal", 2012, 6) + dbc.add.competition(f"Rikssemifinal", 2012, 7) + dbc.add.competition(f"Rikssemifinal", 2012, 8) + dbc.add.competition(f"Riksfinal", 2012, 9) + question_types_items = dbc.get.all(QuestionType) # Add competitions for i in range(len(question_types_items)): - item_comp = dbc.add.competition(f"Tävling {i}", 2000 + i, city_id) + item_comp = dbc.add.competition(f"Tävling {i}", 3000 + i, city_id) dbc.edit.default(item_comp.slides[0], timer=5, title="test-slide-title") # Add two more slides to competition @@ -67,17 +93,16 @@ def create_default_items(): dbc.utils.commit_and_refresh(item_slide) # Add question to competition - """ + item_question = dbc.add.question( name=f"Question {j}: {question_types_items[j].name}", total_score=j, type_id=question_types_items[j].id, slide_id=item_slide.id, ) - """ for k in range(3): - dbc.add.question_alternative(f"Alternative {k}", f"Correct {k}", item_slide.questions[0].id) + dbc.add.question_alternative(f"Alternative {k}", f"Correct {k}", item_question.id) # Add text components # TODO: Add images as components diff --git a/server/tests/test_app.py b/server/tests/test_app.py index e4e46c686c88b6ab728223fc1f1011fcb0d6bf4e..48fbaef0ed13ebda3bdc721424beb23b07b225bc 100644 --- a/server/tests/test_app.py +++ b/server/tests/test_app.py @@ -9,8 +9,7 @@ import pytest from app.core import sockets from tests import app, client, db -from tests.test_helpers import (add_default_values, change_order_test, delete, - get, post, put) +from tests.test_helpers import add_default_values, change_order_test, delete, get, post, put # @pytest.mark.skip(reason="Takes long time") @@ -404,8 +403,9 @@ def test_question_api(client): # Get questions from another competition that should have some questions CID = 3 response, body = get(client, f"/api/competitions/{CID}/questions", headers=headers) + num_questions = 3 assert response.status_code == codes.OK - assert body["count"] == 0 + assert body["count"] == num_questions # Add question name = "Nytt namn" @@ -420,11 +420,12 @@ def test_question_api(client): assert response.status_code == codes.OK assert item_question["name"] == name assert item_question["type_id"] == type_id + num_questions += 1 # Checks number of questions response, body = get(client, f"/api/competitions/{CID}/questions", headers=headers) assert response.status_code == codes.OK - assert body["count"] == 1 + assert body["count"] == num_questions """ # Delete question response, _ = delete(client, f"/api/competitions/{CID}/slides/{slide_order}/questions/{QID}", headers=headers) diff --git a/server/tests/test_db.py b/server/tests/test_db.py index 649ebbf14e43d50a7279c7ed484383bf3aba8782..82a75a3f6f8916efefed99de346e22b91e1b0f54 100644 --- a/server/tests/test_db.py +++ b/server/tests/test_db.py @@ -78,7 +78,7 @@ def test_media(client): def test_copy(client): add_default_values() - # Fetches an empty competition + # Fetches a competition list_item_competitions, _ = dbc.search.competition(name="Tävling 1") item_competition_original = list_item_competitions[0] @@ -86,13 +86,22 @@ def test_copy(client): num_slides = 3 item_slides, total = dbc.search.slide(competition_id=item_competition_original.id) assert total == num_slides - item_slide_original = item_slides[0] + item_slide_original = item_slides[1] + + dbc.delete.slide(item_slides[0]) + num_slides -= 1 # Inserts several copies of the same slide num_copies = 3 for _ in range(num_copies): + # Slide must contain some of these things to copy + assert len(item_slide_original.components) > 0 + assert len(item_slide_original.questions) > 0 + assert len(item_slide_original.questions[0].alternatives) > 0 + item_slide_copy = dbc.copy.slide(item_slide_original) num_slides += 1 + check_slides_copy(item_slide_original, item_slide_copy, num_slides, num_slides - 1) assert item_slide_copy.competition_id == item_slide_original.competition_id @@ -100,6 +109,7 @@ def test_copy(client): num_copies = 3 for _ in range(num_copies): item_competition_copy = dbc.copy.competition(item_competition_original) + assert len(item_competition_copy.slides) > 0 for order, item_slide in enumerate(item_competition_copy.slides): item_slide_original = item_competition_original.slides[order] check_slides_copy(item_slide_original, item_slide, num_slides, order) @@ -126,8 +136,11 @@ def test_copy(client): def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order): - """ Checks that two slides are correct copies of each other. Looks big but is quite fast. """ - assert item_slide_copy.order == order # 0 indexing + """ + Checks that two slides are correct copies of each other. + This function looks big but is quite fast. + """ + assert item_slide_copy.order == order assert item_slide_copy.title == item_slide_original.title assert item_slide_copy.body == item_slide_original.body assert item_slide_copy.timer == item_slide_original.timer @@ -151,6 +164,7 @@ def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order): assert c1.text == c2.text elif c1.type_id == 2: assert c1.image_id == c2.image_id + # Checks that all questions were correctly copied questions = item_slide_original.questions questions_copy = item_slide_copy.questions @@ -170,10 +184,12 @@ def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order): assert len(alternatives) == len(alternatives_copy) for a1, a2 in zip(alternatives, alternatives_copy): - assert a1.text == a2.text - assert a1.value == a2.value - assert a1.quesiton_id == q1.id - assert a2.quesiton_id == q2.id + assert a1.alternative == a2.alternative + assert a1.alternative_order == a2.alternative_order + assert a1.correct == a2.correct + assert a1.correct_order == a2.correct_order + assert a1.question_id == q1.id + assert a2.question_id == q2.id # Checks that the copy put the slide in the database item_slides, total = dbc.search.slide( diff --git a/server/tests/test_helpers.py b/server/tests/test_helpers.py index 7bcda31a086e687f9bb3e1b96f263d2cc34a482f..f5abbfcbe1991bf379e5c9020b99088122d6d6e9 100644 --- a/server/tests/test_helpers.py +++ b/server/tests/test_helpers.py @@ -67,7 +67,10 @@ def add_default_values(): dbc.utils.commit_and_refresh(item_slide) # Add question to competition - # dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, slide_id=item_slide.id) + item_question = dbc.add.question(name=f"Q{i+1}", total_score=i + 1, type_id=1, slide_id=item_slide.id) + + for k in range(3): + dbc.add.question_alternative(f"Alternative {k}", f"Correct {k}", item_question.id) # Add text component dbc.add.component(1, item_slide.id, 1, i, 2 * i, 3 * i, 4 * i, text="Text")