Skip to content

Commit

Permalink
Merge pull request #280 from Arquisoft/develop
Browse files Browse the repository at this point in the history
added tests for coverage and flag questions
  • Loading branch information
Toto-hitori authored Apr 24, 2024
2 parents 3b20f67 + e37ad64 commit 5596ad0
Show file tree
Hide file tree
Showing 15 changed files with 600 additions and 336 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package lab.en2b.quizapi.questions.answer;

public enum AnswerCategory {
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE, COUNTRY_FLAG
}

5 changes: 5 additions & 0 deletions questiongenerator/src/main/java/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public static void main(String[] args) {
new WhosThatPokemonQuestion("es");
}

if (GeneralRepositoryStorer.doesntExist(AnswerCategory.COUNTRY_FLAG)) {
new CountryFlagQuestion("en");
new CountryFlagQuestion("es");
}

/*
// VIDEOS not yet supported
if(GeneralRepositoryStorer.doesntExist(AnswerCategory.SONG.toString())) {
Expand Down
2 changes: 1 addition & 1 deletion questiongenerator/src/main/java/model/AnswerCategory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package model;

public enum AnswerCategory {
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE
CAPITAL_CITY, COUNTRY, SONG, STADIUM, BALLON_DOR, GAMES_PUBLISHER, PAINTING, WTPOKEMON, GAMES_COUNTRY, GAMES_GENRE, BASKETBALL_VENUE, COUNTRY_FLAG
}

77 changes: 77 additions & 0 deletions questiongenerator/src/main/java/templates/CountryFlagQuestion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package templates;

import model.QuestionCategory;
import model.QuestionType;
import model.Answer;
import model.AnswerCategory;
import model.Question;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class CountryFlagQuestion extends QuestionTemplate {

private static final String[] spanishStringsIni = {"¿Que país tiene esta bandera? ", "¿A qué país pertenece esta bandera? ", "¿De qué país es esta bandera? ", "¿Cuál es el país de esta bandera? "};
private static final String[] englishStringsIni= {"Which country has this flag? ", "To which country belongs this flag? ", "From which country is this flag? ", "What is the country represented by this flag? "};

List<String> countryLabels;

public CountryFlagQuestion(String langCode) {
super(langCode);
}

@Override
public void setQuery() {
this.sparqlQuery = "SELECT ?countryLabel ?flagLabel\n" +
"WHERE " +
"{ " +
" ?country wdt:P31 wd:Q6256; " +
" wdt:P41 ?flag. " +
" SERVICE wikibase:label { bd:serviceParam wikibase:language \"" + langCode + "\". } " +
"}";
}

@Override
public void processResults() {
countryLabels = new ArrayList<>();
List<Question> questions = new ArrayList<>();
List<Answer> answers = new ArrayList<>();

for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
String countryLabel = result.getJSONObject("countryLabel").getString("value");
String flagLabel = result.getJSONObject("flagLabel").getString("value");

if (needToSkip(countryLabel, flagLabel)) {
continue;
}

Answer a = new Answer(countryLabel, AnswerCategory.COUNTRY_FLAG, langCode);
answers.add(a);

if (langCode.equals("es")){
String questionString = spanishStringsIni[i%4] + QGHelper.LINKCONCAT + flagLabel;
questions.add(new Question(a, questionString, QuestionCategory.GEOGRAPHY, QuestionType.IMAGE));
} else {
String questionString = englishStringsIni[i%4] + QGHelper.LINKCONCAT + flagLabel;
questions.add(new Question(a, questionString, QuestionCategory.GEOGRAPHY, QuestionType.IMAGE));
}
}
repository.saveAll(new ArrayList<>(answers));
repository.saveAll(new ArrayList<>(questions));
}

private boolean needToSkip(String countryLabel, String venueLabel){
if (countryLabels.contains(countryLabel)) {
return true;
}
countryLabels.add(countryLabel);

if (QGHelper.isEntityName(countryLabel) || QGHelper.isEntityName(venueLabel)) {
return true;
}

return false;
}
}
6 changes: 3 additions & 3 deletions webapp/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@
"login-send": "Your email or password are not found in our database",
"validation": {
"type": "Validation Error: ",
"message": "Incorrect data"
"message": "Invalid email format"
},
"conflict": {
"type": "Conflict Error: ",
"message": "User already exists"
"message": "Invalid email format or credentials (username or email) already in use"
},
"unknown": {
"type": "Unknown Error",
"message": ""
},
"authorized": {
"type": "Authorization Error: ",
"message": "Incorrect password"
"message": "Invalid email or password, check for them to be correct"
}
},
"rules": {
Expand Down
6 changes: 3 additions & 3 deletions webapp/public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@
"login-send": "Tu email o contraseña no se encuentran en nuestra base de datos",
"validation": {
"type": "Error de Validación: ",
"message": "Datos incorrectos"
"message": "El formato del correo electrónico no es correcto"
},
"conflict": {
"type": "Error de Conflicto: ",
"message": "El usuario ya existe"
"message": "Formato de correo electrónico no válido o credenciales (nombre de usuario o correo electrónico) ya en uso"
},
"unknown": {
"type": "Error Desconocido",
"message": ""
},
"authorized": {
"type": "Error de Autorización: ",
"message": "Contraseña incorrecta"
"message": "Correo electrónico o contraseña no válidos, verifique que sean correctos"
}
},
"rules": {
Expand Down
17 changes: 3 additions & 14 deletions webapp/src/components/auth/AuthManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default class AuthManager {
static #instance = null;
#isLoggedIn = false;
#axiosInstance = null;


constructor() {
if (!AuthManager.#instance) {
Expand Down Expand Up @@ -46,7 +47,7 @@ export default class AuthManager {
throw requestAnswer;
}
} catch (error) {
onError(error);
onError(error);
}
}

Expand Down Expand Up @@ -100,19 +101,7 @@ export default class AuthManager {
throw requestAnswer;
}
} catch (error) {
let errorType;
switch (error.response ? error.response.status : null) {
case 400:
errorType = { type: "error.validation.type", message: "error.validation.message"};
break;
case 409:
errorType = { type: "error.conflict.type", message: "error.conflict.message"};
break;
default:
errorType = { type: "error.unknown.type", message: "error.unknown.message"};
break;
}
onError(errorType);
onError(error);
}
}
}
122 changes: 61 additions & 61 deletions webapp/src/components/dashboard/DashboardButton.jsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,62 @@
import React from "react";
import PropTypes from 'prop-types';
import { Button, Box } from "@chakra-ui/react";
import { FaKiwiBird, FaRandom, FaPalette } from "react-icons/fa";
import { TbWorld } from "react-icons/tb";
import { IoIosFootball, IoLogoGameControllerB } from "react-icons/io";

const DashboardButton = ({ label, selectedButton, onClick, iconName }) => {
const isSelected = label === selectedButton;
let icon = null;

switch (iconName) {
case "FaKiwiBird":
icon = <FaKiwiBird style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "IoIosFootball":
icon = <IoIosFootball style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaGlobeAmericas":
icon = <TbWorld style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "IoLogoGameControllerB":
icon = <IoLogoGameControllerB style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaPalette":
icon = <FaPalette style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaRandom":
icon = <FaRandom style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
default:
break;
}

return (
<Button
colorScheme={"green"}
variant={isSelected ? "solid" : "ghost"}
textAlign="center"
m="1em"
display="flex"
flexDirection="column"
alignItems="center"
size="lg"
height="4rem"
maxW={{ base: "100%", md: "calc(100% / 3 - 2em)" }}
onClick={() => onClick(label)}
>
{icon}
<Box>{label}</Box>
</Button>
);
};

DashboardButton.propTypes = {
label: PropTypes.string.isRequired,
selectedButton: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
iconName: PropTypes.string.isRequired
};

import React from "react";
import PropTypes from 'prop-types';
import { Button, Box } from "@chakra-ui/react";
import { FaKiwiBird, FaRandom, FaPalette } from "react-icons/fa";
import { TbWorld } from "react-icons/tb";
import { IoIosFootball, IoLogoGameControllerB } from "react-icons/io";

const DashboardButton = ({ label, selectedButton, onClick, iconName }) => {
const isSelected = label === selectedButton;
let icon = null;

switch (iconName) {
case "FaKiwiBird":
icon = <FaKiwiBird data-testid={"FaKiwiBird"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "IoIosFootball":
icon = <IoIosFootball data-testid={"IoIosFootball"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaGlobeAmericas":
icon = <TbWorld data-testid={"FaGlobeAmericas"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "IoLogoGameControllerB":
icon = <IoLogoGameControllerB data-testid={"IoLogoGameControllerB"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaPalette":
icon = <FaPalette data-testid={"FaPalette"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
case "FaRandom":
icon = <FaRandom data-testid={"FaRandom"} style={{ marginBottom: '0.5em', marginRight: '0.25em', fontSize: '1.8em' }} />;
break;
default:
break;
}

return (
<Button
colorScheme={"green"}
variant={isSelected ? "solid" : "ghost"}
textAlign="center"
m="1em"
display="flex"
flexDirection="column"
alignItems="center"
size="lg"
height="4rem"
maxW={{ base: "100%", md: "calc(100% / 3 - 2em)" }}
onClick={() => onClick(label)}
>
{icon}
<Box>{label}</Box>
</Button>
);
};

DashboardButton.propTypes = {
label: PropTypes.string.isRequired,
selectedButton: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
iconName: PropTypes.string.isRequired
};

export default DashboardButton;
4 changes: 1 addition & 3 deletions webapp/src/pages/Game.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default function Game() {
if (error.response.status === HttpStatusCode.Conflict) {
throw error;
} else {
console.error("Error fetching question:", error);
navigate("/dashboard");
}
}
Expand Down Expand Up @@ -150,7 +149,6 @@ export default function Game() {
navigate("/dashboard");
}
} catch (error) {
console.error("Error initializing game:", error);
navigate("/dashboard");
}
};
Expand Down Expand Up @@ -198,7 +196,7 @@ export default function Game() {
{
(!loading && hasImage) && <Flex maxH={"40vh"}
maxW={"40vw"} justify={"center"}>
<Image src={question.image} alt={t("game.image")}></Image>
<Image src={question.image} data-testid={"image"} alt={t("game.image")}></Image>
</Flex>
}
<Box bg="white" p={"4 0.5"} borderRadius="md" boxShadow="md" mt={4} mb={4} w={["80%", "60%"]} shadow="2xl" rounded="1rem" alignItems="center">
Expand Down
19 changes: 17 additions & 2 deletions webapp/src/pages/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,24 @@ export default function Login() {
"password": password
};
try {
await new AuthManager().login(loginData, navigateToDashboard, setErrorMessage);
await new AuthManager().login(loginData, navigateToDashboard, setLocalizedErrorMessage);
} catch {
setErrorMessage("Error desconocido");
const message = { type: t("error.login"), message: t("error.login-desc")};
setErrorMessage(message);
}
}

const setLocalizedErrorMessage = (error) => {
switch (error.response.status) {
case 400:
setErrorMessage({ type: t("error.validation.type"), message: t("error.validation.message")});
break;
case 401:
setErrorMessage({ type: t("error.authorized.type"), message: t("error.authorized.message")});
break;
default:
setErrorMessage({ type: t("error.login"), message: t("error.login-desc")});
break;
}
}

Expand Down
9 changes: 5 additions & 4 deletions webapp/src/pages/Signup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,21 @@ export default function Signup() {
try {
await new AuthManager().register(registerData, navigateToDashboard, setLocalizedErrorMessage);
} catch {
setErrorMessage("Error desconocido");
const message = { type: t("error.register"), message: t("error.register-desc")};
setErrorMessage(message);
}
};

const setLocalizedErrorMessage = (error) => {
switch (error.response ? error.response.status : null) {
switch (error.response.status) {
case 400:
setErrorMessage({ type: t("error.validation.type"), message: t("error.validation.message")});
setErrorMessage({ type: t("error.conflict.type"), message: t("error.conflict.message")});
break;
case 401:
setErrorMessage({ type: t("error.authorized.type"), message: t("error.authorized.message")});
break;
default:
setErrorMessage({ type: t("error.unknown.type"), message: t("error.unknown.message")});
setErrorMessage({ type: t("error.register"), message: t("error.register-desc")});
break;
}
}
Expand Down
Loading

0 comments on commit 5596ad0

Please sign in to comment.