use of ai.elimu.model.content.StoryBookParagraph in project webapp by elimu-ai.
the class DbContentImportHelper method performDatabaseContentImport.
/**
* Extracts educational content from the CSV files in {@code src/main/resources/db/content_TEST/<Language>/} and
* stores it in the database.
*
* @param environment The environment from which to import the database content.
* @param language The language to use during the import.
* @param webApplicationContext Context needed to access DAOs.
*/
public synchronized void performDatabaseContentImport(Environment environment, Language language, WebApplicationContext webApplicationContext) {
logger.info("performDatabaseContentImport");
logger.info("environment: " + environment + ", language: " + language);
if (!((environment == Environment.TEST) || (environment == Environment.PROD))) {
throw new IllegalArgumentException("Database content can only be imported from the TEST environment or from the PROD environment");
}
String contentDirectoryPath = "db" + File.separator + "content_" + environment + File.separator + language.toString().toLowerCase();
logger.info("contentDirectoryPath: \"" + contentDirectoryPath + "\"");
URL contentDirectoryURL = getClass().getClassLoader().getResource(contentDirectoryPath);
logger.info("contentDirectoryURL: " + contentDirectoryURL);
if (contentDirectoryURL == null) {
logger.warn("The content directory was not found. Aborting content import.");
return;
}
File contentDirectory = new File(contentDirectoryURL.getPath());
logger.info("contentDirectory: " + contentDirectory);
contributorDao = (ContributorDao) webApplicationContext.getBean("contributorDao");
Contributor contributor = new Contributor();
contributor.setEmail("dev@elimu.ai");
contributor.setFirstName("Dev");
contributor.setLastName("Contributor");
contributor.setRoles(new HashSet<>(Arrays.asList(Role.CONTRIBUTOR, Role.EDITOR, Role.ANALYST, Role.ADMIN)));
contributor.setRegistrationTime(Calendar.getInstance());
contributorDao.create(contributor);
// Extract and import Letters from CSV file in src/main/resources/
File lettersCsvFile = new File(contentDirectory, "letters.csv");
List<Letter> letters = CsvContentExtractionHelper.getLettersFromCsvBackup(lettersCsvFile, soundDao);
logger.info("letters.size(): " + letters.size());
letterDao = (LetterDao) webApplicationContext.getBean("letterDao");
letterContributionEventDao = (LetterContributionEventDao) webApplicationContext.getBean("letterContributionEventDao");
for (Letter letter : letters) {
letterDao.create(letter);
LetterContributionEvent letterContributionEvent = new LetterContributionEvent();
letterContributionEvent.setContributor(contributor);
letterContributionEvent.setLetter(letter);
letterContributionEvent.setRevisionNumber(1);
letterContributionEvent.setTime(Calendar.getInstance());
letterContributionEvent.setTimeSpentMs((long) (Math.random() * 10) * 60000L);
letterContributionEvent.setPlatform(Platform.WEBAPP);
letterContributionEventDao.create(letterContributionEvent);
}
// Extract and import Sounds from CSV file in src/main/resources/
File soundsCsvFile = new File(contentDirectory, "sounds.csv");
List<Sound> sounds = CsvContentExtractionHelper.getSoundsFromCsvBackup(soundsCsvFile);
logger.info("sounds.size(): " + sounds.size());
soundDao = (SoundDao) webApplicationContext.getBean("soundDao");
for (Sound sound : sounds) {
soundDao.create(sound);
}
// Extract and import letter-sound correspondences in src/main/resources/
File letterToAllophioneMappingsCsvFile = new File(contentDirectory, "letter-sound-correspondences.csv");
List<LetterSoundCorrespondence> letterSoundCorrespondences = CsvContentExtractionHelper.getLetterSoundCorrespondencesFromCsvBackup(letterToAllophioneMappingsCsvFile, letterDao, soundDao, letterSoundCorrespondenceDao);
logger.info("letterSoundCorrespondences.size(): " + letterSoundCorrespondences.size());
letterSoundCorrespondenceDao = (LetterSoundCorrespondenceDao) webApplicationContext.getBean("letterSoundCorrespondenceDao");
letterSoundCorrespondenceContributionEventDao = (LetterSoundCorrespondenceContributionEventDao) webApplicationContext.getBean("letterSoundCorrespondenceContributionEventDao");
for (LetterSoundCorrespondence letterSoundCorrespondence : letterSoundCorrespondences) {
letterSoundCorrespondenceDao.create(letterSoundCorrespondence);
LetterSoundCorrespondenceContributionEvent letterSoundCorrespondenceContributionEvent = new LetterSoundCorrespondenceContributionEvent();
letterSoundCorrespondenceContributionEvent.setContributor(contributor);
letterSoundCorrespondenceContributionEvent.setLetterSoundCorrespondence(letterSoundCorrespondence);
letterSoundCorrespondenceContributionEvent.setRevisionNumber(1);
letterSoundCorrespondenceContributionEvent.setTime(Calendar.getInstance());
letterSoundCorrespondenceContributionEvent.setTimeSpentMs((long) (Math.random() * 10) * 60000L);
letterSoundCorrespondenceContributionEvent.setPlatform(Platform.WEBAPP);
letterSoundCorrespondenceContributionEventDao.create(letterSoundCorrespondenceContributionEvent);
}
// Extract and import Words from CSV file in src/main/resources/
File wordsCsvFile = new File(contentDirectory, "words.csv");
List<Word> words = CsvContentExtractionHelper.getWordsFromCsvBackup(wordsCsvFile, letterDao, soundDao, letterSoundCorrespondenceDao, wordDao);
logger.info("words.size(): " + words.size());
wordDao = (WordDao) webApplicationContext.getBean("wordDao");
wordContributionEventDao = (WordContributionEventDao) webApplicationContext.getBean("wordContributionEventDao");
for (Word word : words) {
wordDao.create(word);
WordContributionEvent wordContributionEvent = new WordContributionEvent();
wordContributionEvent.setContributor(contributor);
wordContributionEvent.setWord(word);
wordContributionEvent.setRevisionNumber(1);
wordContributionEvent.setTime(Calendar.getInstance());
wordContributionEvent.setTimeSpentMs((long) (Math.random() * 10) * 60000L);
wordContributionEvent.setPlatform(Platform.WEBAPP);
wordContributionEventDao.create(wordContributionEvent);
}
// Extract and import Numbers from CSV file in src/main/resources/
File numbersCsvFile = new File(contentDirectory, "numbers.csv");
List<Number> numbers = CsvContentExtractionHelper.getNumbersFromCsvBackup(numbersCsvFile, wordDao);
logger.info("numbers.size(): " + numbers.size());
numberDao = (NumberDao) webApplicationContext.getBean("numberDao");
numberContributionEventDao = (NumberContributionEventDao) webApplicationContext.getBean("numberContributionEventDao");
for (Number number : numbers) {
numberDao.create(number);
NumberContributionEvent numberContributionEvent = new NumberContributionEvent();
numberContributionEvent.setContributor(contributor);
numberContributionEvent.setNumber(number);
numberContributionEvent.setRevisionNumber(1);
numberContributionEvent.setTime(Calendar.getInstance());
numberContributionEvent.setTimeSpentMs((long) (Math.random() * 10) * 60000L);
numberContributionEvent.setPlatform(Platform.WEBAPP);
numberContributionEventDao.create(numberContributionEvent);
}
// Extract and import Syllables from CSV file in src/main/resources/
// TODO
// Extract and import Emojis from CSV file in src/main/resources/
File emojisCsvFile = new File(contentDirectory, "emojis.csv");
List<Emoji> emojis = CsvContentExtractionHelper.getEmojisFromCsvBackup(emojisCsvFile, wordDao);
logger.info("emojis.size(): " + emojis.size());
emojiDao = (EmojiDao) webApplicationContext.getBean("emojiDao");
for (Emoji emoji : emojis) {
emojiDao.create(emoji);
}
// Extract and import Images from CSV file in src/main/resources/
// TODO
// Extract and import Audios from CSV file in src/main/resources/
// TODO
// Extract and import StoryBooks from CSV file in src/main/resources/
File storyBooksCsvFile = new File(contentDirectory, "storybooks.csv");
List<StoryBookGson> storyBookGsons = CsvContentExtractionHelper.getStoryBooksFromCsvBackup(storyBooksCsvFile);
logger.info("storyBookGsons.size(): " + storyBookGsons.size());
storyBookDao = (StoryBookDao) webApplicationContext.getBean("storyBookDao");
storyBookChapterDao = (StoryBookChapterDao) webApplicationContext.getBean("storyBookChapterDao");
storyBookParagraphDao = (StoryBookParagraphDao) webApplicationContext.getBean("storyBookParagraphDao");
storyBookContributionEventDao = (StoryBookContributionEventDao) webApplicationContext.getBean("storyBookContributionEventDao");
for (StoryBookGson storyBookGson : storyBookGsons) {
// Convert from GSON to JPA
StoryBook storyBook = new StoryBook();
storyBook.setTitle(storyBookGson.getTitle());
storyBook.setDescription(storyBookGson.getDescription());
// TODO: storyBook.setContentLicense();
// TODO: storyBook.setAttributionUrl();
storyBook.setReadingLevel(storyBookGson.getReadingLevel());
storyBookDao.create(storyBook);
for (StoryBookChapterGson storyBookChapterGson : storyBookGson.getStoryBookChapters()) {
// Convert from GSON to JPA
StoryBookChapter storyBookChapter = new StoryBookChapter();
storyBookChapter.setStoryBook(storyBook);
storyBookChapter.setSortOrder(storyBookChapterGson.getSortOrder());
// TODO: storyBookChapter.setImage();
storyBookChapterDao.create(storyBookChapter);
for (StoryBookParagraphGson storyBookParagraphGson : storyBookChapterGson.getStoryBookParagraphs()) {
// Convert from GSON to JPA
StoryBookParagraph storyBookParagraph = new StoryBookParagraph();
storyBookParagraph.setStoryBookChapter(storyBookChapter);
storyBookParagraph.setSortOrder(storyBookParagraphGson.getSortOrder());
storyBookParagraph.setOriginalText(storyBookParagraphGson.getOriginalText());
List<String> wordsInOriginalText = WordExtractionHelper.getWords(storyBookParagraph.getOriginalText(), language);
logger.info("wordsInOriginalText.size(): " + wordsInOriginalText.size());
List<Word> paragraphWords = new ArrayList<>();
logger.info("paragraphWords.size(): " + paragraphWords.size());
for (String wordInOriginalText : wordsInOriginalText) {
logger.info("wordInOriginalText: \"" + wordInOriginalText + "\"");
wordInOriginalText = wordInOriginalText.toLowerCase();
logger.info("wordInOriginalText (lower-case): \"" + wordInOriginalText + "\"");
Word word = wordDao.readByText(wordInOriginalText);
logger.info("word: " + word);
paragraphWords.add(word);
}
storyBookParagraph.setWords(paragraphWords);
storyBookParagraphDao.create(storyBookParagraph);
}
}
StoryBookContributionEvent storyBookContributionEvent = new StoryBookContributionEvent();
storyBookContributionEvent.setContributor(contributor);
storyBookContributionEvent.setStoryBook(storyBook);
storyBookContributionEvent.setRevisionNumber(1);
storyBookContributionEvent.setTime(Calendar.getInstance());
storyBookContributionEvent.setTimeSpentMs((long) (Math.random() * 10) * 60000L);
storyBookContributionEvent.setPlatform(Platform.WEBAPP);
storyBookContributionEventDao.create(storyBookContributionEvent);
}
// Extract and import Videos from CSV file in src/main/resources/
// TODO
String analyticsDirectoryPath = "db" + File.separator + "analytics_" + environment + File.separator + language.toString().toLowerCase();
logger.info("analyticsDirectoryPath: \"" + analyticsDirectoryPath + "\"");
URL analyticsDirectoryURL = getClass().getClassLoader().getResource(analyticsDirectoryPath);
logger.info("analyticsDirectoryURL: " + analyticsDirectoryURL);
if (analyticsDirectoryURL == null) {
logger.warn("The analytics directory was not found. Aborting analytics import.");
return;
}
File analyticsDirectory = new File(analyticsDirectoryURL.getPath());
logger.info("analyticsDirectory: " + analyticsDirectory);
// Extract and import LetterLearningEvents from CSV file in src/main/resources/
// TODO
// Extract and import WordLearningEvents from CSV file in src/main/resources/
// TODO
// Extract and import StoryBookLearningEvents from CSV file in src/main/resources/
File storyBookLearningEventsCsvFile = new File(analyticsDirectory, "storybook-learning-events.csv");
applicationDao = (ApplicationDao) webApplicationContext.getBean("applicationDao");
List<StoryBookLearningEvent> storyBookLearningEvents = CsvAnalyticsExtractionHelper.getStoryBookLearningEventsFromCsvBackup(storyBookLearningEventsCsvFile, applicationDao, storyBookDao);
logger.info("storyBookLearningEvents.size(): " + storyBookLearningEvents.size());
storyBookLearningEventDao = (StoryBookLearningEventDao) webApplicationContext.getBean("storyBookLearningEventDao");
for (StoryBookLearningEvent storyBookLearningEvent : storyBookLearningEvents) {
storyBookLearningEventDao.create(storyBookLearningEvent);
}
logger.info("Content import complete");
}
use of ai.elimu.model.content.StoryBookParagraph in project webapp by elimu-ai.
the class SyllableUsageCountScheduler method execute.
// At 07:30 every morning
@Scheduled(cron = "00 30 07 * * *")
public synchronized void execute() {
logger.info("execute");
logger.info("Calculating usage count for Syllables");
Map<String, Integer> syllableFrequencyMap = new HashMap<>();
Language language = Language.valueOf(ConfigHelper.getProperty("content.language"));
List<StoryBook> storyBooks = storyBookDao.readAllOrdered();
logger.info("storyBooks.size(): " + storyBooks.size());
for (StoryBook storyBook : storyBooks) {
logger.info("storyBook.getTitle(): " + storyBook.getTitle());
List<String> paragraphs = new ArrayList<>();
List<StoryBookChapter> storyBookChapters = storyBookChapterDao.readAll(storyBook);
for (StoryBookChapter storyBookChapter : storyBookChapters) {
List<StoryBookParagraph> storyBookParagraphs = storyBookParagraphDao.readAll(storyBookChapter);
for (StoryBookParagraph storyBookParagraph : storyBookParagraphs) {
paragraphs.add(storyBookParagraph.getOriginalText());
}
}
Map<String, Integer> syllableFrequencyMapForBook = SyllableFrequencyHelper.getSyllableFrequency(paragraphs, language);
syllableFrequencyMapForBook.keySet().forEach(syllableText -> syllableFrequencyMap.put(syllableText, syllableFrequencyMap.getOrDefault(syllableText, 0) + syllableFrequencyMapForBook.get(syllableText)));
}
logger.info("syllableFrequencyMap: " + syllableFrequencyMap);
for (String syllableText : syllableFrequencyMap.keySet()) {
// Skip syllables that are actual words
// TODO: add logic to Word editing
Word word = wordDao.readByText(syllableText);
if (word != null) {
continue;
}
// TODO: add support for trigrams
if (syllableText.length() != 2) {
continue;
}
Syllable existingSyllable = syllableDao.readByText(syllableText);
if (existingSyllable == null) {
Syllable syllable = new Syllable();
syllable.setTimeLastUpdate(Calendar.getInstance());
syllable.setText(syllableText);
syllable.setUsageCount(syllableFrequencyMap.get(syllableText));
syllableDao.create(syllable);
} else {
existingSyllable.setUsageCount(syllableFrequencyMap.get(syllableText));
syllableDao.update(existingSyllable);
}
}
logger.info("execute complete");
}
use of ai.elimu.model.content.StoryBookParagraph in project webapp by elimu-ai.
the class StoryBookCreateFromEPubController method handleSubmit.
@RequestMapping(method = RequestMethod.POST)
public String handleSubmit(StoryBook storyBook, @RequestParam("bytes") MultipartFile multipartFile, BindingResult result, Model model, HttpServletRequest request, HttpSession session) throws IOException {
logger.info("handleSubmit");
Image storyBookCoverImage = null;
List<StoryBookChapter> storyBookChapters = new ArrayList<>();
List<StoryBookParagraph> storyBookParagraphs = new ArrayList<>();
if (multipartFile.isEmpty()) {
result.rejectValue("bytes", "NotNull");
} else {
String contentType = multipartFile.getContentType();
logger.info("contentType: " + contentType);
String name = multipartFile.getName();
logger.info("name: " + name);
String originalFilename = multipartFile.getOriginalFilename();
logger.info("originalFilename: " + originalFilename);
long size = multipartFile.getSize();
logger.info("size: " + size + " (" + (size / 1024) + "kB)");
byte[] ePubBytes = multipartFile.getBytes();
logger.info("ePubBytes.length: " + (ePubBytes.length / 1024 / 1024) + "MB");
List<File> filesInEPub = unzipFiles(ePubBytes, originalFilename);
logger.info("filesInEPub.size(): " + filesInEPub.size());
// Extract the ePUB's metadata from its OPF file
File opfFile = null;
for (File file : filesInEPub) {
if (file.getName().endsWith(".opf")) {
opfFile = file;
}
}
logger.info("opfFile: \"" + opfFile + "\"");
if (opfFile == null) {
throw new IllegalArgumentException("The OPF file was not found");
} else {
String title = EPubMetadataExtractionHelper.extractTitleFromOpfFile(opfFile);
logger.info("title: \"" + title + "\"");
storyBook.setTitle(title);
String description = EPubMetadataExtractionHelper.extractDescriptionFromOpfFile(opfFile);
logger.info("description: \"" + description + "\"");
if (StringUtils.isNotBlank(description)) {
logger.info("description.length(): " + description.length());
if (description.length() > 1024) {
description = description.substring(0, 1023);
}
storyBook.setDescription(description);
}
storyBookCoverImage = new Image();
String coverImageReference = EPubMetadataExtractionHelper.extractCoverImageReferenceFromOpfFile(opfFile);
logger.info("coverImageReference: " + coverImageReference);
File coverImageFile = new File(opfFile.getParent(), coverImageReference);
logger.info("coverImageFile: " + coverImageFile);
logger.info("coverImageFile.exists(): " + coverImageFile.exists());
URI coverImageUri = coverImageFile.toURI();
logger.info("coverImageUri: " + coverImageUri);
byte[] coverImageBytes = IOUtils.toByteArray(coverImageUri);
storyBookCoverImage.setBytes(coverImageBytes);
byte[] headerBytes = Arrays.copyOfRange(coverImageBytes, 0, 6);
// "GIF87a"
byte[] gifHeader87a = { 71, 73, 70, 56, 55, 97 };
// "GIF89a"
byte[] gifHeader89a = { 71, 73, 70, 56, 57, 97 };
if (Arrays.equals(gifHeader87a, headerBytes) || Arrays.equals(gifHeader89a, headerBytes)) {
storyBookCoverImage.setContentType("image/gif");
storyBookCoverImage.setImageFormat(ImageFormat.GIF);
} else if (coverImageFile.getName().toLowerCase().endsWith(".png")) {
storyBookCoverImage.setContentType("image/png");
storyBookCoverImage.setImageFormat(ImageFormat.PNG);
} else if (coverImageFile.getName().toLowerCase().endsWith(".jpg") || coverImageFile.getName().toLowerCase().endsWith(".jpeg")) {
storyBookCoverImage.setContentType("image/jpg");
storyBookCoverImage.setImageFormat(ImageFormat.JPG);
} else if (coverImageFile.getName().toLowerCase().endsWith(".gif")) {
storyBookCoverImage.setContentType("image/gif");
storyBookCoverImage.setImageFormat(ImageFormat.GIF);
}
try {
int[] dominantColor = ImageColorHelper.getDominantColor(storyBookCoverImage.getBytes());
storyBookCoverImage.setDominantColor("rgb(" + dominantColor[0] + "," + dominantColor[1] + "," + dominantColor[2] + ")");
} catch (NullPointerException ex) {
// javax.imageio.IIOException: Unsupported Image Type
}
if (storyBookCoverImage.getImageFormat() != ImageFormat.GIF) {
// Reduce size if large image
int imageWidth = ImageHelper.getWidth(coverImageBytes);
logger.info("imageWidth: " + imageWidth + "px");
if (imageWidth > ImageHelper.MINIMUM_WIDTH) {
coverImageBytes = ImageHelper.scaleImage(coverImageBytes, ImageHelper.MINIMUM_WIDTH);
storyBookCoverImage.setBytes(coverImageBytes);
}
}
}
// Extract the ePUB's chapters
File tableOfContentsFile = null;
for (File file : filesInEPub) {
if (file.getName().startsWith("toc.")) {
tableOfContentsFile = file;
}
}
logger.info("tableOfContentsFile: \"" + tableOfContentsFile + "\"");
if (tableOfContentsFile == null) {
throw new IllegalArgumentException("The TOC file was not found");
} else {
List<String> chapterReferences = null;
if (tableOfContentsFile.getName().endsWith(".xhtml")) {
// StoryBookProvider#GLOBAL_DIGITAL_LIBRARY or StoryBookProvider#LETS_READ_ASIA
chapterReferences = EPubChapterExtractionHelper.extractChapterReferencesFromTableOfContentsFile(tableOfContentsFile);
} else if (tableOfContentsFile.getName().endsWith(".ncx")) {
// StoryBookProvider#STORYWEAVER
chapterReferences = EPubChapterExtractionHelper.extractChapterReferencesFromTableOfContentsFileNcx(tableOfContentsFile);
}
logger.info("chapterReferences.size(): " + chapterReferences.size());
// Extract each chapter's image (if any) and paragraphs
for (String chapterReference : chapterReferences) {
logger.info("chapterReference: \"" + chapterReference + "\"");
File chapterFile = new File(opfFile.getParent(), chapterReference);
logger.info("chapterFile: \"" + chapterFile + "\"");
StoryBookChapter storyBookChapter = new StoryBookChapter();
storyBookChapter.setSortOrder(storyBookChapters.size());
storyBookChapters.add(storyBookChapter);
String chapterImageReference = EPubImageExtractionHelper.extractImageReferenceFromChapterFile(chapterFile);
logger.info("chapterImageReference: " + chapterImageReference);
if (StringUtils.isNotBlank(chapterImageReference)) {
File chapterImageFile = null;
if (chapterImageReference.startsWith("http://") || chapterImageReference.startsWith("https://")) {
// Download the file
URL sourceUrl = new URL(chapterImageReference);
String tmpDir = System.getProperty("java.io.tmpdir");
logger.info("tmpDir: " + tmpDir);
File tmpDirElimuAi = new File(tmpDir, "elimu-ai");
logger.info("tmpDirElimuAi: " + tmpDirElimuAi);
logger.info("tmpDirElimuAi.mkdir(): " + tmpDirElimuAi.mkdir());
chapterImageFile = new File(tmpDirElimuAi, "chapter-image");
logger.warn("Downloading image from " + sourceUrl + " and storing at " + chapterImageFile);
// 1000 milliseconds x 10
int connectionTimeout = 1000 * 10;
// 1000 milliseconds x 10
int readTimeout = 1000 * 10;
FileUtils.copyURLToFile(sourceUrl, chapterImageFile, connectionTimeout, readTimeout);
} else {
chapterImageFile = new File(chapterFile.getParent(), chapterImageReference);
}
logger.info("chapterImageFile: " + chapterImageFile);
logger.info("chapterImageFile.exists(): " + chapterImageFile.exists());
URI chapterImageUri = chapterImageFile.toURI();
logger.info("chapterImageUri: " + chapterImageUri);
byte[] chapterImageBytes = IOUtils.toByteArray(chapterImageUri);
Image chapterImage = new Image();
chapterImage.setBytes(chapterImageBytes);
byte[] headerBytes = Arrays.copyOfRange(chapterImageBytes, 0, 6);
// "GIF87a"
byte[] gifHeader87a = { 71, 73, 70, 56, 55, 97 };
// "GIF89a"
byte[] gifHeader89a = { 71, 73, 70, 56, 57, 97 };
if (Arrays.equals(gifHeader87a, headerBytes) || Arrays.equals(gifHeader89a, headerBytes)) {
chapterImage.setContentType("image/gif");
chapterImage.setImageFormat(ImageFormat.GIF);
} else if (chapterImageFile.getName().toLowerCase().endsWith(".png")) {
chapterImage.setContentType("image/png");
chapterImage.setImageFormat(ImageFormat.PNG);
} else if (chapterImageFile.getName().toLowerCase().endsWith(".jpg") || chapterImageFile.getName().toLowerCase().endsWith(".jpeg")) {
chapterImage.setContentType("image/jpg");
chapterImage.setImageFormat(ImageFormat.JPG);
} else if (chapterImageFile.getName().toLowerCase().endsWith(".gif")) {
chapterImage.setContentType("image/gif");
chapterImage.setImageFormat(ImageFormat.GIF);
}
try {
int[] dominantColor = ImageColorHelper.getDominantColor(chapterImage.getBytes());
chapterImage.setDominantColor("rgb(" + dominantColor[0] + "," + dominantColor[1] + "," + dominantColor[2] + ")");
} catch (NullPointerException ex) {
// javax.imageio.IIOException: Unsupported Image Type
}
if (chapterImage.getImageFormat() != ImageFormat.GIF) {
// Reduce size if large image
int imageWidth = ImageHelper.getWidth(chapterImageBytes);
logger.info("imageWidth: " + imageWidth + "px");
if (imageWidth > ImageHelper.MINIMUM_WIDTH) {
chapterImageBytes = ImageHelper.scaleImage(chapterImageBytes, ImageHelper.MINIMUM_WIDTH);
chapterImage.setBytes(chapterImageBytes);
}
}
storyBookChapter.setImage(chapterImage);
}
List<String> paragraphs = EPubParagraphExtractionHelper.extractParagraphsFromChapterFile(chapterFile);
logger.info("paragraphs.size(): " + paragraphs.size());
for (int i = 0; i < paragraphs.size(); i++) {
String paragraph = paragraphs.get(i);
logger.info("paragraph: \"" + paragraph + "\"");
logger.info("paragraph.length(): " + paragraph.length());
StoryBookParagraph storyBookParagraph = new StoryBookParagraph();
storyBookParagraph.setStoryBookChapter(storyBookChapter);
storyBookParagraph.setSortOrder(i);
if (paragraph.length() > 1024) {
logger.warn("Reducing the length of the paragraph to its initial 1,024 characters.");
paragraph = paragraph.substring(0, 1023);
}
storyBookParagraph.setOriginalText(paragraph);
// Note: updating the paragraph's list of Words is handled by the ParagraphWordScheduler
storyBookParagraphs.add(storyBookParagraph);
}
}
}
}
if (result.hasErrors()) {
return "content/storybook/create-from-epub";
} else {
// Store the StoryBook in the database
storyBook.setTimeLastUpdate(Calendar.getInstance());
storyBookDao.create(storyBook);
StoryBookContributionEvent storyBookContributionEvent = new StoryBookContributionEvent();
storyBookContributionEvent.setContributor((Contributor) session.getAttribute("contributor"));
storyBookContributionEvent.setTime(Calendar.getInstance());
storyBookContributionEvent.setStoryBook(storyBook);
storyBookContributionEvent.setRevisionNumber(storyBook.getRevisionNumber());
storyBookContributionEvent.setComment("Uploaded ePUB file (🤖 auto-generated comment)");
storyBookContributionEvent.setTimeSpentMs(System.currentTimeMillis() - Long.valueOf(request.getParameter("timeStart")));
storyBookContributionEvent.setPlatform(Platform.WEBAPP);
storyBookContributionEventDao.create(storyBookContributionEvent);
// Store the StoryBook's cover image in the database, and assign it to the StoryBook
storyBookCoverImage.setTitle("storybook-" + storyBook.getId() + "-cover");
imageDao.create(storyBookCoverImage);
storeImageContributionEvent(storyBookCoverImage, session, request);
storyBook.setCoverImage(storyBookCoverImage);
storyBookDao.update(storyBook);
// Store the StoryBookChapters in the database
int chapterSortOrder = 0;
for (StoryBookChapter storyBookChapter : storyBookChapters) {
storyBookChapter.setStoryBook(storyBook);
// Get the paragraphs associated with this chapter
List<StoryBookParagraph> storyBookParagraphsAssociatedWithChapter = new ArrayList<>();
for (StoryBookParagraph storyBookParagraph : storyBookParagraphs) {
if (storyBookParagraph.getStoryBookChapter().getSortOrder().equals(storyBookChapter.getSortOrder())) {
storyBookParagraphsAssociatedWithChapter.add(storyBookParagraph);
}
}
logger.info("storyBookParagraphsAssociatedWithChapter.size(): " + storyBookParagraphsAssociatedWithChapter.size());
// Exclude chapters containing book metadata
boolean isMetadata = false;
for (StoryBookParagraph storyBookParagraph : storyBookParagraphsAssociatedWithChapter) {
String originalTextLowerCase = storyBookParagraph.getOriginalText().toLowerCase();
if (originalTextLowerCase.contains("author: ") || originalTextLowerCase.contains("illustrator: ") || originalTextLowerCase.contains("translator: ") || originalTextLowerCase.contains("creative commons") || originalTextLowerCase.contains("pratham books") || originalTextLowerCase.contains("storyweaver") || originalTextLowerCase.contains("copyright page")) {
isMetadata = true;
break;
}
}
if (isMetadata) {
continue;
}
// Update the chapter's sort order (in case any of the previous chapters were excluded)
storyBookChapter.setSortOrder(chapterSortOrder);
// Store the chapter's image (if any)
Image chapterImage = storyBookChapter.getImage();
if (chapterImage != null) {
chapterImage.setTitle("storybook-" + storyBook.getId() + "-ch-" + (storyBookChapter.getSortOrder() + 1));
imageDao.create(chapterImage);
storeImageContributionEvent(chapterImage, session, request);
}
// Only store the chapter if it has an image or at least one paragraph
if ((chapterImage != null) || (!storyBookParagraphsAssociatedWithChapter.isEmpty())) {
storyBookChapterDao.create(storyBookChapter);
chapterSortOrder++;
}
// Store the chapter's paragraphs in the database
for (StoryBookParagraph storyBookParagraph : storyBookParagraphsAssociatedWithChapter) {
storyBookParagraph.setStoryBookChapter(storyBookChapter);
storyBookParagraphDao.create(storyBookParagraph);
}
}
String contentUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/content/storybook/edit/" + storyBook.getId();
String embedThumbnailUrl = null;
if (storyBook.getCoverImage() != null) {
embedThumbnailUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/image/" + storyBook.getCoverImage().getId() + "_r" + storyBook.getCoverImage().getRevisionNumber() + "." + storyBook.getCoverImage().getImageFormat().toString().toLowerCase();
}
DiscordHelper.sendChannelMessage("Storybook created (imported from ePUB): " + contentUrl, "\"" + storyBookContributionEvent.getStoryBook().getTitle() + "\"", "Comment: \"" + storyBookContributionEvent.getComment() + "\"", null, embedThumbnailUrl);
return "redirect:/content/storybook/edit/" + storyBook.getId();
}
}
use of ai.elimu.model.content.StoryBookParagraph in project webapp by elimu-ai.
the class StoryBookEditController method handleSubmit.
@RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String handleSubmit(@Valid StoryBook storyBook, BindingResult result, Model model, HttpServletRequest request, HttpSession session) {
logger.info("handleSubmit");
StoryBook existingStoryBook = storyBookDao.readByTitle(storyBook.getTitle());
if ((existingStoryBook != null) && !existingStoryBook.getId().equals(storyBook.getId())) {
result.rejectValue("title", "NonUnique");
}
if (result.hasErrors()) {
model.addAttribute("storyBook", storyBook);
model.addAttribute("timeStart", System.currentTimeMillis());
model.addAttribute("contentLicenses", ContentLicense.values());
List<Image> coverImages = imageDao.readAllOrdered();
model.addAttribute("coverImages", coverImages);
model.addAttribute("readingLevels", ReadingLevel.values());
List<StoryBookChapter> storyBookChapters = storyBookChapterDao.readAll(storyBook);
model.addAttribute("storyBookChapters", storyBookChapters);
// Map<StoryBookChapter.id, List<StoryBookParagraph>>
Map<Long, List<StoryBookParagraph>> paragraphsPerStoryBookChapterMap = new HashMap<>();
for (StoryBookChapter storyBookChapter : storyBookChapters) {
paragraphsPerStoryBookChapterMap.put(storyBookChapter.getId(), storyBookParagraphDao.readAll(storyBookChapter));
}
model.addAttribute("paragraphsPerStoryBookChapterMap", paragraphsPerStoryBookChapterMap);
List<String> paragraphs = new ArrayList<>();
for (StoryBookChapter storyBookChapter : storyBookChapters) {
List<StoryBookParagraph> storyBookParagraphs = storyBookParagraphDao.readAll(storyBookChapter);
for (StoryBookParagraph storyBookParagraph : storyBookParagraphs) {
paragraphs.add(storyBookParagraph.getOriginalText());
}
}
model.addAttribute("storyBookContributionEvents", storyBookContributionEventDao.readAll(storyBook));
model.addAttribute("storyBookPeerReviewEvents", storyBookPeerReviewEventDao.readAll(storyBook));
Language language = Language.valueOf(ConfigHelper.getProperty("content.language"));
Map<String, Integer> wordFrequencyMap = WordFrequencyHelper.getWordFrequency(paragraphs, language);
model.addAttribute("wordFrequencyMap", wordFrequencyMap);
Map<String, Word> wordMap = new HashMap<>();
for (Word word : wordDao.readAllOrdered()) {
wordMap.put(word.getText(), word);
}
model.addAttribute("wordMap", wordMap);
model.addAttribute("emojisByWordId", getEmojisByWordId());
Map<String, Integer> letterFrequencyMap = LetterFrequencyHelper.getLetterFrequency(paragraphs, language);
model.addAttribute("letterFrequencyMap", letterFrequencyMap);
Map<String, Letter> letterMap = new HashMap<>();
for (Letter letter : letterDao.readAllOrdered()) {
letterMap.put(letter.getText(), letter);
}
model.addAttribute("letterMap", letterMap);
return "content/storybook/edit";
} else {
storyBook.setTimeLastUpdate(Calendar.getInstance());
storyBook.setRevisionNumber(storyBook.getRevisionNumber() + 1);
storyBookDao.update(storyBook);
StoryBookContributionEvent storyBookContributionEvent = new StoryBookContributionEvent();
storyBookContributionEvent.setContributor((Contributor) session.getAttribute("contributor"));
storyBookContributionEvent.setTime(Calendar.getInstance());
storyBookContributionEvent.setStoryBook(storyBook);
storyBookContributionEvent.setRevisionNumber(storyBook.getRevisionNumber());
storyBookContributionEvent.setComment(StringUtils.abbreviate(request.getParameter("contributionComment"), 1000));
storyBookContributionEvent.setTimeSpentMs(System.currentTimeMillis() - Long.valueOf(request.getParameter("timeStart")));
storyBookContributionEvent.setPlatform(Platform.WEBAPP);
storyBookContributionEventDao.create(storyBookContributionEvent);
String contentUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/content/storybook/edit/" + storyBook.getId();
String embedThumbnailUrl = null;
if (storyBook.getCoverImage() != null) {
embedThumbnailUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/image/" + storyBook.getCoverImage().getId() + "_r" + storyBook.getCoverImage().getRevisionNumber() + "." + storyBook.getCoverImage().getImageFormat().toString().toLowerCase();
}
DiscordHelper.sendChannelMessage("Storybook edited: " + contentUrl, "\"" + storyBookContributionEvent.getStoryBook().getTitle() + "\"", "Comment: \"" + storyBookContributionEvent.getComment() + "\"", null, embedThumbnailUrl);
// Refresh REST API cache
storyBooksJsonService.refreshStoryBooksJSONArray();
return "redirect:/content/storybook/list#" + storyBook.getId();
}
}
use of ai.elimu.model.content.StoryBookParagraph in project webapp by elimu-ai.
the class StoryBookChapterDeleteController method handleRequest.
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String handleRequest(HttpSession session, @PathVariable Long storyBookId, @PathVariable Long id) {
logger.info("handleRequest");
Contributor contributor = (Contributor) session.getAttribute("contributor");
logger.info("contributor.getRoles(): " + contributor.getRoles());
if (!contributor.getRoles().contains(Role.EDITOR)) {
// TODO: return HttpStatus.FORBIDDEN
throw new IllegalAccessError("Missing role for access");
}
StoryBookChapter storyBookChapterToBeDeleted = storyBookChapterDao.read(id);
logger.info("storyBookChapterToBeDeleted: " + storyBookChapterToBeDeleted);
logger.info("storyBookChapterToBeDeleted.getSortOrder(): " + storyBookChapterToBeDeleted.getSortOrder());
// Delete the chapter's paragraphs
List<StoryBookParagraph> storyBookParagraphs = storyBookParagraphDao.readAll(storyBookChapterToBeDeleted);
logger.info("storyBookParagraphs.size(): " + storyBookParagraphs.size());
for (StoryBookParagraph storyBookParagraphToBeDeleted : storyBookParagraphs) {
// Delete the paragraph's reference from corresponding audios (if any)
List<Audio> paragraphAudios = audioDao.readAll(storyBookParagraphToBeDeleted);
for (Audio paragraphAudio : paragraphAudios) {
paragraphAudio.setStoryBookParagraph(null);
audioDao.update(paragraphAudio);
}
logger.info("Deleting StoryBookParagraph with ID " + storyBookParagraphToBeDeleted.getId());
storyBookParagraphDao.delete(storyBookParagraphToBeDeleted);
}
// Delete the chapter
logger.info("Deleting StoryBookChapter with ID " + storyBookChapterToBeDeleted.getId());
storyBookChapterDao.delete(storyBookChapterToBeDeleted);
// Delete the chapter's image (if any)
Image chapterImage = storyBookChapterToBeDeleted.getImage();
logger.info("chapterImage: " + chapterImage);
if (chapterImage != null) {
// Remove content labels
chapterImage.setLiteracySkills(null);
chapterImage.setNumeracySkills(null);
chapterImage.setLetters(null);
chapterImage.setNumbers(null);
chapterImage.setWords(null);
imageDao.update(chapterImage);
// Remove contribution events
for (ImageContributionEvent imageContributionEvent : imageContributionEventDao.readAll(chapterImage)) {
logger.warn("Deleting ImageContributionEvent from the database");
imageContributionEventDao.delete(imageContributionEvent);
}
logger.warn("Deleting the chapter image from the database");
imageDao.delete(chapterImage);
}
// Update the StoryBook's metadata
StoryBook storyBook = storyBookChapterToBeDeleted.getStoryBook();
storyBook.setTimeLastUpdate(Calendar.getInstance());
storyBook.setRevisionNumber(storyBook.getRevisionNumber() + 1);
storyBook.setPeerReviewStatus(PeerReviewStatus.PENDING);
storyBookDao.update(storyBook);
// Store contribution event
StoryBookContributionEvent storyBookContributionEvent = new StoryBookContributionEvent();
storyBookContributionEvent.setContributor(contributor);
storyBookContributionEvent.setTime(Calendar.getInstance());
storyBookContributionEvent.setStoryBook(storyBook);
storyBookContributionEvent.setRevisionNumber(storyBook.getRevisionNumber());
storyBookContributionEvent.setComment("Deleted storybook chapter " + (storyBookChapterToBeDeleted.getSortOrder() + 1) + " (🤖 auto-generated comment)");
storyBookContributionEvent.setTimeSpentMs(0L);
storyBookContributionEvent.setPlatform(Platform.WEBAPP);
storyBookContributionEventDao.create(storyBookContributionEvent);
String contentUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/content/storybook/edit/" + storyBook.getId();
String embedThumbnailUrl = null;
if (storyBook.getCoverImage() != null) {
embedThumbnailUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/image/" + storyBook.getCoverImage().getId() + "_r" + storyBook.getCoverImage().getRevisionNumber() + "." + storyBook.getCoverImage().getImageFormat().toString().toLowerCase();
}
DiscordHelper.sendChannelMessage("Storybook chapter deleted: " + contentUrl, "\"" + storyBookContributionEvent.getStoryBook().getTitle() + "\"", "Comment: \"" + storyBookContributionEvent.getComment() + "\"", null, embedThumbnailUrl);
// Update the sorting order of the remaining chapters
List<StoryBookChapter> storyBookChapters = storyBookChapterDao.readAll(storyBook);
logger.info("storyBookChapters.size(): " + storyBookChapters.size());
for (StoryBookChapter storyBookChapter : storyBookChapters) {
logger.info("storyBookChapter.getId(): " + storyBookChapter.getId() + ", storyBookChapter.getSortOrder(): " + storyBookChapter.getSortOrder());
if (storyBookChapter.getSortOrder() > storyBookChapterToBeDeleted.getSortOrder()) {
// Reduce sort order by 1
storyBookChapter.setSortOrder(storyBookChapter.getSortOrder() - 1);
storyBookChapterDao.update(storyBookChapter);
logger.info("storyBookChapter.getSortOrder() (after update): " + storyBookChapter.getSortOrder());
}
}
// Refresh the REST API cache
storyBooksJsonService.refreshStoryBooksJSONArray();
return "redirect:/content/storybook/edit/" + storyBookId;
}
Aggregations