diff --git a/src/main/java/org/wikitolearn/wikirating/model/Revision.java b/src/main/java/org/wikitolearn/wikirating/model/Revision.java index 3f69ba5..9ecd57e 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Revision.java +++ b/src/main/java/org/wikitolearn/wikirating/model/Revision.java @@ -1,365 +1,366 @@ package org.wikitolearn.wikirating.model; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.NodeEntity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import org.neo4j.ogm.annotation.Relationship; import org.neo4j.ogm.annotation.typeconversion.DateLong; //import org.springframework.data.annotation.Transient; import java.util.Date; import java.util.Set; /** * This class handles the data of the Revision of a page. * Created by valsdav on 14/03/17. */ @JsonIgnoreProperties(ignoreUnknown = true) @NodeEntity( label = "Revision" ) public class Revision { @GraphId private Long graphId; @JsonProperty("revid") private int revId; private String lang; @JsonProperty("userid") private int userId; @JsonProperty("parentid") private int parentId; @DateLong private Date timestamp; @JsonProperty("size") private long length; private double changeCoefficient; private double currentMeanVote; private double currentVotesReliability; private double currentNormalisesVotesReliability; private double totalMeanVote; private double totalVotesReliability; private double totalNormalisesVotesReliability; private boolean validated; @Index(unique = true, primary=true) private String langRevId; @Relationship(type="PREVIOUS_REVISION", direction = Relationship.OUTGOING) private Revision previousRevision; @Relationship(type="VOTE", direction = Relationship.INCOMING) private Set votes; //This relationship represents the connection between the revision //and its creator (user). It saves his reliability at the moment of the author. @Relationship(type="AUTHOR", direction = Relationship.INCOMING) private Author author; /** * */ public Revision() {} /** * @param revId * @param userId * @param parentId * @param length * @param lang * @param timestamp */ public Revision(int revId, String lang, int userId, int parentId, long length, Date timestamp) { this.revId = revId; this.langRevId = lang +"_"+revId; this.userId = userId; this.parentId = parentId; this.length = length; this.lang = lang; this.timestamp = timestamp; this.validated = false; + this.changeCoefficient = 0.0; } /** * @return the revId */ public int getRevId() { return revId; } /** * @param revId the revId to set */ public void setRevId(int revId) { this.revId = revId; } /** * @return the userId */ public int getUserId() { return userId; } /** * @param userid the userId to set */ public void setUserId(int userId) { this.userId = userId; } /** * @return the parentId */ public int getParentId() { return parentId; } /** * @param parentId the parentId to set */ public void setParentId(int parentId) { this.parentId = parentId; } /** * @return the length */ public long getLength() { return length; } /** * @param length the length to set */ public void setLength(long length) { this.length = length; } /** * @return the changeCoefficient */ public double getChangeCoefficient() { return changeCoefficient; } /** * @param changeCoefficient the changeCoefficient to set */ public void setChangeCoefficient(double changeCoefficient) { this.changeCoefficient = changeCoefficient; } /** * @return the currentMeanVote */ public double getCurrentMeanVote() { return currentMeanVote; } /** * @param currentMeanVote the currentMeanVote to set */ public void setCurrentMeanVote(double currentMeanVote) { this.currentMeanVote = currentMeanVote; } /** * @return the currentVotesReliability */ public double getCurrentVotesReliability() { return currentVotesReliability; } /** * @param currentVotesReliability the currentVotesReliability to set */ public void setCurrentVotesReliability(double currentVotesReliability) { this.currentVotesReliability = currentVotesReliability; } /** * @return the currentNormalisesVotesReliability */ public double getCurrentNormalisesVotesReliability() { return currentNormalisesVotesReliability; } /** * @param currentNormalisesVotesReliability the currentNormalisesVotesReliability to set */ public void setCurrentNormalisesVotesReliability(double currentNormalisesVotesReliability) { this.currentNormalisesVotesReliability = currentNormalisesVotesReliability; } /** * @return the totalMeanVote */ public double getTotalMeanVote() { return totalMeanVote; } /** * @param totalMeanVote the totalMeanVote to set */ public void setTotalMeanVote(double totalMeanVote) { this.totalMeanVote = totalMeanVote; } /** * @return the totalVotesReliability */ public double getTotalVotesReliability() { return totalVotesReliability; } /** * @param totalVotesReliability the totalVotesReliability to set */ public void setTotalVotesReliability(double totalVotesReliability) { this.totalVotesReliability = totalVotesReliability; } /** * @return the totalNormalisesVotesReliability */ public double getTotalNormalisesVotesReliability() { return totalNormalisesVotesReliability; } /** * @param totalNormalisesVotesReliability the totalNormalisesVotesReliability to set */ public void setTotalNormalisesVotesReliability(double totalNormalisesVotesReliability) { this.totalNormalisesVotesReliability = totalNormalisesVotesReliability; } /** * @return the validated */ public boolean isValidated() { return validated; } /** * @param validated the validated to set */ public void setValidated(boolean validated) { this.validated = validated; } /** * @return the lang */ public String getLang() { return lang; } /** * @param lang the lang to set */ public void setLang(String lang) { this.lang = lang; } /** * @return the langRevId */ public String getLangRevId() { return langRevId; } /** * @param langRevId the langRevId to set */ public void setLangRevId(String langRevId) { this.langRevId = langRevId; } /** * @return the graphId */ public Long getGraphId() { return graphId; } /** * @param graphId the graphId to set */ public void setGraphId(Long graphId) { this.graphId = graphId; } /** * @return the timestamp */ public Date getTimestamp() { return timestamp; } /** * @param timestamp the timestamp to set */ public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } /** * @return the previousRevision */ public Revision getPreviousRevision() { return previousRevision; } /** * @param previousRevision the previousRevision to set */ public void setPreviousRevision(Revision previousRevision) { this.previousRevision = previousRevision; } /** * @return the votes */ public Set getVotes() { return votes; } /** * @param votes the votes to set */ public void setVotes(Set votes) { this.votes = votes; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } /** * * @param vote the vote to set */ public void addVote(Vote vote){ this.votes.add(vote); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Revision{" + "revId=" + revId + ", lang='" + lang + '\'' + ", userId=" + userId + ", parentId=" + parentId + ", timestamp=" + timestamp + ", length=" + length + ", validated=" + validated + ", langRevId='" + langRevId + '\'' + ", author=" + author + '}'; } } \ No newline at end of file diff --git a/src/main/java/org/wikitolearn/wikirating/service/PageService.java b/src/main/java/org/wikitolearn/wikirating/service/PageService.java index 981eea2..c4e160d 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/PageService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/PageService.java @@ -1,369 +1,372 @@ /** * */ package org.wikitolearn.wikirating.service; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.wikitolearn.wikirating.exception.GetPagesUpdateInfoException; import org.wikitolearn.wikirating.exception.PageNotFoundException; import org.wikitolearn.wikirating.exception.UpdatePagesAndRevisionsException; import org.wikitolearn.wikirating.model.CourseTree; import org.wikitolearn.wikirating.model.Page; import org.wikitolearn.wikirating.model.Revision; import org.wikitolearn.wikirating.model.UpdateInfo; import org.wikitolearn.wikirating.repository.PageRepository; import org.wikitolearn.wikirating.service.mediawiki.PageMediaWikiService; import org.wikitolearn.wikirating.service.mediawiki.UpdateMediaWikiService; /** * * @author aletundo, valsdav * */ @Service public class PageService { private static final Logger LOG = LoggerFactory.getLogger(PageService.class); @Autowired private PageMediaWikiService pageMediaWikiService; @Autowired private RevisionService revisionService; @Autowired private PageRepository pageRepository; @Autowired private UpdateMediaWikiService updateMediaWikiService; @Autowired private UserService userService; @Value("${mediawiki.namespace}") private String namespace; /** * This methods inserts all the pages inside the DB querying the MediaWiki API. * @param lang String * @param apiUrl String The MediaWiki API url * @return CompletableFuture */ @Async public CompletableFuture initPages( String lang, String apiUrl ){ List pages = pageMediaWikiService.getAll(apiUrl); pages.forEach(page -> { page.setLang(lang); page.setLangPageId(lang + "_" +page.getPageId()); }); pageRepository.save(pages); LOG.info("Inserted all {} pages", lang); return CompletableFuture.completedFuture(true); } /** * * @param lang * @param apiUrl * @param start * @param end * @return * @throws UpdatePagesAndRevisionsException */ @Async public CompletableFuture updatePages(String lang, String apiUrl, Date start, Date end) throws UpdatePagesAndRevisionsException{ try{ List updates = updateMediaWikiService.getPagesUpdateInfo(apiUrl, namespace, start, end); for(UpdateInfo update : updates){ if(!namespace.equals(update.getNs())) continue; switch (update.getType()) { case "new": - // Create the new page + // Create the new page. The change coefficient for the new revision is set to 0 by default. Revision newRev = revisionService.addRevision(update.getRevid(), lang, update.getUserid(), update.getOld_revid(), update.getNewlen(), update.getTimestamp()); // Then create a new Page and link it with the revision addPage(update.getPageid(), update.getTitle(), lang, newRev); userService.setAuthorship(newRev); break; case "edit": // Create a new revision Revision updateRev = revisionService.addRevision(update.getRevid(), lang, update.getUserid(), update.getOld_revid(), update.getNewlen(), update.getTimestamp()); // Then add it to the page addRevisionToPage(lang + "_" + update.getPageid(), updateRev); + // Then calculate the changeCoefficient + revisionService.setChangeCoefficient(apiUrl, updateRev); + // Finally set the authorship userService.setAuthorship(updateRev); break; case "move": // Move the page to the new title movePage(update.getTitle(), update.getNewTitle(), lang); break; case "delete": // Delete the page and all its revisions deletePage(update.getTitle(), lang); break; default: break; } } }catch(GetPagesUpdateInfoException | PageNotFoundException e){ LOG.error("An error occurred while updating pages and revisions: {}", e.getMessage()); throw new UpdatePagesAndRevisionsException(); } return CompletableFuture.completedFuture(true); } /** * Create a new Page. It requires the firstRevision of the Page in order * to create the LAST_REVISION and FIRST_REVISION relationships. * @param pageid * @param title * @param lang * @param firstRevision * @return the added page */ public Page addPage(int pageid, String title, String lang, Revision firstRevision){ Page page = new Page(pageid, title, lang, lang + "_" + pageid); // Add the links from the page to the first and last revision page.setFistRevision(firstRevision); page.setLastRevision(firstRevision); pageRepository.save(page); return page; } /** * Get the page with the given pageId and language * @param pageId the id of the page * @param lang the language of the page * @return the requested page * @throws PageNotFoundException */ public Page getPage(int pageId, String lang) throws PageNotFoundException{ Page page = pageRepository.findByLangPageId(lang + "_" + pageId); if(page == null){ LOG.error("Page with pageId {} and lang {} not found.", pageId, lang); throw new PageNotFoundException(); } return page; } /** * Get the page with the given langePageId * @param langPageId the langPageId of the page * @return the requested page * @throws PageNotFoundException */ public Page getPage(String langPageId) throws PageNotFoundException{ Page page = pageRepository.findByLangPageId(langPageId); if(page == null){ LOG.error("Page with langPageId: {} not found.", langPageId); throw new PageNotFoundException(); } return page; } /** * Add a new revision to a page. It links the Page to the new revision via * LAST_REVISION link. Moreover it create the PREVIOUS_REVISION link. * @param langPageId * @param rev */ public void addRevisionToPage(String langPageId, Revision rev) throws PageNotFoundException{ Page page = pageRepository.findByLangPageId(langPageId); if(page == null){ throw new PageNotFoundException(); } // Add PREVIOUS_REVISION relationship rev.setPreviousRevision(page.getLastRevision()); page.setLastRevision(rev); // The changes on the revision will be automatically persisted pageRepository.save(page); } /** * Change title of a page. The method is prefixed by move to follow MediaWiki naming. * @param oldTitle the old title of the page * @param newTitle the new title to set * @param lang the language of the page * @return the updated page * @throws PageNotFoundException */ public Page movePage(String oldTitle, String newTitle, String lang) throws PageNotFoundException{ Page page = pageRepository.findByTitleAndLang(oldTitle, lang); if(page == null){ throw new PageNotFoundException(); } page.setTitle(newTitle); pageRepository.save(page); return page; } /** * Delete a page from the graph given its title and domain language. * @param title the title of the page * @param lang the language of the page * @throws PageNotFoundException */ public void deletePage(String title, String lang) throws PageNotFoundException{ Page page = pageRepository.findByTitleAndLang(title, lang); if(page == null){ throw new PageNotFoundException(); } // Delete the revisions of the page revisionService.deleteRevisionsOfPage(page.getLangPageId()); // Delete finally the page itself pageRepository.delete(page); } /** * Get the pages labeled by :CourseRoot label * @return the list of course root pages */ public List getCourseRootPages(String lang){ return pageRepository.findAllCourseRootPages(lang); } /** * Get the page labeled only by :Page label * @return the list of the uncategorized pages */ public List getUncategorizedPages(String lang){ return pageRepository.findAllUncategorizedPages(lang); } /** * Initialize for the first time the course structure using labels and relationships * @param lang the language of the domain * @param apiUrl the MediaWiki API url */ @Async public CompletableFuture initCourseStructure(String lang, String apiUrl){ List pages = pageRepository.findAllByLang(lang); // Remove all pages that are not course root pages Predicate pagePredicate = page -> page.getTitle().contains("/"); pages.removeIf(pagePredicate); /*for(Page p : pages){ // Get course tree and prepare relationship sets CourseTree tree = pageMediaWikiService.getCourseTree(apiUrl, p.getTitle()); Set levelsTwo = new HashSet<>(); int index = 0; for(String levelTwo : tree.getLevelsTwo()){ Set levelsThree = new HashSet<>(); // Add CourseLEvelTwo label and add levels two to the set to be saved String levelTwoTitle = (tree.getRoot() + "/" + levelTwo).trim(); Page levelTwoPage = pageRepository.findByTitleAndLang(levelTwoTitle, lang); // Skip malformed page if(levelTwoPage == null) continue; levelTwoPage.addLabel("CourseLevelTwo"); levelsTwo.add(levelTwoPage); // Add CourseLevelThree labels and add levels three to the set to be saved for(String levelThree : tree.getLevelsTree().get(index)){ String levelThreeTitle = (levelTwoTitle + "/" + levelThree).trim(); Page levelThreePage = pageRepository.findByTitleAndLang(levelThreeTitle, lang); // Skip malformed page if(levelThreePage == null) continue; levelThreePage.addLabel("CourseLevelThree"); levelsThree.add(levelThreePage); } // Set LEVEL_THREE relationships levelTwoPage.setLevelsThree(levelsThree); pageRepository.save(levelsThree); index++; } // Set LEVEL_TWO relationships and CourseRoot label p.addLabel("CourseRoot"); p.setLevelsTwo(levelsTwo); pageRepository.save(levelsTwo); pageRepository.save(p); }*/ applyCourseStructure(lang, apiUrl, pages); return CompletableFuture.completedFuture(true); } /** * * @param lang the language of the domain * @param apiUrl the MediaWiki API url * @return */ public CompletableFuture updateCourseStructure(String lang, String apiUrl) { List courseRootPages = getCourseRootPages(lang); List uncategorizedPages = getUncategorizedPages(lang); // Remove all uncategorized pages that are not course root pages Predicate pagePredicate = page -> page.getTitle().contains("/"); uncategorizedPages.removeIf(pagePredicate); courseRootPages.addAll(uncategorizedPages); applyCourseStructure(lang, apiUrl, courseRootPages); return CompletableFuture.completedFuture(true); } /** * @param lang the language of the domain * @param apiUrl the MediaWiki API url * @param courseRootPages */ private void applyCourseStructure(String lang, String apiUrl, List courseRootPages) { for (Page p : courseRootPages) { // Get course tree and prepare relationship set CourseTree tree = pageMediaWikiService.getCourseTree(apiUrl, p.getTitle()); Set levelsTwo = (p.getLevelsTwo() == null) ? new HashSet<>() : p.getLevelsTwo(); int index = 0; for (String levelTwo : tree.getLevelsTwo()) { String levelTwoTitle = (tree.getRoot() + "/" + levelTwo).trim(); Page levelTwoPage = pageRepository.findByTitleAndLang(levelTwoTitle, lang); // Skip malformed page if (levelTwoPage == null) continue; // Add CourseLevelTwo label and add levels two to the set to be saved if(!levelsTwo.contains(levelTwoPage)){ levelTwoPage.addLabel("CourseLevelTwo"); levelsTwo.add(levelTwoPage); } Set levelsThree = (levelTwoPage.getLevelsThree() == null) ? new HashSet<>() : levelTwoPage.getLevelsThree(); // Add CourseLevelThree labels and add levels three to the set to be saved for (String levelThree : tree.getLevelsTree().get(index)) { String levelThreeTitle = (levelTwoTitle + "/" + levelThree).trim(); Page levelThreePage = pageRepository.findByTitleAndLang(levelThreeTitle, lang); // Skip malformed page if (levelThreePage == null) continue; if(!levelsThree.contains(levelThreePage)){ levelThreePage.addLabel("CourseLevelThree"); levelsThree.add(levelThreePage); } } // Set LEVEL_THREE relationships levelTwoPage.setLevelsThree(levelsThree); pageRepository.save(levelsThree); index++; } // Set LEVEL_TWO relationships and CourseRoot label p.addLabel("CourseRoot"); p.setLevelsTwo(levelsTwo); pageRepository.save(levelsTwo); pageRepository.save(p); } } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java b/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java index 3d259f6..bff2310 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java @@ -1,197 +1,209 @@ /** * */ package org.wikitolearn.wikirating.service; import java.util.Date; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.wikitolearn.wikirating.exception.RevisionNotFoundException; import org.wikitolearn.wikirating.model.Page; import org.wikitolearn.wikirating.model.Revision; import org.wikitolearn.wikirating.repository.PageRepository; import org.wikitolearn.wikirating.repository.RevisionRepository; import org.wikitolearn.wikirating.service.mediawiki.RevisionMediaWikiService; import static java.lang.Math.exp; /** * * @author aletundo, valsdav * */ @Service public class RevisionService { private static final Logger LOG = LoggerFactory.getLogger(RevisionService.class); @Autowired private RevisionMediaWikiService revisionMediaWikiService; @Autowired private RevisionRepository revisionRepository; @Autowired private PageRepository pageRepository; /** * Initialize the revisions for the first time querying the MediaWiki API. * This method adds the revisions and sets the FIRST_REVISION, * LAST_REVISION and PREVIOUS_REVISION relationships. * @param lang the domain language * @param apiUrl the MediaWiki API url * @return CompletableFuture */ @Async public CompletableFuture initRevisions(String lang, String apiUrl) { List pages = pageRepository.findAllByLang(lang); for(Page page : pages){ List revisions = revisionMediaWikiService.getAllRevisionByPageId(apiUrl, page.getPageId()); // Set the first and the last revisions for the current page page.setFistRevision(revisions.get(0)); page.setLastRevision(revisions.get(revisions.size() - 1)); ListIterator it = revisions.listIterator(); while(it.hasNext()){ Revision rev = it.next(); rev.setLangRevId(lang + "_" + rev.getRevId()); rev.setLang(lang); if (it.previousIndex() != 0) { rev.setPreviousRevision(revisions.get(it.previousIndex() - 1)); } } // Saving all the revisions node and the page node revisionRepository.save(revisions); pageRepository.save(page); LOG.info("Inserted revisions for page {}", page.getLangPageId()); } return CompletableFuture.completedFuture(true); } @Async public CompletableFuture calculateChangeCoefficientAllRevisions(String lang, String apiUrl){ LOG.info("Calculating changeCoefficient for all the revisions..."); Set revisions = revisionRepository.findByLang(lang); for (Revision rev : revisions){ double changeCoefficient = calculateChangeCoefficient(apiUrl, rev); rev.setChangeCoefficient(changeCoefficient); } revisionRepository.save(revisions); LOG.info("ChangeCoefficient calculated for all revisions."); return CompletableFuture.completedFuture(true); } /** * Add a new Revision to the graph * @param revid * @param lang * @param userid * @param parentid * @param length * @param timestamp * @return */ public Revision addRevision(int revid, String lang, int userid, int parentid, int length, Date timestamp){ Revision rev = new Revision(revid, lang, userid, parentid, length, timestamp); revisionRepository.save(rev); return rev; } /** * Delete a revision given its langPageId * @param langPageId the langPageId of the revision */ public void deleteRevisionsOfPage(String langPageId) throws RevisionNotFoundException{ Set revisions = revisionRepository.findAllRevisionOfPage(langPageId); if (revisions.size() == 0){ LOG.error("Revisions of page {} not found", langPageId); throw new RevisionNotFoundException("Revisions of page "+langPageId+" not found"); } revisionRepository.delete(revisions); } public Set getRevisionsOfPage(String langPageId) throws RevisionNotFoundException{ Set revisions = revisionRepository.findAllRevisionOfPage(langPageId); if (revisions.size() == 0){ LOG.error("Revisions of page {} not found", langPageId); throw new RevisionNotFoundException("Revisions of page "+langPageId+" not found"); } return revisions; } /** * Get the requested revision * @param langRevId the langRevId of the revision * @return the revision * @throws RevisionNotFoundException */ public Revision getRevision(String langRevId) throws RevisionNotFoundException{ Revision revision = revisionRepository.findByLangRevId(langRevId); if(revision == null){ LOG.error("Revision {} not found", langRevId); throw new RevisionNotFoundException(); } return revision; } /** * Update the given revision * @param revision * @return the updated revision */ public Revision updateRevision(Revision revision){ revisionRepository.save(revision); return revision; } - /** + /** + * Calculate and set the changeCoefficient of a Revision. + * This method also persist the Revision changes. + * @param apiUrl + * @param revision + */ + public void setChangeCoefficient(String apiUrl, Revision revision){ + double cc = calculateChangeCoefficient(apiUrl, revision); + revision.setChangeCoefficient(cc); + revisionRepository.save(revision); + } + + /** * Calculate the changeCoefficient for a given Revision querying mediawiki * and returns the number. It doesn't store it in the Revision. * @param revision * @return */ public double calculateChangeCoefficient(String apiUrl, Revision revision){ - double previousLength = 0; - double changeCoefficient = 0; + double previousLength = 0.0; + double changeCoefficient = 0.0; // Get the previous Revision Revision previousRevision = revision.getPreviousRevision(); if (previousRevision == null){ - previousLength = 1; - changeCoefficient = 0; + previousLength = 1.0; + changeCoefficient = 0.0; LOG.info("Change coefficient of revision {} (first-rev): {}", revision.getLangRevId(), changeCoefficient); } else{ double prevL = (double) previousRevision.getLength(); // Suppose the mean line length of 120 characters and that the length is in bytes. // We want a "lenght" in n° of lines if (prevL == 0){ - previousLength = 1; + previousLength = 1.0; }else { previousLength = (prevL < 120) ? 1 : prevL / 120; } // Query mediawiki for diff text String diffText = revisionMediaWikiService.getDiffPreviousRevision(apiUrl, previousRevision.getRevId(), revision.getRevId()); int addedLines = StringUtils.countMatches(diffText, "diff-addedline"); int deletedLines = StringUtils.countMatches(diffText, "diff-deletedline"); int inlineChanges = StringUtils.countMatches(diffText, "diffchange-inline"); // Finally calculation of change Coefficient double t = ((1.2 * deletedLines + addedLines) ) / previousLength; changeCoefficient = 1 / exp(0.7 * t); LOG.info("Change coefficient of revision {} (+{}-{}/{}): {}", revision.getLangRevId(), addedLines, deletedLines, previousLength, changeCoefficient); } return changeCoefficient; } }