no.dusken.momus.service.ArticleService.java Source code

Java tutorial

Introduction

Here is the source code for no.dusken.momus.service.ArticleService.java

Source

/*
 * Copyright 2014 Studentmediene i Trondheim AS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package no.dusken.momus.service;

import com.google.api.services.drive.model.File;
import no.dusken.momus.authentication.UserLoginService;
import no.dusken.momus.exceptions.RestException;
import no.dusken.momus.model.Article;
import no.dusken.momus.model.ArticleRevision;
import no.dusken.momus.model.Person;
import no.dusken.momus.service.drive.GoogleDriveService;
import no.dusken.momus.service.indesign.IndesignExport;
import no.dusken.momus.service.indesign.IndesignGenerator;
import no.dusken.momus.service.repository.ArticleRepository;
import no.dusken.momus.service.repository.ArticleRevisionRepository;
import no.dusken.momus.service.search.ArticleQueryBuilder;
import no.dusken.momus.service.search.ArticleSearchParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class ArticleService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    ArticleRepository articleRepository;

    @Autowired
    ArticleRevisionRepository articleRevisionRepository;

    @Autowired
    IndesignGenerator indesignGenerator;

    @Autowired
    UserLoginService userLoginService;

    @Autowired
    GoogleDriveService googleDriveService;

    @PersistenceContext
    EntityManager entityManager;

    public Article getArticleById(Long id) {
        Article article = articleRepository.findOne(id);
        if (article == null) {
            logger.warn("Article with id {} not found, tried by user {}", id, userLoginService.getId());
            throw new RestException("Article " + id + " not found", 404);
        }
        return article;
    }

    public Article createNewArticle(Article article) {
        File document = googleDriveService.createDocument(article.getName());

        if (document == null) {
            throw new RestException("Couldn't create article, Google Docs failed", 500);
        }

        article.setGoogleDriveId(document.getId());

        Article newArticle = articleRepository.saveAndFlush(article);

        logger.info("Article {} ({}) created ", newArticle.getId(), newArticle.getName());
        return articleRepository.findOne(newArticle.getId());
    }

    public Article saveUpdatedArticle(Article article) {
        article.setLastUpdated(new Date());
        logger.info("Article \"{}\" (id: {}) updated", article.getName(), article.getId());
        return articleRepository.saveAndFlush(article);
    }

    public Article saveNewContent(Article article) {
        Article existing = articleRepository.findOne(article.getId());
        String newContent = article.getContent();
        String oldContent = existing.getContent();

        if (newContent.equals(oldContent)) {
            // Inserting comments in the Google Docs triggers a change, but the content we see is the same.
            // So it would look weird having multiple revisions without any changes.
            logger.info("No changes made to content of article {}", article.getId());
            return existing;
        }

        existing.setContent(newContent);
        createRawContent(existing);

        createNewRevision(existing, false);

        return saveUpdatedArticle(existing);
    }

    public Article archiveArticle(Article article) {
        Article existing = getArticleById(article.getId());
        existing.setArchived(true);
        return saveUpdatedArticle(existing);
    }

    public Article restoreArticle(Article article) {
        Article existing = getArticleById(article.getId());
        existing.setArchived(false);
        return saveUpdatedArticle(existing);
    }

    public Article saveMetadata(Article article) {
        Article existing = articleRepository.findOne(article.getId());

        if (!article.getStatus().equals(existing.getStatus())) {
            createNewRevision(article, true);
        }

        existing.setName(article.getName());
        existing.setJournalists(article.getJournalists());
        existing.setPhotographers(article.getPhotographers());
        existing.setComment(article.getComment());
        existing.setPublication(article.getPublication());
        existing.setType(article.getType());
        existing.setStatus(article.getStatus());
        existing.setSection(article.getSection());
        existing.setUseIllustration(article.getUseIllustration());
        existing.setImageText(article.getImageText());
        createRawContent(existing);
        existing.setQuoteCheckStatus(article.getQuoteCheckStatus());
        existing.setExternalAuthor(article.getExternalAuthor());
        existing.setExternalPhotographer(article.getExternalPhotographer());

        return saveUpdatedArticle(existing);
    }

    public Article saveNote(Article article) {
        Article existing = articleRepository.findOne(article.getId());

        existing.setNote(article.getNote());

        return saveUpdatedArticle(existing);
    }

    public List<Article> searchForArticles(ArticleSearchParams params) {
        long start = System.currentTimeMillis();

        ArticleQueryBuilder builder = new ArticleQueryBuilder(params);
        String queryText = builder.getFullQuery();
        Map<String, Object> queryParams = builder.getQueryParams();

        TypedQuery<Article> query = entityManager.createQuery(queryText, Article.class);

        for (Map.Entry<String, Object> e : queryParams.entrySet()) {
            query.setParameter(e.getKey(), e.getValue());
        }

        query.setMaxResults(params.getPageSize() + 1); // One extra, so searchpage can see if there is "more pages"
        query.setFirstResult(params.getStartOfPage());

        List<Article> resultList = query.getResultList();

        long end = System.currentTimeMillis();
        long timeUsed = end - start;
        logger.debug("Time spent on search: {}ms", timeUsed);
        if (timeUsed > 800) {
            logger.warn("Time spent on search high ({}ms), params were: {}", timeUsed, params);
        }

        return resultList;
    }

    public IndesignExport exportArticle(Long id) {
        Article article = getArticleById(id);
        return indesignGenerator.generateFromArticle(article);
    }

    public ArticleRepository getArticleRepository() {
        return articleRepository;
    }

    private void createNewRevision(Article article, boolean changedStatus) {
        ArticleRevision revision = getExistingRevision(article, changedStatus);

        revision.setStatusChanged(changedStatus);
        revision.setContent(article.getContent());
        revision.setArticle(article);
        revision.setStatus(article.getStatus());

        revision = articleRevisionRepository.save(revision);
        logger.info("Saved revision for article(id:{}) with id: {}, content:\n{}", article.getId(),
                revision.getId(), revision.getContent());
    }

    /**
     * Returns a reusable revision if one is found, or a new one otherwise.
     * This is to not get too man small revision.
     * If the revision is for a status change, a new one is returned anyway
     * A reusable revision is one that wasn't for a status change and not too old
     */
    private ArticleRevision getExistingRevision(Article article, boolean changedStatus) {
        ArticleRevision newRevision = new ArticleRevision();
        newRevision.setSavedDate(new Date());

        if (changedStatus) {
            return newRevision;
        }

        List<ArticleRevision> revisions = articleRevisionRepository
                .findByArticleIdOrderBySavedDateDesc(article.getId());

        if (revisions.size() == 0) {
            return newRevision;
        }

        ArticleRevision existing = revisions.get(0);
        long timeDiff = new Date().getTime() - existing.getSavedDate().getTime();

        if (timeDiff < 3 * 60 * 1000 && !existing.isStatusChanged()) {
            logger.info("Reusing revision {} for article {}", existing.getId(), article.getId());
            return existing;
        } else {
            return newRevision;
        }
    }

    public void createRawContent(Article article) {
        StringBuilder raw = new StringBuilder();

        String content = stripOffHtml(article.getContent());

        int contentLength = content.length();

        raw.append(content).append(" ").append(article.getName()).append(" ")
                .append(article.getSection() != null ? article.getSection().getName() : "").append(" ")
                .append(article.getStatus() != null ? article.getStatus().getName() : "").append(" ")
                .append(article.getType() != null ? article.getType().getName() : "").append(" ")
                .append(article.getComment()).append(" ");

        for (Person journalist : article.getJournalists()) {
            raw.append(journalist.getFullName()).append(" ");
        }
        for (Person photo : article.getPhotographers()) {
            raw.append(photo.getFullName()).append(" ");
        }

        String rawContent = raw.toString().toLowerCase();
        logger.info("Raw content {}, length of content: {}", rawContent, contentLength);

        article.setRawcontent(rawContent);
        article.setContentLength(contentLength);
    }

    private String stripOffHtml(String html) {
        String[] tags = { "<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<p>", "<i>", "<b>", "<blockquote>", "<br>",
                "<ul>", "<ol>", "<li>" };
        for (String tag : tags) {
            html = html.replaceAll(tag, " ").replaceAll(tag.substring(0, 1) + "/" + tag.substring(1, tag.length()),
                    "");
        }

        // Remove consecutive spaces
        html = html.replaceAll("\\s+", " ");

        return html;
    }

}