Search in sources :

Example 6 with StoryBookContributionEvent

use of ai.elimu.model.contributor.StoryBookContributionEvent in project webapp by elimu-ai.

the class StoryBookParagraphEditController method handleSubmit.

@RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String handleSubmit(HttpServletRequest request, HttpSession session, @Valid StoryBookParagraph storyBookParagraph, BindingResult result, Model model) {
    logger.info("handleSubmit");
    Contributor contributor = (Contributor) session.getAttribute("contributor");
    if (result.hasErrors()) {
        model.addAttribute("storyBookParagraph", storyBookParagraph);
        model.addAttribute("timeStart", System.currentTimeMillis());
        return "content/storybook/paragraph/edit";
    } else {
        // Fetch previously stored paragraph to make it possible to check if the text was modified or not when
        // storing the StoryBookContributionEvent below.
        StoryBookParagraph storyBookParagraphBeforeEdit = storyBookParagraphDao.read(storyBookParagraph.getId());
        storyBookParagraphDao.update(storyBookParagraph);
        // Update the storybook's metadata
        StoryBook storyBook = storyBookParagraph.getStoryBookChapter().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("Edited storybook paragraph in chapter " + (storyBookParagraph.getStoryBookChapter().getSortOrder() + 1) + " (🤖 auto-generated comment)");
        if (!storyBookParagraphBeforeEdit.getOriginalText().equals(storyBookParagraph.getOriginalText())) {
            storyBookContributionEvent.setParagraphTextBefore(StringUtils.abbreviate(storyBookParagraphBeforeEdit.getOriginalText(), 1000));
            storyBookContributionEvent.setParagraphTextAfter(StringUtils.abbreviate(storyBookParagraph.getOriginalText(), 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 paragraph edited: " + contentUrl, "\"" + storyBookContributionEvent.getStoryBook().getTitle() + "\"", "Comment: \"" + storyBookContributionEvent.getComment() + "\"", null, embedThumbnailUrl);
        // Refresh the REST API cache
        storyBooksJsonService.refreshStoryBooksJSONArray();
        return "redirect:/content/storybook/edit/" + storyBookParagraph.getStoryBookChapter().getStoryBook().getId() + "#ch-id-" + storyBookParagraph.getStoryBookChapter().getId();
    }
}
Also used : StoryBookContributionEvent(ai.elimu.model.contributor.StoryBookContributionEvent) StoryBook(ai.elimu.model.content.StoryBook) Contributor(ai.elimu.model.contributor.Contributor) StoryBookParagraph(ai.elimu.model.content.StoryBookParagraph) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 7 with StoryBookContributionEvent

use of ai.elimu.model.contributor.StoryBookContributionEvent in project webapp by elimu-ai.

the class StoryBookPeerReviewEventCreateController method handleSubmit.

@RequestMapping(method = RequestMethod.POST)
public String handleSubmit(@RequestParam Long storyBookContributionEventId, @RequestParam Boolean approved, @RequestParam(required = false) String comment, HttpSession session) {
    logger.info("handleSubmit");
    Contributor contributor = (Contributor) session.getAttribute("contributor");
    logger.info("storyBookContributionEventId: " + storyBookContributionEventId);
    StoryBookContributionEvent storyBookContributionEvent = storyBookContributionEventDao.read(storyBookContributionEventId);
    logger.info("storyBookContributionEvent: " + storyBookContributionEvent);
    // Store the peer review event
    StoryBookPeerReviewEvent storyBookPeerReviewEvent = new StoryBookPeerReviewEvent();
    storyBookPeerReviewEvent.setContributor(contributor);
    storyBookPeerReviewEvent.setStoryBookContributionEvent(storyBookContributionEvent);
    storyBookPeerReviewEvent.setApproved(approved);
    storyBookPeerReviewEvent.setComment(StringUtils.abbreviate(comment, 1000));
    storyBookPeerReviewEvent.setTime(Calendar.getInstance());
    storyBookPeerReviewEvent.setPlatform(Platform.WEBAPP);
    storyBookPeerReviewEventDao.create(storyBookPeerReviewEvent);
    String contentUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/content/storybook/edit/" + storyBookContributionEvent.getStoryBook().getId();
    String embedThumbnailUrl = null;
    if (storyBookContributionEvent.getStoryBook().getCoverImage() != null) {
        embedThumbnailUrl = "http://" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language").toLowerCase() + ".elimu.ai/image/" + storyBookContributionEvent.getStoryBook().getCoverImage().getId() + "_r" + storyBookContributionEvent.getStoryBook().getCoverImage().getRevisionNumber() + "." + storyBookContributionEvent.getStoryBook().getCoverImage().getImageFormat().toString().toLowerCase();
    }
    DiscordHelper.sendChannelMessage("Storybook peer-reviewed: " + contentUrl, "\"" + storyBookContributionEvent.getStoryBook().getTitle() + "\"", "Comment: \"" + storyBookPeerReviewEvent.getComment() + "\"", storyBookPeerReviewEvent.isApproved(), embedThumbnailUrl);
    // Update the storybook's peer review status
    int approvedCount = 0;
    int notApprovedCount = 0;
    for (StoryBookPeerReviewEvent peerReviewEvent : storyBookPeerReviewEventDao.readAll(storyBookContributionEvent)) {
        if (peerReviewEvent.isApproved()) {
            approvedCount++;
        } else {
            notApprovedCount++;
        }
    }
    logger.info("approvedCount: " + approvedCount);
    logger.info("notApprovedCount: " + notApprovedCount);
    StoryBook storyBook = storyBookContributionEvent.getStoryBook();
    if (approvedCount >= notApprovedCount) {
        storyBook.setPeerReviewStatus(PeerReviewStatus.APPROVED);
    } else {
        storyBook.setPeerReviewStatus(PeerReviewStatus.NOT_APPROVED);
    }
    storyBookDao.update(storyBook);
    return "redirect:/content/storybook/edit/" + storyBookContributionEvent.getStoryBook().getId() + "#contribution-events";
}
Also used : StoryBookContributionEvent(ai.elimu.model.contributor.StoryBookContributionEvent) StoryBook(ai.elimu.model.content.StoryBook) Contributor(ai.elimu.model.contributor.Contributor) StoryBookPeerReviewEvent(ai.elimu.model.contributor.StoryBookPeerReviewEvent) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 8 with StoryBookContributionEvent

use of ai.elimu.model.contributor.StoryBookContributionEvent in project webapp by elimu-ai.

the class ContributorController method handleRequest.

@RequestMapping(method = RequestMethod.GET)
public String handleRequest(@PathVariable Long contributorId, Model model) {
    logger.info("handleRequest");
    Contributor contributor = contributorDao.read(contributorId);
    model.addAttribute("contributor2", contributor);
    // For contributor-summarized.jsp
    model.addAttribute("storyBookContributionsCount", storyBookContributionEventDao.readCount(contributor));
    model.addAttribute("storyBookPeerReviewsCount", storyBookPeerReviewEventDao.readCount(contributor));
    model.addAttribute("audioContributionsCount", audioContributionEventDao.readCount(contributor));
    model.addAttribute("audioPeerReviewsCount", audioPeerReviewEventDao.readCount(contributor));
    model.addAttribute("wordContributionsCount", wordContributionEventDao.readCount(contributor));
    model.addAttribute("wordPeerReviewsCount", wordPeerReviewEventDao.readCount(contributor));
    model.addAttribute("numberContributionsCount", numberContributionEventDao.readCount(contributor));
    model.addAttribute("numberPeerReviewsCount", numberPeerReviewEventDao.readCount(contributor));
    // For contributor-storybooks.jsp
    List<StoryBookContributionEvent> storyBookContributionEvents = storyBookContributionEventDao.readAll(contributor);
    model.addAttribute("storyBookContributionEvents", storyBookContributionEvents);
    model.addAttribute("storyBookPeerReviewEvents", storyBookPeerReviewEventDao.readAll(contributor));
    Map<Long, List<StoryBookPeerReviewEvent>> storyBookPeerReviewEventsByContributionMap = new HashMap<>();
    for (StoryBookContributionEvent storyBookContributionEvent : storyBookContributionEvents) {
        storyBookPeerReviewEventsByContributionMap.put(storyBookContributionEvent.getId(), storyBookPeerReviewEventDao.readAll(storyBookContributionEvent));
    }
    model.addAttribute("storyBookPeerReviewEventsByContributionMap", storyBookPeerReviewEventsByContributionMap);
    // For contributor-words.jsp
    List<WordContributionEvent> wordContributionEvents = wordContributionEventDao.readAll(contributor);
    model.addAttribute("wordContributionEvents", wordContributionEvents);
    model.addAttribute("wordPeerReviewEvents", wordPeerReviewEventDao.readAll(contributor));
    Map<Long, List<WordPeerReviewEvent>> wordPeerReviewEventsByContributionMap = new HashMap<>();
    for (WordContributionEvent wordContributionEvent : wordContributionEvents) {
        wordPeerReviewEventsByContributionMap.put(wordContributionEvent.getId(), wordPeerReviewEventDao.readAll(wordContributionEvent));
    }
    model.addAttribute("wordPeerReviewEventsByContributionMap", wordPeerReviewEventsByContributionMap);
    // For contributor-numbers.jsp
    List<NumberContributionEvent> numberContributionEvents = numberContributionEventDao.readAll(contributor);
    model.addAttribute("numberContributionEvents", numberContributionEvents);
    model.addAttribute("numberPeerReviewEvents", numberPeerReviewEventDao.readAll(contributor));
    Map<Long, List<NumberPeerReviewEvent>> numberPeerReviewEventsByContributionMap = new HashMap<>();
    for (NumberContributionEvent numberContributionEvent : numberContributionEvents) {
        numberPeerReviewEventsByContributionMap.put(numberContributionEvent.getId(), numberPeerReviewEventDao.readAll(numberContributionEvent));
    }
    model.addAttribute("numberPeerReviewEventsByContributionMap", numberPeerReviewEventsByContributionMap);
    return "content/contributor/contributor";
}
Also used : HashMap(java.util.HashMap) Contributor(ai.elimu.model.contributor.Contributor) StoryBookContributionEvent(ai.elimu.model.contributor.StoryBookContributionEvent) NumberContributionEvent(ai.elimu.model.contributor.NumberContributionEvent) List(java.util.List) WordContributionEvent(ai.elimu.model.contributor.WordContributionEvent) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 9 with StoryBookContributionEvent

use of ai.elimu.model.contributor.StoryBookContributionEvent 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");
}
Also used : StoryBookChapter(ai.elimu.model.content.StoryBookChapter) LetterContributionEvent(ai.elimu.model.contributor.LetterContributionEvent) Word(ai.elimu.model.content.Word) StoryBook(ai.elimu.model.content.StoryBook) StoryBookLearningEvent(ai.elimu.model.analytics.StoryBookLearningEvent) StoryBookGson(ai.elimu.model.v2.gson.content.StoryBookGson) ArrayList(java.util.ArrayList) Contributor(ai.elimu.model.contributor.Contributor) URL(java.net.URL) LetterSoundCorrespondenceContributionEvent(ai.elimu.model.contributor.LetterSoundCorrespondenceContributionEvent) StoryBookContributionEvent(ai.elimu.model.contributor.StoryBookContributionEvent) Number(ai.elimu.model.content.Number) Emoji(ai.elimu.model.content.Emoji) LetterSoundCorrespondence(ai.elimu.model.content.LetterSoundCorrespondence) WordContributionEvent(ai.elimu.model.contributor.WordContributionEvent) StoryBookParagraphGson(ai.elimu.model.v2.gson.content.StoryBookParagraphGson) StoryBookParagraph(ai.elimu.model.content.StoryBookParagraph) Sound(ai.elimu.model.content.Sound) Letter(ai.elimu.model.content.Letter) NumberContributionEvent(ai.elimu.model.contributor.NumberContributionEvent) File(java.io.File) StoryBookChapterGson(ai.elimu.model.v2.gson.content.StoryBookChapterGson)

Example 10 with StoryBookContributionEvent

use of ai.elimu.model.contributor.StoryBookContributionEvent 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();
    }
}
Also used : StoryBookChapter(ai.elimu.model.content.StoryBookChapter) ArrayList(java.util.ArrayList) StoryBookParagraph(ai.elimu.model.content.StoryBookParagraph) Image(ai.elimu.model.content.multimedia.Image) URI(java.net.URI) URL(java.net.URL) StoryBookContributionEvent(ai.elimu.model.contributor.StoryBookContributionEvent) File(java.io.File) MultipartFile(org.springframework.web.multipart.MultipartFile) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Aggregations

