Skip to content

Commit

Permalink
Merge pull request #96 from Arquisoft/question-generation
Browse files Browse the repository at this point in the history
First version of the question generator
  • Loading branch information
Pelayori authored Feb 29, 2024
2 parents 5cbe943 + 19e1129 commit 94488b6
Show file tree
Hide file tree
Showing 23 changed files with 843 additions and 3 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.wikidata.wdtk</groupId>
<artifactId>wdtk-dumpfiles</artifactId>
<version>0.14.4</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.uniovi.components;

import com.uniovi.components.generators.geography.CapitalQuestionGenerator;
import com.uniovi.entities.Question;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class QuestionGeneratorTestController {

@Autowired
CapitalQuestionGenerator qgen;

@RequestMapping("/test")
public void test() {
List<Question> q = qgen.getQuestions();
for(Question question : q){
System.out.println(question);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.uniovi.components.generators;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.uniovi.entities.Answer;
import com.uniovi.entities.Category;
import com.uniovi.entities.Question;
import com.uniovi.services.CategoryService;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractQuestionGenerator implements QuestionGenerator{

private List<Question> questions = new ArrayList<>();
protected final CategoryService categoryService;
private String query;
protected String statement;

protected AbstractQuestionGenerator(CategoryService categoryService) {
this.categoryService = categoryService;
}

public void questionGenerator(String statement, List<String> options, String correctAnswer, Category category){
List<Answer> answers = new ArrayList<>();
//Generamos las respuestas y las añadimos a la lista
for(String s: options){
Answer answer = new Answer(s, false);
answers.add(answer);
}
//Generamos la respuesta correcta y la añadimos a la lista
Answer correct = new Answer(correctAnswer, true);
answers.add(correct);

Question question = new Question(statement, answers, correct, category);
question.scrambleOptions();
questions.add(question);
}

public List<Question> getQuestions() {
HttpClient client = HttpClient.newHttpClient();
try {

String endpointUrl = "https://query.wikidata.org/sparql?query=" +
URLEncoder.encode(this.getQuery(), StandardCharsets.UTF_8.toString()) +
"&format=json";

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpointUrl))
.header("Accept", "application/json")
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// Process the JSON response using Jackson ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonResponse = objectMapper.readTree(response.body());

// Access the data from the JSON response
JsonNode resultsNode = jsonResponse.path("results").path("bindings");

for (JsonNode result : resultsNode) {

List<String> options = this.generateOptions(resultsNode, result);
String correctAnswer = this.generateCorrectAnswer(result);
String statement = this.getQuestionSubject(result);
questionGenerator(statement, options, correctAnswer, this.getCategory());

}
} catch (Exception e) {
throw new RuntimeException(e);
}

return questions;
}

protected abstract List<String> generateOptions(JsonNode results, JsonNode result);
protected abstract String generateCorrectAnswer(JsonNode result);

protected abstract String getQuestionSubject(JsonNode result);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.uniovi.components.generators;

import com.uniovi.entities.Category;
import com.uniovi.entities.Question;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public interface QuestionGenerator {

String getQuery();
List<Question> getQuestions();

Category getCategory();


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.uniovi.components.generators.geography;

import com.uniovi.components.generators.AbstractQuestionGenerator;
import com.uniovi.entities.Category;
import com.uniovi.services.CategoryService;

public abstract class AbstractGeographyGenerator extends AbstractQuestionGenerator {

protected AbstractGeographyGenerator(CategoryService categoryService) {
super(categoryService);
}

@Override
public Category getCategory() {
return categoryService.getCategoryByName("Geography");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.uniovi.components.generators.geography;

import com.fasterxml.jackson.databind.JsonNode;
import com.uniovi.services.CategoryService;

import java.util.*;

public class BorderQuestionGenerator extends AbstractGeographyGenerator{

private Set<String> usedCountries = new HashSet<>();

public BorderQuestionGenerator(CategoryService categoryService) {
super(categoryService);
this.statement = "Which countries share a border with ";
}

private List<String> getAllBorderingCountries(JsonNode resultsNode, String correctCountry) {
List<String> allBorderingCountries = new ArrayList<>();
for (JsonNode result : resultsNode) {
String borderingCountry = result.path("borderingCountryLabel").path("value").asText();
if (!borderingCountry.equals(correctCountry)) {
allBorderingCountries.add(borderingCountry);
}
}
return allBorderingCountries;
}

private List<String> selectRandomIncorrectBorderingCountries(List<String> allBorderingCountries, String correctCountry, int count) {
List<String> incorrectBorderingCountries = new ArrayList<>();
Random random = new Random();
while (incorrectBorderingCountries.size() < count && allBorderingCountries.size() > 0) {
int randomIndex = random.nextInt(allBorderingCountries.size());
String selectedBorderingCountry = allBorderingCountries.remove(randomIndex);
if (!selectedBorderingCountry.equals(correctCountry) && !incorrectBorderingCountries.contains(selectedBorderingCountry)) {
incorrectBorderingCountries.add(selectedBorderingCountry);
}
}
return incorrectBorderingCountries;
}

@Override
protected List<String> generateOptions(JsonNode results, JsonNode result) {
String borderingCountryLabel = result.path("borderingCountryLabel").path("value").asText();
return selectRandomIncorrectBorderingCountries(
getAllBorderingCountries(results, borderingCountryLabel),
borderingCountryLabel, 3);
}

@Override
protected String generateCorrectAnswer(JsonNode result) {
return result.path("borderingCountryLabel").path("value").asText();
}

@Override
protected String getQuestionSubject(JsonNode result) {
return this.statement + result.path("countryLabel").path("value").asText() + "?";
}

@Override
public String getQuery() {
return "SELECT DISTINCT ?country ?countryLabel ?borderingCountry ?borderingCountryLabel\n" +
"WHERE {" +
" ?country wdt:P31 wd:Q3624078 ." +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q3024240}" +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q28171280}" +
" ?country wdt:P47 ?borderingCountry ." +
" SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],en\" }" +
"}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.uniovi.components.generators.geography;

import com.fasterxml.jackson.databind.JsonNode;
import com.uniovi.services.CategoryService;
import org.springframework.stereotype.Component;

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

@Component
public class CapitalQuestionGenerator extends AbstractGeographyGenerator{

public CapitalQuestionGenerator(CategoryService categoryService) {
super(categoryService);
this.statement = "What is the capital of ";
}

@Override
public String getQuery() {
return "SELECT DISTINCT ?country ?countryLabel ?capital ?capitalLabel\n" +
"WHERE {" +
" ?country wdt:P31 wd:Q3624078 ." +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q3024240}" +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q28171280}" +
" OPTIONAL { ?country wdt:P36 ?capital } ." +
" SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],en\" }" +
"}" +
"ORDER BY ?countryLabel";
}

private List<String> getAllCapitals(JsonNode resultsNode, String correctCapital) {
// Obtener todas las capitales del JSON (distintas a la capital correcta)
List<String> allCapitals = new ArrayList<>();
for (JsonNode result : resultsNode) {
String capital = result.path("capitalLabel").path("value").asText();
if (!capital.equals(correctCapital)) {
allCapitals.add(capital);
}
}
return allCapitals;
}

private List<String> selectRandomIncorrectCapitals(List<String> allCapitals, String correctCapital, int count) {
List<String> incorrectCapitals = new ArrayList<>();
Random random = new Random();
while (incorrectCapitals.size() < count && allCapitals.size() > 0) {
int randomIndex = random.nextInt(allCapitals.size());
String selectedCapital = allCapitals.remove(randomIndex);
if (!selectedCapital.equals(correctCapital) && !incorrectCapitals.contains(selectedCapital)) {
incorrectCapitals.add(selectedCapital);
}
}
return incorrectCapitals;
}

@Override
protected List<String> generateOptions(JsonNode results, JsonNode result) {
String capitalLabel = result.path("capitalLabel").path("value").asText();
return selectRandomIncorrectCapitals(getAllCapitals(results, capitalLabel), capitalLabel, 3);
}

@Override
protected String generateCorrectAnswer(JsonNode result) {
return result.path("capitalLabel").path("value").asText();
}

@Override
protected String getQuestionSubject(JsonNode result) {
return this.statement + result.path("countryLabel").path("value").asText() + "?";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.uniovi.components.generators.geography;

import com.fasterxml.jackson.databind.JsonNode;
import com.uniovi.services.CategoryService;

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

public class ContinentQuestionGeneration extends AbstractGeographyGenerator{
public ContinentQuestionGeneration(CategoryService categoryService) {
super(categoryService);
this.statement = "In which continent is ";
}

private List<String> getAllContinents(JsonNode resultsNode, String correctContinent) {
// Obtener todas las capitales del JSON (distintas a la capital correcta)
List<String> allContinents = new ArrayList<>();
for (JsonNode result : resultsNode) {
String continent = result.path("continentLabel").path("value").asText();
if (!continent.equals(correctContinent)) {
allContinents.add(continent);
}
}
return allContinents;
}

private List<String> selectRandomIncorrectContinents(List<String> allContinents, String correctContinent, int count) {
List<String> incorrectContinents = new ArrayList<>();
Random random = new Random();
while (incorrectContinents.size() < count && allContinents.size() > 0) {
int randomIndex = random.nextInt(allContinents.size());
String selectedCapital = allContinents.remove(randomIndex);
if (!selectedCapital.equals(correctContinent) && !incorrectContinents.contains(selectedCapital)) {
incorrectContinents.add(selectedCapital);
}
}
return incorrectContinents;
}

@Override
protected List<String> generateOptions(JsonNode results, JsonNode result) {
String continentLabel = result.path("continentLabel").path("value").asText();
return selectRandomIncorrectContinents(getAllContinents(results, continentLabel), continentLabel, 3);
}

@Override
protected String generateCorrectAnswer(JsonNode result) {
return result.path("continentLabel").path("value").asText();
}

@Override
protected String getQuestionSubject(JsonNode result) {
return this.statement + result.path("countryLabel").path("value").asText() + "?";
}

@Override
public String getQuery() {
return "SELECT DISTINCT ?country ?countryLabel ?continent ?continentLabel\n" +
"WHERE {" +
" ?country wdt:P31 wd:Q3624078 " +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q3024240}" +
" FILTER NOT EXISTS {?country wdt:P31 wd:Q28171280}" +
" OPTIONAL { ?country wdt:P30 ?continent } ." +
" SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE],en\" }" +
"}" +
"ORDER BY ?countryLabel";
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/uniovi/dto/AnswerDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.uniovi.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class AnswerDto {
private Long id;
private String text;
private boolean correct;

}
Loading

0 comments on commit 94488b6

Please sign in to comment.