-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add db initialization with sample data (#20)
* add db initialization with sample data * fix according to comments * fix ExampleDataInitializer description
- Loading branch information
1 parent
3f30196
commit f932251
Showing
7 changed files
with
196 additions
and
60 deletions.
There are no files selected for viewing
133 changes: 80 additions & 53 deletions
133
server/src/main/java/org/hyperskill/community/flashcards/card/ExampleDataInitializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,110 @@ | ||
package org.hyperskill.community.flashcards.card; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.annotation.PostConstruct; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.hyperskill.community.flashcards.card.model.Card; | ||
import org.hyperskill.community.flashcards.card.model.MultipleChoiceQuiz; | ||
import org.hyperskill.community.flashcards.card.model.QuestionAndAnswerCard; | ||
import org.hyperskill.community.flashcards.card.model.SingleChoiceQuiz; | ||
import org.hyperskill.community.flashcards.category.model.Category; | ||
import org.hyperskill.community.flashcards.category.model.CategoryAccess; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.hyperskill.community.flashcards.registration.User; | ||
import org.hyperskill.community.flashcards.registration.UserDto; | ||
import org.hyperskill.community.flashcards.registration.UserMapper; | ||
import org.springframework.core.io.ClassPathResource; | ||
import org.springframework.core.io.Resource; | ||
import org.springframework.data.mongodb.core.MongoTemplate; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
/** | ||
* Initializes the 'example' database with sample cards. | ||
* Initializes the 'cards' database with sample cards. | ||
*/ | ||
@Component | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class ExampleDataInitializer { | ||
private static final String exampleCollection = "example"; | ||
private static final String userCollection = "user"; | ||
private static final String categoryCollection = "category"; | ||
private static final String userJsonPath = "/json/users.json"; | ||
private static final String flashcardsJsonPath = "/json/flashcards.json"; | ||
private final ObjectMapper objectMapper = new ObjectMapper(); | ||
private final UserMapper userMapper; | ||
private final MongoTemplate mongoTemplate; | ||
|
||
public ExampleDataInitializer(@Qualifier("example") MongoTemplate mongoTemplate) { | ||
this.mongoTemplate = mongoTemplate; | ||
} | ||
|
||
/** | ||
* This method checks if collections to be initialized are empty, and if yes | ||
* it reads data from <code>/resources/json</code> folder and insert it | ||
* in the said collections. This ensures that empty database is initialized | ||
* with the same data, while non-empty database state remains unchanged. | ||
*/ | ||
@PostConstruct | ||
public void init() { | ||
List<Card> mathCards = List.of( | ||
QuestionAndAnswerCard.builder() | ||
.title("card 1") | ||
.question("Calculate 2 + 2 = ?") | ||
.answer("4") | ||
.tags(Set.of("equations")) | ||
.build(), | ||
SingleChoiceQuiz.builder() | ||
.title("card 2") | ||
.question("Solve the equation: 2x + 3 = 9") | ||
.options(List.of("2", "5", "6", "3")) | ||
.correctOption(3) | ||
.build(), | ||
MultipleChoiceQuiz.builder() | ||
.title("card 3") | ||
.question("Select all correct statements about the equation: 2x + 3 = 9") | ||
.options(List.of("It's a square equation", "It's a linear equation", "It doesn't have a root", "It's root is 3")) | ||
.correctOptions(List.of(1, 3)) | ||
.build() | ||
); | ||
var geographyCards = List.of( | ||
QuestionAndAnswerCard.builder() | ||
.title("card 4") | ||
.question("Capital of Spain") | ||
.answer("Madrid") | ||
.tags(Set.of("Europe", "cities")) | ||
.build() | ||
); | ||
var chemistryCards = List.of( | ||
SingleChoiceQuiz.builder() | ||
.title("card 5") | ||
.question("Which of the elements has the lowest atomic weight?") | ||
.options(List.of("Au", "Fe", "Li", "Pt")) | ||
.correctOption(2) | ||
.tags(Set.of("elements", "atoms")) | ||
.build() | ||
); | ||
if (isCollectionNotEmpty(userCollection)) { | ||
log.warn("Collection {} is not empty, aborting database initialization", userCollection); | ||
return; | ||
} | ||
if (isCollectionNotEmpty(categoryCollection)) { | ||
log.warn("Collection {} is not empty, aborting database initialization", categoryCollection); | ||
return; | ||
} | ||
if (isCollectionNotEmpty(exampleCollection)) { | ||
log.warn("Collection {} is not empty, aborting database initialization", exampleCollection); | ||
return; | ||
} | ||
|
||
Resource usersJson = new ClassPathResource(userJsonPath); | ||
Resource flashcardsJson = new ClassPathResource(flashcardsJsonPath); | ||
|
||
try { | ||
List<UserDto> userDTOs = objectMapper.readValue(usersJson.getFile(), new TypeReference<>() {}); | ||
List<User> users = userDTOs.stream().map(userMapper::toDocument).toList(); | ||
if (users.size() == 0) { | ||
throw new IllegalStateException("No users to insert"); | ||
} | ||
mongoTemplate.insertAll(users); | ||
|
||
var categoryAccess = new CategoryAccess(users.get(0).getUsername(), "rwd"); | ||
mongoTemplate.insert(new Category(null, exampleCollection, Set.of(categoryAccess))); | ||
|
||
log.info("Dropping any existing collections in 'example' database..."); | ||
mongoTemplate.getDb().listCollectionNames().forEach(mongoTemplate::dropCollection); | ||
JsonNode jsonNode = objectMapper.readTree(flashcardsJson.getFile()); | ||
List<SingleChoiceQuiz> scqCards = parseCards(jsonNode.get("scq_cards"), SingleChoiceQuiz.class); | ||
List<MultipleChoiceQuiz> mcqCards = parseCards(jsonNode.get("mcq_cards"), MultipleChoiceQuiz.class); | ||
List<QuestionAndAnswerCard> qnaCards = parseCards(jsonNode.get("qna_cards"), QuestionAndAnswerCard.class); | ||
|
||
mongoTemplate.insert(scqCards, exampleCollection); | ||
mongoTemplate.insert(mcqCards, exampleCollection); | ||
mongoTemplate.insert(qnaCards, exampleCollection); | ||
|
||
} catch (Exception e) { | ||
log.error("Error updating database", e); | ||
mongoTemplate.getDb().drop(); | ||
} | ||
} | ||
|
||
private <T extends Card> List<T> parseCards(JsonNode jsonNode, Class<T> clazz) throws JsonProcessingException { | ||
List<T> list = new ArrayList<>(); | ||
for (JsonNode node : jsonNode) { | ||
T card = objectMapper.treeToValue(node, clazz); | ||
list.add(card); | ||
} | ||
return list; | ||
} | ||
|
||
log.info("Inserting sample data to 'example' database..."); | ||
var categoryAccess = new CategoryAccess("[email protected]", "rwd"); | ||
mongoTemplate.insert(new Category(null, "math", Set.of(categoryAccess))); | ||
mongoTemplate.insert(new Category(null, "geography", Set.of(categoryAccess))); | ||
mongoTemplate.insert(new Category(null, "chemistry", Set.of(categoryAccess))); | ||
private boolean isCollectionNotEmpty(String collection) { | ||
var collectionExists = mongoTemplate.getDb().listCollectionNames() | ||
.into(new ArrayList<>()) | ||
.contains(collection); | ||
|
||
mathCards.forEach(card -> mongoTemplate.insert(card, "math")); | ||
geographyCards.forEach(card -> mongoTemplate.insert(card, "geography")); | ||
chemistryCards.forEach(card -> mongoTemplate.insert(card, "chemistry")); | ||
return collectionExists && mongoTemplate.getDb().getCollection(collection).countDocuments() > 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
{ | ||
"qna_cards": [ | ||
{ | ||
"title": "card 1", | ||
"question": "Capital of Brazil", | ||
"answer": "Brasilia", | ||
"tags": [ | ||
"america", "cities" | ||
] | ||
}, | ||
{ | ||
"title": "card 2", | ||
"question": "Capital of Spain", | ||
"answer": "Madrid", | ||
"tags": [ | ||
"europe", "cities" | ||
] | ||
}, | ||
{ | ||
"title": "card 3", | ||
"question": "Name this chemical element: Tl", | ||
"answer": "Thallium", | ||
"tags": [ | ||
"elements", "symbols" | ||
] | ||
}, | ||
{ | ||
"title": "card 4", | ||
"question": "What is the square root of 144", | ||
"answer": "12", | ||
"tags": [] | ||
} | ||
], | ||
"scq_cards": [ | ||
{ | ||
"title": "card 5", | ||
"question": "What is the root of this equation: 3x + 2 = 14", | ||
"options": ["5", "9", "3", "4"], | ||
"correctOption": 3, | ||
"tags": ["equations"] | ||
}, | ||
{ | ||
"title": "card 6", | ||
"question": "Name the largest country among the following:", | ||
"options": ["Portugal", "Sweden", "Australia", "Japan"], | ||
"correctOption": 2, | ||
"tags": ["countries"] | ||
}, | ||
{ | ||
"title": "card 7", | ||
"question": "What is the type of this reaction: NaOH + HCl = NaCl + H2O", | ||
"options": ["Oxidation", "Reduction", "Neutralization", "Neither"], | ||
"correctOption": 3, | ||
"tags": ["reactions"] | ||
}, | ||
{ | ||
"title": "card 8", | ||
"question": "Name the capital of Luxembourg", | ||
"options": ["Luxembourg", "Strasbourg", "Andorra la Vella", "London"], | ||
"correctOption": 3, | ||
"tags": ["europe", "cities"] | ||
} | ||
], | ||
"mcq_cards": [ | ||
{ | ||
"title": "card 9", | ||
"question": "Select all correct statements about this equation: ax + by = c", | ||
"options": ["It's a quadratic equation", "It's a linear equation", "It describes a line", "It describes a curve"], | ||
"correctOptions": [1, 2], | ||
"tags": ["geometry", "equations"] | ||
}, | ||
{ | ||
"title": "card 10", | ||
"question": "Which of the following is true about DNA?", | ||
"options": ["DNA is a double-stranded molecule", "DNA contains genetic information", "DNA stands for Deoxyribonucleic acid", "DNA is found only in animals"], | ||
"correctOptions": [0, 1, 2], | ||
"tags": ["genetics"] | ||
}, | ||
{ | ||
"title": "card 11", | ||
"question": "Which of the following elements is a noble gas?", | ||
"options": ["Oxygen", "Helium", "Carbon", "Nitrogen"], | ||
"correctOptions": [1], | ||
"tags": ["elements"] | ||
}, | ||
{ | ||
"title": "card 12", | ||
"question": "Which of the following is a fundamental force in nature?", | ||
"options": ["Gravity", "Friction", "Light", "Heat"], | ||
"correctOptions": [0], | ||
"tags": ["forces"] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[ | ||
{ | ||
"email": "[email protected]", | ||
"password": "12345678" | ||
}, | ||
{ | ||
"email": "[email protected]", | ||
"password": "12345678" | ||
} | ||
] |