StoryBookContributionEvent (ai.elimu.model.contributor.StoryBookContributionEvent)13 RequestMapping (org.springframework.web.bind.annotation.RequestMapping)12 Contributor (ai.elimu.model.contributor.Contributor)10 StoryBook (ai.elimu.model.content.StoryBook)9 StoryBookParagraph (ai.elimu.model.content.StoryBookParagraph)6 Image (ai.elimu.model.content.multimedia.Image)5 StoryBookChapter (ai.elimu.model.content.StoryBookChapter)4 ArrayList (java.util.ArrayList)4 NumberContributionEvent (ai.elimu.model.contributor.NumberContributionEvent)3 WordContributionEvent (ai.elimu.model.contributor.WordContributionEvent)3 HashMap (java.util.HashMap)3 Letter (ai.elimu.model.content.Letter)2 Word (ai.elimu.model.content.Word)2 Audio (ai.elimu.model.content.multimedia.Audio)2 StoryBookPeerReviewEvent (ai.elimu.model.contributor.StoryBookPeerReviewEvent)2 File (java.io.File)2 URL (java.net.URL)2 List (java.util.List)2 StoryBookLearningEvent (ai.elimu.model.analytics.StoryBookLearningEvent)1 Emoji (ai.elimu.model.content.Emoji)1