[Added] Functionality for getting data from the database [NOTE: swapping seems to difficult for now, might have to go for a cache approach]
This commit is contained in:
@@ -9,6 +9,7 @@ import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
|
||||
@@ -26,13 +27,13 @@ public class ApiService {
|
||||
|
||||
private static final System.Logger logger = System.getLogger(ApiService.class.getName());
|
||||
|
||||
private final String apiUrl = "https://opentdb.com/api.php";
|
||||
private final String apiUrl = "https://opentdb.com";
|
||||
|
||||
public ApiService() {
|
||||
}
|
||||
|
||||
private String buildRequestString(QuizSettings quizSettings) {
|
||||
return apiUrl + quizSettings.toApiQuery();
|
||||
return apiUrl + "/api.php" + quizSettings.toApiQuery();
|
||||
}
|
||||
|
||||
public void handleRequest(@Valid Request request) {
|
||||
@@ -54,7 +55,9 @@ public class ApiService {
|
||||
* Store them in the DataService
|
||||
*/
|
||||
public void collectBulk() {
|
||||
String url = apiUrl + "?amount=50";
|
||||
// TODO: Make this happen only once, when a new database is generated
|
||||
getCategories();
|
||||
String url = apiUrl + "/api.php?amount=50";
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
@@ -64,5 +67,21 @@ public class ApiService {
|
||||
} catch (RestClientException | IOException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void getCategories() {
|
||||
String url = apiUrl + "/api_category.php";
|
||||
try {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
String json = response.getBody();
|
||||
CategoryResponse categoryResponse = cleanerService.extractCategoryResponse(json);
|
||||
responseHandler.handleCategoryResponse(categoryResponse);
|
||||
|
||||
} catch (RestClientException | IOException e) {
|
||||
logger.log(System.Logger.Level.ERROR, "Critical failure", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,87 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.QuestionDTO;
|
||||
import com.ninjaws.quiz.repositories.CategoryRepository;
|
||||
|
||||
@Service
|
||||
public class CleanerService {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
public CleanerService() {}
|
||||
public CleanerService() {
|
||||
}
|
||||
|
||||
public ApiResponse extractApiResponse(String json) throws IOException {
|
||||
return objectMapper.readValue(json, ApiResponse.class);
|
||||
}
|
||||
|
||||
public CategoryResponse extractCategoryResponse(String json) throws IOException {
|
||||
return objectMapper.readValue(json, CategoryResponse.class);
|
||||
}
|
||||
|
||||
public List<QuestionDTO> questionListEntitytoDTO(List<QuestionEntity> entities) {
|
||||
List<QuestionDTO> dtos = new ArrayList<>();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
QuestionEntity entity = entities.get(i);
|
||||
dtos.add(questionEntityToDTO(entity));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public QuestionDTO questionEntityToDTO(QuestionEntity entity) {
|
||||
QuestionDTO dto = new QuestionDTO();
|
||||
dto.setQuestion(entity.getQuestion());
|
||||
dto.setCorrectAnswer(entity.getCorrectAnswer());
|
||||
dto.setIncorrectAnswer(entity.getIncorrectAnswers());
|
||||
dto.setCategory(entity.getCategory().getName());
|
||||
dto.setType(entity.getType());
|
||||
dto.setDifficulty(entity.getDifficulty());
|
||||
return dto;
|
||||
}
|
||||
|
||||
public List<QuestionEntity> questionListDTOtoEntity(List<QuestionDTO> dtos) {
|
||||
List<QuestionEntity> entities = new ArrayList<>();
|
||||
for (int i = 0; i < dtos.size(); i++) {
|
||||
QuestionDTO dto = dtos.get(i);
|
||||
entities.add(questionDTOtoEntity(dto));
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public QuestionEntity questionDTOtoEntity(QuestionDTO dto) {
|
||||
QuestionEntity entity = new QuestionEntity();
|
||||
Category category = categoryRepository.findByName(decodeString(dto.getCategory())).orElseThrow(() -> new IllegalStateException("Category not in DB!"));
|
||||
entity.setCategory(category);
|
||||
entity.setDifficulty(dto.getDifficulty());
|
||||
entity.setType(dto.getType());
|
||||
entity.setQuestion(dto.getQuestion());
|
||||
entity.setCorrectAnswer(dto.getCorrectAnswer());
|
||||
entity.setIncorrectAnswers(dto.getIncorrectAnswers());
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* For some reason, the categories come with special characters,
|
||||
* But the categories from the questions dont
|
||||
* So this has to be manually fixed if they are to be stored in the db (otherwise namematching doesnt work)
|
||||
*/
|
||||
private String decodeString(String text){
|
||||
return StringEscapeUtils.unescapeHtml4(text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,23 +5,61 @@ import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.models.Question;
|
||||
import com.ninjaws.quiz.entities.Category;
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.repositories.CategoryRepository;
|
||||
import com.ninjaws.quiz.repositories.QuestionRepository;
|
||||
|
||||
@Service
|
||||
public class DataService {
|
||||
|
||||
@Autowired
|
||||
private QuestionRepository questionRepository;
|
||||
|
||||
public List<Question> getAllQuestions() {
|
||||
@Autowired
|
||||
private CategoryRepository categoryRepository;
|
||||
|
||||
public List<QuestionEntity> getAllQuestions() {
|
||||
return questionRepository.findAll();
|
||||
}
|
||||
|
||||
public Question saveQuestion(Question question) {
|
||||
public QuestionEntity saveQuestion(QuestionEntity question) {
|
||||
return questionRepository.save(question);
|
||||
}
|
||||
|
||||
public List<Question> saveQuestions(List<Question> questions) {
|
||||
public List<QuestionEntity> saveQuestions(List<QuestionEntity> questions) {
|
||||
return questionRepository.saveAll(questions);
|
||||
}
|
||||
|
||||
// public List<Question> getRandomQuestions(String type, String category, String difficulty, int limit) {
|
||||
// return questionRepository.findRandomQuestions(type, limit);
|
||||
// }
|
||||
|
||||
public List<QuestionEntity> lookupQuestions(QuizSettings quizSettings) {
|
||||
// Long category = Long.valueOf(quizSettings.getCategory().getApiValue() == 1 ? null : quizSettings.getCategory().getApiValue());
|
||||
// String difficulty = quizSettings.getDifficulty() == QuizSettings.Difficulty.ANY ? null : quizSettings.getDifficulty().getApiValue();
|
||||
// String type = quizSettings.getType() == QuizSettings.Type.ANY ? null : quizSettings.getType().getApiValue();
|
||||
// System.out.println(category);
|
||||
// System.out.println(difficulty);
|
||||
// System.out.println(type);
|
||||
|
||||
return questionRepository.lookupQuestions(quizSettings.getCategory(), quizSettings.getDifficulty(), quizSettings.getType(), quizSettings.getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get questions that match the specified QuizSettings
|
||||
* @return The questions if the query was possible, and an empty array if not
|
||||
*/
|
||||
// public List<Question> getByQuizSettings(QuizSettings quizSettings) {
|
||||
// Specification<Product> spec = Specification.where(QuestionSpecification.hasType(type))
|
||||
// .and(QuestionSpecification.hasCategory(category))
|
||||
// .and(QuestionSpecification.hasDifficulty(difficulty));
|
||||
|
||||
// return productRepository.findAll(spec);
|
||||
// }
|
||||
|
||||
public List<Category> saveCategories(List<Category> categories) {
|
||||
return categoryRepository.saveAll(categories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DatabaseScheduler {
|
||||
@Autowired
|
||||
private JdbcTemplate secondaryJdbcTemplate;
|
||||
|
||||
@Scheduled(fixedRate = 60000) // Runs every 60 seconds
|
||||
public void createAndFillSecondaryDatabase() {
|
||||
// Create tables and insert data into the secondary database
|
||||
secondaryJdbcTemplate.
|
||||
.execute("CREATE TABLE IF NOT EXISTS sample_table (id INT PRIMARY KEY, name VARCHAR(255))");
|
||||
secondaryJdbcTemplate.update("INSERT INTO sample_table (id, name) VALUES (?, ?)", 1, "Sample Data");
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.ninjaws.quiz.services;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.entities.QuestionEntity;
|
||||
import com.ninjaws.quiz.models.Answers;
|
||||
import com.ninjaws.quiz.models.QuizSettings;
|
||||
import com.ninjaws.quiz.models.Request;
|
||||
@@ -18,6 +20,10 @@ public class QuizService {
|
||||
private CacheService cacheService;
|
||||
@Autowired
|
||||
private QueueService queueService;
|
||||
@Autowired
|
||||
private DataService dataService;
|
||||
@Autowired
|
||||
private CleanerService cleanerService;
|
||||
|
||||
/**
|
||||
* Actions:
|
||||
@@ -29,7 +35,22 @@ public class QuizService {
|
||||
*/
|
||||
public Optional<String> createSession(QuizSettings quizSettings) {
|
||||
Session session = new Session();
|
||||
|
||||
/** First, we check if there are already enough items in our db */
|
||||
List<QuestionEntity> questionsRetrieved = dataService.lookupQuestions(quizSettings);
|
||||
if(!questionsRetrieved.isEmpty()) {
|
||||
session.setQuestions(cleanerService.questionListEntitytoDTO(questionsRetrieved));
|
||||
}
|
||||
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
|
||||
/**
|
||||
* If the questions were retrieved from the DB, then it doesn't need to be added to the queue
|
||||
*/
|
||||
if(!questionsRetrieved.isEmpty()) {
|
||||
return Optional.of(session.getId());
|
||||
}
|
||||
|
||||
boolean addedToQueue = queueService.addToQueue(new Request(session.getId(), quizSettings));
|
||||
/** In this order, because doing the queue before the cache could lead to race conditions (request completion immediately calls cache, but then the item doesn't exist) */
|
||||
if(!addedToQueue) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ninjaws.quiz.models.ApiResponse;
|
||||
import com.ninjaws.quiz.models.CategoryResponse;
|
||||
import com.ninjaws.quiz.models.Session;
|
||||
|
||||
@Service
|
||||
@@ -13,6 +14,8 @@ public class ResponseHandlingService {
|
||||
private CacheService cacheService;
|
||||
@Autowired
|
||||
private DataService dataService;
|
||||
@Autowired
|
||||
private CleanerService cleanerService;
|
||||
|
||||
/**
|
||||
* If there is no sessionId, then this is a request from the scheduler
|
||||
@@ -30,6 +33,7 @@ public class ResponseHandlingService {
|
||||
case 0:
|
||||
// Success
|
||||
updateSessionInCache(session, response);
|
||||
dataService.saveQuestions(cleanerService.questionListDTOtoEntity(response.getResults()));
|
||||
break;
|
||||
case 1:
|
||||
// Not enough questions to satisfy the request.
|
||||
@@ -63,8 +67,7 @@ public class ResponseHandlingService {
|
||||
public void handleResponse(ApiResponse response) {
|
||||
switch (response.getResponseCode()) {
|
||||
case 0:
|
||||
// TODO: Store in DB
|
||||
dataService.saveQuestions(response.getResults());
|
||||
dataService.saveQuestions(cleanerService.questionListDTOtoEntity(response.getResults()));
|
||||
break;
|
||||
case 4:
|
||||
// Signal DataService that we have extracted all the data. Stop gathering, switch to caching-mode
|
||||
@@ -83,4 +86,7 @@ public class ResponseHandlingService {
|
||||
cacheService.addToCache(session.getId(), session);
|
||||
}
|
||||
|
||||
public void handleCategoryResponse(CategoryResponse categoryResponse) {
|
||||
dataService.saveCategories(categoryResponse.getTrivia_categories());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user