diff --git a/src/main/java/org/wikitolearn/wikirating/WikiRatingApplication.java b/src/main/java/org/wikitolearn/wikirating/WikiRatingApplication.java index 7f7aed2..0b1ba13 100644 --- a/src/main/java/org/wikitolearn/wikirating/WikiRatingApplication.java +++ b/src/main/java/org/wikitolearn/wikirating/WikiRatingApplication.java @@ -1,41 +1,41 @@ package org.wikitolearn.wikirating; import java.util.concurrent.Executor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; import org.springframework.scheduling.annotation.AsyncConfigurerSupport; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * * @author aletundo * */ @SpringBootApplication @EnableAsync @EnableScheduling -@EntityScan("org.wikitolearn.wikirating.model") +@EntityScan("org.wikitolearn.wikirating.model.graph") @EnableNeo4jRepositories(basePackages = "org.wikitolearn.wikirating.repository") @EnableTransactionManagement public class WikiRatingApplication extends AsyncConfigurerSupport{ public static void main(String[] args) { SpringApplication.run(WikiRatingApplication.class, args); } @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setThreadNamePrefix("ServicesThread-"); executor.initialize(); return executor; } } diff --git a/src/main/java/org/wikitolearn/wikirating/controller/PageController.java b/src/main/java/org/wikitolearn/wikirating/controller/PageController.java index 16ef593..2e9e108 100644 --- a/src/main/java/org/wikitolearn/wikirating/controller/PageController.java +++ b/src/main/java/org/wikitolearn/wikirating/controller/PageController.java @@ -1,145 +1,159 @@ /** * */ package org.wikitolearn.wikirating.controller; +import java.time.Instant; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import org.wikitolearn.wikirating.exception.GenericException; import org.wikitolearn.wikirating.exception.PageNotFoundException; import org.wikitolearn.wikirating.exception.RevisionNotFoundException; -import org.wikitolearn.wikirating.model.Page; -import org.wikitolearn.wikirating.model.Revision; -import org.wikitolearn.wikirating.model.TemporaryVote; -import org.wikitolearn.wikirating.model.Vote; +import org.wikitolearn.wikirating.model.api.ApiResponseSuccess; +import org.wikitolearn.wikirating.model.graph.Page; +import org.wikitolearn.wikirating.model.graph.Revision; +import org.wikitolearn.wikirating.model.graph.TemporaryVote; +import org.wikitolearn.wikirating.model.graph.Vote; import org.wikitolearn.wikirating.service.PageService; import org.wikitolearn.wikirating.service.RevisionService; import org.wikitolearn.wikirating.service.VoteService; /** - * @author aletundo RESTController for pages resources. It handles all the - * requests to /pages. + * RESTController for pages resources. It handles all the requests to {lang}/pages + * @author aletundo */ @RestController @RequestMapping("{lang}/pages/") public class PageController { private static final Logger LOG = LoggerFactory.getLogger(PageController.class); @Autowired private PageService pageService; @Autowired private RevisionService revisionService; @Autowired private VoteService voteService; /** * Handle GET requests on {lang}/pages/{pageId} URI. The last revision of the page is returned. * * @param pageId * int The id of the page - * @return response Revision + * @return the last revision of the requested page + * @throws PageNotFoundException */ @RequestMapping(value = "{pageId}", method = RequestMethod.GET, produces = "application/json") + @ResponseStatus(HttpStatus.OK) @ResponseBody - public Revision getLastRevisionByPageId(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId) { + public ApiResponseSuccess getLastRevisionByPageId(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId) throws PageNotFoundException{ try{ Page page = pageService.getPage(pageId, lang); - return page.getLastRevision(); + ApiResponseSuccess body = new ApiResponseSuccess(page.getLastRevision(), Instant.now().toEpochMilli()); + return body; }catch(PageNotFoundException e){ LOG.error("Impossible to get the last revision of {}_{}: {}",lang, pageId, e.getMessage()); - throw new GenericException(e.getMessage()); + throw e; } } /** * Handle GET requests on {lang}/pages/{pageId}/revisions URI. All the revision for the requested page are returned. * * @param pageId * int The id of the page - * @return response The list with all the revisions + * @return a list with all revisions of the requested page */ @RequestMapping(value = "{pageId}/revisions", method = RequestMethod.GET, produces = "application/json") + @ResponseStatus(HttpStatus.OK) @ResponseBody - public Set getAllPageRevisions(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId) { + public ApiResponseSuccess getAllPageRevisions(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId) { String langPageId = lang + "_" + pageId; try{ Set revisions = revisionService.getRevisionsOfPage(langPageId); - return revisions; + ApiResponseSuccess body = new ApiResponseSuccess(revisions, Instant.now().toEpochMilli()); + return body; }catch(RevisionNotFoundException e){ LOG.error("Impossible to get revisions of page {}", langPageId); - throw new GenericException(e.getMessage()); + throw e; } } /** * Handle GET requests on {lang}/pages/{pageId}/revisions/{revId} URI. The requested page revision is returned. * * @param pageId * int The id of the page * @param revId * int The id of the revision - * @return response Revision + * @return the requested revision of the page */ @RequestMapping(value = "{pageId}/revisions/{revId}", method = RequestMethod.GET, produces = "application/json") + @ResponseStatus(HttpStatus.OK) @ResponseBody - public Revision getRevisionById(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId, @PathVariable("revId") int revId) { + public ApiResponseSuccess getRevisionById(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId, @PathVariable("revId") int revId) { String langRevId = lang + "_" + revId; try{ Revision revision = revisionService.getRevision(langRevId); - return revision; + ApiResponseSuccess body = new ApiResponseSuccess(revision, Instant.now().toEpochMilli()); + return body; }catch(RevisionNotFoundException e){ String langPageId = lang + "_" + pageId; LOG.error("Impossible to get revision {} of page {}", langRevId, langPageId); - throw new GenericException(e.getMessage()); + throw e; } } /** * Handle GET requests on {lang}/pages/{pageId}/revisions/{revId}/votes URI. All * the votes for the requested page revision are returned. * * @param pageId * int The id of the page * @param revId * int The id of the revision - * @return response Vote + * @return all the votes of the requested revision */ @RequestMapping(value = "{pageId}/revisions/{revId}/votes", method = RequestMethod.GET, produces = "application/json") + @ResponseStatus(HttpStatus.OK) @ResponseBody - public List showVotes(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId, @PathVariable("revId") int revId) { + public ApiResponseSuccess showVotes(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId, @PathVariable("revId") int revId) { // TODO: Work in progress String langRevId = lang + "_" + revId; - return voteService.getAllVotesOfRevision(langRevId); + List votes = voteService.getAllVotesOfRevision(langRevId); + ApiResponseSuccess body = new ApiResponseSuccess(votes, Instant.now().toEpochMilli()); + return body; } /** * Handle POST requests on {lang}/pages/{pageId}/revisions/{revId}/votes URI. It * expects vote and userId as request parameters. * * @param pageId * int The id of the page * @param revId * int The id of the revision * @param vote * double The value of the vote * @param userId * int The id of the user who submit the request * @return response Vote */ @RequestMapping(value = "{pageId}/revisions/{revId}/votes", method = RequestMethod.POST, produces = "application/json") + @ResponseStatus(HttpStatus.CREATED) @ResponseBody public TemporaryVote addVote(@PathVariable("lang") String lang, @PathVariable("pageId") int pageId, @PathVariable("revId") int revId, @RequestParam("vote") double vote, @RequestParam("userId") int userId) { //TODO return null; } } diff --git a/src/main/java/org/wikitolearn/wikirating/exception/error/ApiError.java b/src/main/java/org/wikitolearn/wikirating/exception/error/ApiError.java deleted file mode 100644 index 6fcad68..0000000 --- a/src/main/java/org/wikitolearn/wikirating/exception/error/ApiError.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * - */ -package org.wikitolearn.wikirating.exception.error; - -import java.util.List; - -import org.springframework.http.HttpStatus; - -/** - * @author aletundo - * - */ -public class ApiError { - private long timestamp; - private HttpStatus status; - private String message; - private List errors; - private String exception; - - /** - * - */ - public ApiError() {} - - /** - * @param timestamp - * @param status - * @param message - * @param errors - * @param exception - */ - public ApiError(long timestamp, HttpStatus status, String message, List errors, String exception) { - this.timestamp = timestamp; - this.status = status; - this.message = message; - this.errors = errors; - this.exception = exception; - } - - /** - * @return the timestamp - */ - public long getTimestamp() { - return timestamp; - } - - /** - * @param timestamp the timestamp to set - */ - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; - } - - /** - * @return the status - */ - public HttpStatus getStatus() { - return status; - } - - /** - * @param status the status to set - */ - public void setStatus(HttpStatus status) { - this.status = status; - } - - /** - * @return the message - */ - public String getMessage() { - return message; - } - - /** - * @param message the message to set - */ - public void setMessage(String message) { - this.message = message; - } - - /** - * @return the errors - */ - public List getErrors() { - return errors; - } - - /** - * @param errors the errors to set - */ - public void setErrors(List errors) { - this.errors = errors; - } - - /** - * @return the exception - */ - public String getException() { - return exception; - } - - /** - * @param exception the exception to set - */ - public void setException(String exception) { - this.exception = exception; - } -} diff --git a/src/main/java/org/wikitolearn/wikirating/exception/handler/GlobalExceptionHandler.java b/src/main/java/org/wikitolearn/wikirating/exception/handler/GlobalExceptionHandler.java index d2889b3..90abeae 100644 --- a/src/main/java/org/wikitolearn/wikirating/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/org/wikitolearn/wikirating/exception/handler/GlobalExceptionHandler.java @@ -1,36 +1,68 @@ /** * */ package org.wikitolearn.wikirating.exception.handler; -import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import java.time.Instant; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; import org.wikitolearn.wikirating.exception.GenericException; -import org.wikitolearn.wikirating.exception.error.ApiError; +import org.wikitolearn.wikirating.exception.PageNotFoundException; +import org.wikitolearn.wikirating.exception.RevisionNotFoundException; +import org.wikitolearn.wikirating.model.api.ApiResponseError; /** * @author aletundo * */ -@ControllerAdvice -public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { +@RestControllerAdvice +public class GlobalExceptionHandler{ + @ExceptionHandler(value = { GenericException.class }) - public ResponseEntity handleGenericException(GenericException exception, WebRequest request) { + @ResponseStatus(HttpStatus.NOT_FOUND) + public ApiResponseError handleGenericException(GenericException exception) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); + ApiResponseError body = buildResponseBody(exception); + return body; + } + + @ExceptionHandler(value = { PageNotFoundException.class }) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ApiResponseError handlePageNotFoundException(PageNotFoundException exception) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ApiResponseError body = buildResponseBody(exception); + return body; + } + + @ExceptionHandler(value = { RevisionNotFoundException.class }) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ApiResponseError handleRevisionNotFoundException(RevisionNotFoundException exception) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ApiResponseError body = buildResponseBody(exception); + return body; + } + + /** + * + * @param exception + * @return + */ + private ApiResponseError buildResponseBody(Exception exception){ + Map data = new HashMap<>(); + data.put("error", HttpStatus.NOT_FOUND.name()); + data.put("exception", exception.getClass().getCanonicalName()); + data.put("stacktrace", exception.getStackTrace()); - ApiError apiError = new ApiError(Instant.now().toEpochMilli(), HttpStatus.BAD_REQUEST, exception.getMessage(), - new ArrayList(), exception.getClass().getCanonicalName()); - - return handleExceptionInternal(exception, apiError, headers, apiError.getStatus(), request); + return new ApiResponseError(data, exception.getMessage(), HttpStatus.NOT_FOUND.value(), Instant.now().toEpochMilli()); } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponse.java b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponse.java new file mode 100644 index 0000000..3a38c2e --- /dev/null +++ b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponse.java @@ -0,0 +1,63 @@ +/** + * + */ +package org.wikitolearn.wikirating.model.api; + +/** + * @author aletundo + * + */ +public class ApiResponse { + private String status; + private Object data; + private long timestamp; + + /** + * @param status + * @param data + */ + public ApiResponse(String status, Object data, long timestamp) { + this.status = status; + this.data = data; + this.timestamp = timestamp; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + /** + * @param status the status to set + */ + public void setStatus(String status) { + this.status = status; + } + /** + * @return the data + */ + public Object getData() { + return data; + } + /** + * @param data the data to set + */ + public void setData(Object data) { + this.data = data; + } + + /** + * @return the timestamp + */ + public long getTimestamp() { + return timestamp; + } + + /** + * @param timestamp the timestamp to set + */ + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } +} diff --git a/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseError.java b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseError.java new file mode 100644 index 0000000..8deb3f1 --- /dev/null +++ b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseError.java @@ -0,0 +1,51 @@ +/** + * + */ +package org.wikitolearn.wikirating.model.api; + +/** + * @author aletundo + * + */ +public class ApiResponseError extends ApiResponse{ + private String message; + private int code; + + /** + * + */ + public ApiResponseError(Object data, String message, int code, long timestamp) { + super("error", data, timestamp); + this.code = code; + this.message = message; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * @return the code + */ + public int getCode() { + return code; + } + + /** + * @param code the code to set + */ + public void setCode(int code) { + this.code = code; + } + +} diff --git a/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseSuccess.java b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseSuccess.java new file mode 100644 index 0000000..c7275e9 --- /dev/null +++ b/src/main/java/org/wikitolearn/wikirating/model/api/ApiResponseSuccess.java @@ -0,0 +1,17 @@ +/** + * + */ +package org.wikitolearn.wikirating.model.api; + +/** + * @author aletundo + * + */ +public class ApiResponseSuccess extends ApiResponse { + /** + * + */ + public ApiResponseSuccess(Object data, long timestamp) { + super("success", data, timestamp); + } +} diff --git a/src/main/java/org/wikitolearn/wikirating/model/Author.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Author.java similarity index 97% rename from src/main/java/org/wikitolearn/wikirating/model/Author.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Author.java index 0333678..68b268f 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Author.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Author.java @@ -1,101 +1,101 @@ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.EndNode; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.RelationshipEntity; import org.neo4j.ogm.annotation.StartNode; import com.fasterxml.jackson.annotation.JsonIgnore; /** * This entity represent the edit of a user on a Page. * It stores the the reliability of the user at the moment * of the creation. */ @RelationshipEntity(type="Author") public class Author { @GraphId @JsonIgnore private Long graphId; private double reliability; @StartNode private User user; @EndNode private Revision revision; /** * */ public Author() {} /** * @param reliability */ public Author(double reliability) { this.reliability = reliability; } /** * @return the graphId */ public Long getGraphId() { return graphId; } /** * @param graphId the graphId to set */ public void setGraphId(Long graphId) { this.graphId = graphId; } /** * @return the reliability */ public double getReliability() { return reliability; } /** * @param reliability the reliability to set */ public void setReliability(double reliability) { this.reliability = reliability; } /** * @return the user */ public User getUser() { return user; } /** * @param user the user to set */ public void setUser(User user) { this.user = user; } /** * @return the revision */ public Revision getRevision() { return revision; } /** * @param revision the revision to set */ public void setRevision(Revision revision) { this.revision = revision; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Author [graphId=" + graphId + ", reliability=" + reliability + ", user=" + user + ", revision=" + revision + "]"; } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/Metadata.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Metadata.java similarity index 96% rename from src/main/java/org/wikitolearn/wikirating/model/Metadata.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Metadata.java index 35a3af2..7ed72d9 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Metadata.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Metadata.java @@ -1,55 +1,55 @@ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.Relationship; import org.wikitolearn.wikirating.util.enums.MetadataType; import com.fasterxml.jackson.annotation.JsonIgnore; /** * This entity represents the root of the chain of processes. * It is used also to store some global stats. * Created by valsdav on 24/03/17. */ @NodeEntity(label="Metadata") public class Metadata { @GraphId @JsonIgnore private Long id; @Relationship(type = "LATEST_PROCESS", direction = Relationship.OUTGOING) private Process latestProcess; private MetadataType type; public Metadata() { } public Metadata(MetadataType type){ this.type = type; } public Process getLatestProcess() { return latestProcess; } public void setLatestProcess(Process latestProcess) { this.latestProcess = latestProcess; } public MetadataType getType() { return type; } public void setType(MetadataType type) { this.type = type; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Metadata [id=" + id + ", latestProcess=" + latestProcess + ", type=" + type + "]"; } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/Page.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Page.java similarity index 99% rename from src/main/java/org/wikitolearn/wikirating/model/Page.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Page.java index b38d056..90ad59e 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Page.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Page.java @@ -1,279 +1,279 @@ /** * */ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import java.util.HashSet; import java.util.Set; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.Labels; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.Relationship; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; /** * @author aletundo * */ @NodeEntity( label = "Page") public class Page { @GraphId @JsonIgnore private Long graphId; @JsonProperty("pageId") private int pageId; @Index private String title; @Index private String lang; @Index(unique = true, primary = true) @JsonProperty(access = Access.WRITE_ONLY) private String langPageId; private double pageRank; @Relationship(type = "LAST_REVISION", direction = Relationship.OUTGOING) private Revision lastRevision; @Relationship(type = "FIRST_REVISION", direction = Relationship.OUTGOING) private Revision fistRevision; @Relationship(type = "LEVEL_TWO", direction = Relationship.OUTGOING) private Set levelsTwo; @Relationship(type = "LEVEL_THREE", direction = Relationship.OUTGOING) private Set levelsThree; @Labels private Set labels = new HashSet<>(); /** * */ public Page() {} /** * @param pageId * @param title * @param lang */ public Page(int pageId, String title, String lang, String langPageId) { this.pageId = pageId; this.title = title; this.lang = lang; this.langPageId = langPageId; } /** * @return the graphId */ public Long getGraphId() { return graphId; } /** * @param graphId the graphId to set */ public void setGraphId(Long graphId) { this.graphId = graphId; } /** * @return the pageId */ public int getPageId() { return pageId; } /** * @param pageId the pageId to set */ public void setPageid(int pageId) { this.pageId = pageId; } /** * @return the title */ public String getTitle() { return title; } /** * @param title the title to set */ public void setTitle(String title) { this.title = title; } /** * @return the lang */ public String getLang() { return lang; } /** * @param lang the lang to set */ public void setLang(String lang) { this.lang = lang; } /** * @return the langPageId */ public String getLangPageId() { return langPageId; } /** * @param langPageId the langPageId to set */ public void setLangPageId(String langPageId) { this.langPageId = langPageId; } /** * @return the pageRank */ public double getPageRank() { return pageRank; } /** * @param pageRank the pageRank to set */ public void setPageRank(double pageRank) { this.pageRank = pageRank; } /** * @return the lastRevision */ public Revision getLastRevision() { return lastRevision; } /** * @param lastRevision the lastRevision to set */ public void setLastRevision(Revision lastRevision) { this.lastRevision = lastRevision; } /** * @return the fistRevision */ public Revision getFistRevision() { return fistRevision; } /** * @param fistRevision the fistRevision to set */ public void setFistRevision(Revision fistRevision) { this.fistRevision = fistRevision; } /** * @return the levelsTwo */ public Set getLevelsTwo() { return levelsTwo; } /** * @param levelsTwo the levelsTwo to set */ public void setLevelsTwo(Set levelsTwo) { this.levelsTwo = levelsTwo; } /** * @param levelTwo the levelTwo to add */ public void addLevelTwo(Page levelTwo){ this.levelsTwo.add(levelTwo); } /** * @return the levelsThree */ public Set getLevelsThree() { return levelsThree; } /** * @param levelsThree the levelsThree to set */ public void setLevelsThree(Set levelsThree) { this.levelsThree = levelsThree; } /** * @param levelThree the levelThree to add */ public void addLevelThree(Page levelThree){ this.levelsThree.add(levelThree); } /** * @return the labels */ public Set getLabels() { return labels; } /** * @param labels the labels to set */ public void setLabels(Set labels) { this.labels = labels; } /** * @param label the label to set */ public void addLabel(String label) { this.labels.add(label); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Page [graphId=" + graphId + ", pageId=" + pageId + ", title=" + title + ", lang=" + lang + ", langPageId=" + langPageId + ", pageRank=" + pageRank + ", lastRevision=" + lastRevision + ", fistRevision=" + fistRevision + ", levelsTwo=" + levelsTwo + ", levelsThree=" + levelsThree + ", labels=" + labels + "]"; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Page)) { return false; } Page other = (Page) obj; if (langPageId == null) { if (other.langPageId != null) { return false; } } else if (!langPageId.equals(other.langPageId)) { return false; } return true; } } \ No newline at end of file diff --git a/src/main/java/org/wikitolearn/wikirating/model/Process.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Process.java similarity index 98% rename from src/main/java/org/wikitolearn/wikirating/model/Process.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Process.java index 3aec74c..7960728 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Process.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Process.java @@ -1,107 +1,107 @@ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.Relationship; import org.neo4j.ogm.annotation.typeconversion.DateLong; import org.wikitolearn.wikirating.util.enums.ProcessStatus; import org.wikitolearn.wikirating.util.enums.ProcessType; import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.Date; import java.util.UUID; /** * This class represents a Process happened in the engine. * It has a definite type and can have different results. It is saved in the DB * as a Process vertex. * @author aletundo * @author valsdav */ @NodeEntity( label = "Process") public class Process { @JsonIgnore @GraphId private Long graphId; @Index(unique = true, primary = true) private String processId; private ProcessType processType; private ProcessStatus processStatus; @Relationship(type="PREVIOUS_PROCESS", direction = Relationship.OUTGOING) private Process previousProcess; @DateLong private Date startOfProcess; @DateLong private Date endOfProcess; public Process() {} public Process(String processId, ProcessType processType, ProcessStatus processStatus, Date startOfProcess, Date endOfProcess) { this.processId = processId; this.processType = processType; this.processStatus = processStatus; this.startOfProcess = startOfProcess; this.endOfProcess = endOfProcess; } public Process(ProcessType processType){ this.processId = UUID.randomUUID().toString(); this.processType = processType; this.processStatus = ProcessStatus.ONGOING; this.startOfProcess = new Date(); } public ProcessType getProcessType() { return processType; } public void setProcessType(ProcessType processType) { this.processType = processType; } public ProcessStatus getProcessStatus() { return processStatus; } public void setProcessStatus(ProcessStatus processStatus) { this.processStatus = processStatus; } public Process getPreviousProcess() { return previousProcess; } public void setPreviousProcess(Process previousProcess) { this.previousProcess = previousProcess; } public Date getStartOfProcess() { return startOfProcess; } public void setStartOfProcess(Date startOfProcess) { this.startOfProcess = startOfProcess; } public Date getEndOfProcess() { return endOfProcess; } public void setEndOfProcess(Date endOfProcess) { this.endOfProcess = endOfProcess; } @Override public String toString() { return "Process[" + "processId='" + processId + '\'' + ", processType=" + processType + ", processStatus=" + processStatus + ", startOfProcess=" + startOfProcess + ", endOfProcess=" + endOfProcess + ']'; } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/Revision.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java similarity index 99% rename from src/main/java/org/wikitolearn/wikirating/model/Revision.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java index 2a846dd..29e7546 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Revision.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Revision.java @@ -1,374 +1,374 @@ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.NodeEntity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; 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 @JsonIgnore private Long graphId; @JsonProperty("revid") private int revId; @JsonProperty(access = Access.WRITE_ONLY) private String lang; @JsonProperty(value = "userid", access = Access.WRITE_ONLY) private int userId; @JsonProperty(value = "parentid", access = Access.WRITE_ONLY) private int parentId; @DateLong @JsonProperty(access = Access.WRITE_ONLY) private Date timestamp; @JsonProperty(value = "size", access = Access.WRITE_ONLY) private long length; @JsonProperty(access = Access.WRITE_ONLY) private double changeCoefficient; private double currentMeanVote; private double currentVotesReliability; private double currentNormalisesVotesReliability; private double totalMeanVote; private double totalVotesReliability; private double totalNormalisesVotesReliability; @JsonProperty(access = Access.WRITE_ONLY) private boolean validated; @Index(unique = true, primary=true) @JsonProperty(access = Access.WRITE_ONLY) 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; } /** * @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/model/TemporaryVote.java b/src/main/java/org/wikitolearn/wikirating/model/graph/TemporaryVote.java similarity index 97% rename from src/main/java/org/wikitolearn/wikirating/model/TemporaryVote.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/TemporaryVote.java index 0f47548..c207a40 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/TemporaryVote.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/TemporaryVote.java @@ -1,88 +1,88 @@ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.typeconversion.DateLong; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; /** * This entity represents a Vote that has to be validating, * connecting the user and the Revision after a fetch of the mediawiki api. * @author valsadav * @author aletundo */ public class TemporaryVote { @GraphId @JsonIgnore private Long graphId; private double value; private double reliability; @JsonProperty("userid") private int userId; @JsonProperty("revid") private int revId; private String langRevId; @DateLong private Date timestamp; public TemporaryVote(double value, double reliability, int userId, int revId, String langRevId, Date timestamp) { this.value = value; this.reliability = reliability; this.userId = userId; this.revId = revId; this.langRevId = langRevId; this.timestamp = timestamp; } public double getValue() { return value; } public void setValue(double value) { this.value = value; } public double getReliability() { return reliability; } public void setReliability(double reliability) { this.reliability = reliability; } public int getUserId() { return userId; } public void setUserid(int userId) { this.userId = userId; } public int getRevId() { return revId; } public void setRevId(int revId) { this.revId = revId; } public String getLangRevId() { return langRevId; } public void setLangRevId(String langRevId) { this.langRevId = langRevId; } public Date getTimestamp() { return timestamp; } public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/User.java b/src/main/java/org/wikitolearn/wikirating/model/graph/User.java similarity index 98% rename from src/main/java/org/wikitolearn/wikirating/model/User.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/User.java index 0264969..96ea0ba 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/User.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/User.java @@ -1,186 +1,186 @@ /** * */ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.GraphId; import org.neo4j.ogm.annotation.Index; import org.neo4j.ogm.annotation.NodeEntity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import org.neo4j.ogm.annotation.Relationship; import java.util.HashSet; import java.util.Set; /** * @author aletundo, valsdav * */ @NodeEntity( label = "User") @JsonIgnoreProperties(ignoreUnknown = true) public class User { @GraphId @JsonIgnore private Long graphId; @JsonProperty("name") private String username; @Index(unique=true,primary = true) @JsonProperty("userid") private int userId; private double votesReliability; private double contributesReliability; private double totalReliability; @Relationship( type="AUTHOR", direction = Relationship.OUTGOING) private Set authorship; @Relationship( type="VOTE", direction = Relationship.OUTGOING) private Set votes; /** * */ public User() { this.authorship = new HashSet<>(); this.votes = new HashSet<>(); } /** * @param username * @param userId * @param votesReliability * @param contributesReliability * @param totalReliability */ public User(String username, int userId, double votesReliability, double contributesReliability, double totalReliability) { this.username = username; this.userId = userId; this.votesReliability = votesReliability; this.contributesReliability = contributesReliability; this.totalReliability = totalReliability; this.authorship = new HashSet<>(); this.votes = new HashSet<>(); } /** * @return the username */ public String getUsername() { return username; } /** * @param username the username to set */ public void setUsername(String username) { this.username = username; } /** * @return the userId */ public int getUserId() { return userId; } /** * @param userId the userId to set */ public void setUserId(int userId) { this.userId = userId; } /** * @return the votesReliability */ public double getVotesReliability() { return votesReliability; } /** * @param votesReliability the votesReliability to set */ public void setVotesReliability(double votesReliability) { this.votesReliability = votesReliability; } /** * @return the contributesReliability */ public double getContributesReliability() { return contributesReliability; } /** * @param contributesReliability the contributesReliability to set */ public void setContributesReliability(double contributesReliability) { this.contributesReliability = contributesReliability; } /** * @return the totalReliability */ public double getTotalReliability() { return totalReliability; } /** * @param totalReliability the totalReliability to set */ public void setTotalReliability(double totalReliability) { this.totalReliability = totalReliability; } /** * * @return */ public Set getAuthorship() { return authorship; } /** * * @param authorship */ public void setAuthors(Set authorship) { this.authorship = authorship; } public void addAuthorship(Author author){ this.authorship.add(author); } /** * * @return the votes of the user */ public Set getVotes() { return votes; } /** * * @param votes the votes to set */ public void setVotes(Set votes) { this.votes = votes; } public void addVote(Vote vote){ this.votes.add(vote); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "User [username=" + username + ", userId=" + userId + ", votesReliability=" + votesReliability + ", contributesReliability=" + contributesReliability + ", totalReliability=" + totalReliability + "]"; } } diff --git a/src/main/java/org/wikitolearn/wikirating/model/Vote.java b/src/main/java/org/wikitolearn/wikirating/model/graph/Vote.java similarity index 97% rename from src/main/java/org/wikitolearn/wikirating/model/Vote.java rename to src/main/java/org/wikitolearn/wikirating/model/graph/Vote.java index e38786c..f3b9472 100644 --- a/src/main/java/org/wikitolearn/wikirating/model/Vote.java +++ b/src/main/java/org/wikitolearn/wikirating/model/graph/Vote.java @@ -1,125 +1,125 @@ /** * */ -package org.wikitolearn.wikirating.model; +package org.wikitolearn.wikirating.model.graph; import org.neo4j.ogm.annotation.*; import org.neo4j.ogm.annotation.typeconversion.DateLong; import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.Date; /** * @author aletundo, valsdav * */ @RelationshipEntity( type = "VOTE") public class Vote { @GraphId @JsonIgnore private Long graphId; private double value; private double reliability; @DateLong private Date timestamp; @StartNode private User user; @EndNode private Revision revision; /** * */ public Vote() {} /** * @param value * @param reliability * @param timestamp */ public Vote(double value, double reliability, Date timestamp) { this.value = value; this.reliability = reliability; this.timestamp = timestamp; } /** * @return the value */ public double getValue() { return value; } /** * @param value the value to set */ public void setValue(double value) { this.value = value; } /** * @return the reliability */ public double getReliability() { return reliability; } /** * @param reliability the reliability to set */ public void setReliability(double reliability) { this.reliability = reliability; } /** * * @return the user */ public User getUser() { return user; } /** * * @param user the user to set */ public void setUser(User user) { this.user = user; } /** * * @return the revision */ public Revision getRevision() { return revision; } /** * * @param revision the revision to set */ public void setRevision(Revision revision) { this.revision = revision; } /** * * @return the timestamp */ public Date getTimestamp() { return timestamp; } /** * * @param timestamp the timestamp to set */ public void setTimestamp(Date timestamp) { this.timestamp = timestamp; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "Vote [value=" + value + ", reliability=" + reliability + "]"; } } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/MetadataRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/MetadataRepository.java index 1944431..9c4fdfa 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/MetadataRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/MetadataRepository.java @@ -1,27 +1,27 @@ package org.wikitolearn.wikirating.repository; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; -import org.wikitolearn.wikirating.model.Metadata; +import org.wikitolearn.wikirating.model.graph.Metadata; import org.wikitolearn.wikirating.util.enums.MetadataType; /** * @author aletundo * @author valsdav */ public interface MetadataRepository extends GraphRepository { /** * Get the Metadata node of the specified type * * @param type * @return the Metadata node of the specified type */ Metadata getMetadataByType(MetadataType type); /** * Update the LAST_PROCESS relationship deleting the existing edge and * creating a new one. */ @Query("MATCH (m:Metadata)-[lp:LATEST_PROCESS]->(p1:Process)<-[pp:PREVIOUS_PROCESS]-(p2:Process) DELETE lp CREATE (m)-[:LATEST_PROCESS]->(p2)") void updateLatestProcess(); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/PageRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/PageRepository.java index 5c3f357..83f3211 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/PageRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/PageRepository.java @@ -1,70 +1,70 @@ /** * */ package org.wikitolearn.wikirating.repository; import java.util.List; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; -import org.wikitolearn.wikirating.model.Page; +import org.wikitolearn.wikirating.model.graph.Page; /** * @author aletundo * */ public interface PageRepository extends GraphRepository { /** * * @param title * @param lang * @return */ Page findByTitleAndLang(String title, String lang); /** * * @param langPageId * @return */ Page findByLangPageId(String langPageId); /** * * @param lang * @return */ @Query("MATCH (p:Page {lang:{0}}) RETURN p") List findAllByLang(String lang); /** * * @return */ @Query("MATCH (p:CourseRoot) RETURN p") List findAllCourseRootPages(); /** * * @param lang * @return */ @Query("MATCH (p:CourseRoot) WHERE p.lang = {lang} RETURN p") List findAllCourseRootPages(@Param("lang") String lang); /** * * @return */ @Query("MATCH (p:Page) WHERE NOT p:CourseRoot AND NOT p:CourseLevelTwo AND NOT p:CourseLevelThree RETURN p") List findAllUncategorizedPages(); /** * * @param lang * @return */ @Query("MATCH (p:Page) WHERE p.lang = {lang} AND NOT p:CourseRoot AND NOT p:CourseLevelTwo AND NOT p:CourseLevelThree RETURN p") List findAllUncategorizedPages(@Param("lang") String lang); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/ProcessRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/ProcessRepository.java index 7292f7b..bf37d61 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/ProcessRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/ProcessRepository.java @@ -1,37 +1,37 @@ /** * */ package org.wikitolearn.wikirating.repository; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; -import org.wikitolearn.wikirating.model.Process; +import org.wikitolearn.wikirating.model.graph.Process; import org.wikitolearn.wikirating.util.enums.ProcessType; /** * @author aletundo * @author valsdav */ public interface ProcessRepository extends GraphRepository { /** * This query returns the last process created: the one connected to the * Metadata node of PROCESSES type. * @return the last Process */ @Query("MATCH (p:Process)<-[LATEST_PROCESS]-(m:Metadata {type:'PROCESSES'}) RETURN p") public Process getLatestProcess(); @Query("MATCH (p:Process {processStatus:'ONGOING'}) RETURN p") public Process getOnGoingProcess(); /** * This query returns the latest process registered of the given type. * @param type the type of the process to look for * @return the found process */ @Query("MATCH (m:Metadata)-[*]->(p:Process {processType:{type}}) RETURN p LIMIT 1") public Process getLatestProcessByType(@Param("type") ProcessType type); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java index cee29f2..ef8c5f4 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/RevisionRepository.java @@ -1,57 +1,57 @@ /** * */ package org.wikitolearn.wikirating.repository; import java.util.List; import java.util.Set; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; -import org.wikitolearn.wikirating.model.Revision; +import org.wikitolearn.wikirating.model.graph.Revision; /** * @author aletundo * */ public interface RevisionRepository extends GraphRepository { /** * * @param langRevId * @return */ Revision findByLangRevId(String langRevId); /** * * @param lang * @return */ Set findByLang(String lang); /** * * @param userId * @return */ Set findByUserId(int userId); /** * This query returns all the Revisions of a Page. * The direction -> of the link is important to traverse * only the chain of Revisions of the page without reaching other nodes. * @param langPageId * @return */ @Query("MATCH (p:Page {langPageId:{0}})-[:LAST_REVISION|PREVIOUS_REVISION*]->(r:Revision) RETURN r") Set findAllRevisionOfPage(String langPageId); /** * This query return the previous revision of a Revision identified * by langRevId. * @param langRevId langRevId of the requested revision * @return the previous revision */ @Query("MATCH (r:Revision {langRevId:{0}})-[:PREVIOUS_REVISION]->(a:Revision) RETURN a") Revision findPreviousRevision(String langRevId); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/TemporaryVoteRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/TemporaryVoteRepository.java index 92b01e5..55a71df 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/TemporaryVoteRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/TemporaryVoteRepository.java @@ -1,25 +1,25 @@ package org.wikitolearn.wikirating.repository; import java.util.Date; import java.util.List; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; -import org.wikitolearn.wikirating.model.TemporaryVote; +import org.wikitolearn.wikirating.model.graph.TemporaryVote; /** * Created by valsdav on 24/03/17. */ public interface TemporaryVoteRepository extends GraphRepository { TemporaryVote findByLangRevId(String langRevId); /** * Find temporary votes with added before the given timestamp * @param timestamp * @return the temporary votes list */ @Query("MATCH (n:TemporaryVote) WHERE n.timestamp < {timestamp} RETURN n") List findByTimestamp(@Param("timestamp") Date timestamp); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/UserRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/UserRepository.java index dda90db..755dde4 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/UserRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/UserRepository.java @@ -1,27 +1,27 @@ /** * */ package org.wikitolearn.wikirating.repository; import org.springframework.data.neo4j.repository.GraphRepository; -import org.wikitolearn.wikirating.model.User; +import org.wikitolearn.wikirating.model.graph.User; /** * @author aletundo * */ public interface UserRepository extends GraphRepository { /** * * @param username * @return */ User findByUsername(String username); /** * * @param userId * @return */ User findByUserId(int userId); } diff --git a/src/main/java/org/wikitolearn/wikirating/repository/VoteRepository.java b/src/main/java/org/wikitolearn/wikirating/repository/VoteRepository.java index a7a17c1..364822e 100644 --- a/src/main/java/org/wikitolearn/wikirating/repository/VoteRepository.java +++ b/src/main/java/org/wikitolearn/wikirating/repository/VoteRepository.java @@ -1,26 +1,26 @@ /** * */ package org.wikitolearn.wikirating.repository; import java.util.List; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; -import org.wikitolearn.wikirating.model.Vote; +import org.wikitolearn.wikirating.model.graph.Vote; /** * @author aletundo * */ public interface VoteRepository extends GraphRepository { /** * Get all votes of the requested revision * @param langRevId the langRevId of the revision * @return the list of the votes */ @Query("MATCH (:User)-[v:Vote]->(:Revision {langRevId: {langRevId}}) RETURN v") List getAllVotesOfRevision(@Param("langRevId") String langRevId); } diff --git a/src/main/java/org/wikitolearn/wikirating/service/MaintenanceService.java b/src/main/java/org/wikitolearn/wikirating/service/MaintenanceService.java index 46a9e21..553da37 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/MaintenanceService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/MaintenanceService.java @@ -1,255 +1,255 @@ package org.wikitolearn.wikirating.service; 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.Scheduled; import org.springframework.stereotype.Service; import org.wikitolearn.wikirating.exception.*; -import org.wikitolearn.wikirating.model.Process; +import org.wikitolearn.wikirating.model.graph.Process; import org.wikitolearn.wikirating.util.enums.ProcessStatus; import org.wikitolearn.wikirating.util.enums.ProcessType; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; /** * @author aletundo * @author valsdav */ @Service public class MaintenanceService { private static final Logger LOG = LoggerFactory.getLogger(MaintenanceService.class); @Autowired private UserService userService; @Autowired private PageService pageService; @Autowired private RevisionService revisionService; @Autowired private MetadataService metadataService; @Autowired private ProcessService processService; @Autowired private VoteService voteService; @Value("#{'${mediawiki.langs}'.split(',')}") private List langs; @Value("${mediawiki.protocol}") private String protocol; @Value("${mediawiki.api.url}") private String apiUrl; @Value("${mediawiki.namespace}") private String namespace; /** * Initialize the graph for the first time using parallel threads for each domain language * @return true if initialization succeed */ public boolean initializeGraph() { // Initialize Metadata service metadataService.initMetadata(); // Start a new Process Process initProcess = processService.addProcess(ProcessType.INIT); metadataService.addFirstProcess(initProcess); CompletableFuture initFuture = CompletableFuture // Users and pages .allOf(buildUsersAndPagesFuturesList().toArray(new CompletableFuture[langs.size() + 1])) .thenCompose(result -> CompletableFuture // Revisions .allOf(buildRevisionsFuturesList().toArray(new CompletableFuture[langs.size()]))) .thenCompose(result -> CompletableFuture // Change Coefficients .allOf(buildChangeCoefficientFuturesList().toArray(new CompletableFuture[langs.size()]))) .thenCompose(result -> CompletableFuture // CourseStructure .allOf(buildInitCourseStructureFuturesList().toArray(new CompletableFuture[langs.size()]))) .thenCompose(result -> userService.initAuthorship()); try { boolean result = initFuture.get(); // Save the result of the process if (result){ processService.closeCurrentProcess(ProcessStatus.DONE); }else{ processService.closeCurrentProcess(ProcessStatus.EXCEPTION); } return result; } catch (InterruptedException | ExecutionException e) { LOG.error("Something went wrong. {}", e.getMessage()); return false; } } /** * Entry point for the scheduled graph updated * @return true if the update succeed */ @Scheduled(cron = "${maintenance.update.cron}") public void updateGraph() { Process currentFetchProcess; Date startTimestampCurrentFetch, startTimestampLatestFetch; // Get start timestamp of the latest FETCH Process before opening a new process startTimestampLatestFetch = (processService.getLastProcessStartDateByType(ProcessType.FETCH) != null) ? processService.getLastProcessStartDateByType(ProcessType.FETCH) : processService.getLastProcessStartDateByType(ProcessType.INIT); // Create a new FETCH process try { currentFetchProcess = processService.addProcess(ProcessType.FETCH); metadataService.updateLatestProcess(); startTimestampCurrentFetch = currentFetchProcess.getStartOfProcess(); } catch (PreviousProcessOngoingException e){ LOG.error("Cannot start Update process because the previous process is still ONGOING." + "The update will be aborted."); return; } try { CompletableFuture updateFuture = CompletableFuture .allOf(userService.updateUsers(protocol + langs.get(0) + "." + apiUrl, startTimestampLatestFetch, startTimestampCurrentFetch)) .thenCompose(result -> CompletableFuture .allOf(buildUpdatePagesFuturesList(startTimestampLatestFetch, startTimestampCurrentFetch) .toArray(new CompletableFuture[langs.size()]))) .thenCompose(result -> CompletableFuture .allOf(buildUpdateCourseStructureFuturesList().toArray(new CompletableFuture[langs.size()]))) .thenCompose(result -> voteService.validateTemporaryVotes(startTimestampCurrentFetch)); boolean result = updateFuture.get(); // Save the result of the process, closing the current one if (result) { processService.closeCurrentProcess(ProcessStatus.DONE); } else { processService.closeCurrentProcess(ProcessStatus.ERROR); } } catch (TemporaryVoteValidationException | UpdateUsersException | UpdatePagesAndRevisionsException | InterruptedException | ExecutionException e) { processService.closeCurrentProcess(ProcessStatus.EXCEPTION); LOG.error("An error occurred during a scheduled graph update procedure"); throw new UpdateGraphException(); } } /** * Build a list of CompletableFuture. * * @return a list of CompletableFuture */ private List> buildInitCourseStructureFuturesList() { List> parallelInitCourseStructureFutures = new ArrayList<>(); // Add course structure for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; parallelInitCourseStructureFutures.add(pageService.initCourseStructure(lang, url)); } return parallelInitCourseStructureFutures; } /** * Build a list of CompletableFuture. * * @return a list of CompletableFuture */ private List> buildUpdateCourseStructureFuturesList() { List> parallelUpdateCourseStructureFutures = new ArrayList<>(); // Add course structure for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; parallelUpdateCourseStructureFutures.add(pageService.updateCourseStructure(lang, url)); } return parallelUpdateCourseStructureFutures; } /** * * @param start * @param end * @return */ private List> buildUpdatePagesFuturesList(Date start, Date end) { List> futures = new ArrayList<>(); // Add update pages for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; futures.add(pageService.updatePages(lang, url, start, end)); } return futures; } /** * Build a list of CompletableFuture. The elements are the fetches of pages' * revisions from each domain language. * * @return a list of CompletableFuture */ private List> buildRevisionsFuturesList() { List> parallelRevisionsFutures = new ArrayList<>(); // Add revisions fetch for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; parallelRevisionsFutures.add(revisionService.initRevisions(lang, url)); } return parallelRevisionsFutures; } private List> buildChangeCoefficientFuturesList(){ List> parallelCCFutures = new ArrayList<>(); // Add revisions fetch for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; parallelCCFutures.add(revisionService.calculateChangeCoefficientAllRevisions(lang, url)); } return parallelCCFutures; } /** * Build a list of CompletableFuture. The first one is the fetch of the * users from the first domain in mediawiki.langs list. The rest of the * elements are the fetches of the pages for each language. This * implementation assumes that the users are shared among domains. * * @return a list of CompletableFuture */ private List> buildUsersAndPagesFuturesList() { List> usersAndPagesInsertion = new ArrayList<>(); // Add users fetch as fist operation usersAndPagesInsertion.add(userService.initUsers(protocol + langs.get(0) + "." + apiUrl)); // Add pages fetch for each domain language for (String lang : langs) { String url = protocol + lang + "." + apiUrl; usersAndPagesInsertion.add(pageService.initPages(lang, url)); } return usersAndPagesInsertion; } /*@SuppressWarnings("unchecked") private List> buildFuturesList(Object obj, String methodPrefix) { List> futures = new ArrayList<>(); for (String lang : langs) { String url = protocol + lang + "." + apiUrl; Method[] methods = obj.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { try { if (methods[i].getName().startsWith(methodPrefix) && methods[i].getParameterCount() == 1) { futures.add((CompletableFuture) methods[i].invoke(obj, url)); } else if (methods[i].getName().startsWith(methodPrefix) && methods[i].getParameterCount() == 2) { futures.add((CompletableFuture) methods[i].invoke(obj, lang, url)); } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return futures; }*/ } diff --git a/src/main/java/org/wikitolearn/wikirating/service/MetadataService.java b/src/main/java/org/wikitolearn/wikirating/service/MetadataService.java index b4ed59e..9cc94fd 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/MetadataService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/MetadataService.java @@ -1,71 +1,71 @@ package org.wikitolearn.wikirating.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.wikitolearn.wikirating.exception.FirstProcessInsertionException; import org.wikitolearn.wikirating.exception.LatestProcessUpdateException; -import org.wikitolearn.wikirating.model.Metadata; -import org.wikitolearn.wikirating.model.Process; +import org.wikitolearn.wikirating.model.graph.Metadata; +import org.wikitolearn.wikirating.model.graph.Process; import org.wikitolearn.wikirating.repository.MetadataRepository; import org.wikitolearn.wikirating.util.enums.MetadataType; /** * This service manages the addition of information about Metadata * @author aletundo * @author valsdav */ @Service public class MetadataService { private static final Logger LOG = LoggerFactory.getLogger(MetadataService.class); @Autowired private MetadataRepository metadataRepository; /** * This method insert in the DB the root nodes of the metadata */ public void initMetadata(){ Metadata metadataProcesses = new Metadata(MetadataType.PROCESSES); Metadata metadataStats = new Metadata(MetadataType.STATS); metadataRepository.save(metadataProcesses); metadataRepository.save(metadataStats); LOG.info("Initialized Metadata nodes"); } /** * Update the LATEST_PROCESS relationship * @return */ public void updateLatestProcess() throws LatestProcessUpdateException{ try{ metadataRepository.updateLatestProcess(); LOG.info("Updated LATEST_PROCESS relationship"); }catch(Exception e){ LOG.error("Something went wrong during update LATEST_PROCESS relationship: {}", e.getMessage()); throw new LatestProcessUpdateException(); } } /** * Add the first process of the chain to the Metadata node. * @param process First process */ public void addFirstProcess(Process process) throws FirstProcessInsertionException { try { Metadata metadata = metadataRepository.getMetadataByType(MetadataType.PROCESSES); // checking if this isn't the first Process. if (metadata.getLatestProcess() != null) { LOG.error("A Process already exists. You cannot re-insert the first Process"); throw new FirstProcessInsertionException("A Process already exists. You cannot re-insert the first Process"); } metadata.setLatestProcess(process); metadataRepository.save(metadata); } catch (FirstProcessInsertionException f){ throw f; } catch (Exception e){ LOG.error("Something went wrong during insertion of the first Process: {}", e.getMessage()); throw new FirstProcessInsertionException(); } } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/PageService.java b/src/main/java/org/wikitolearn/wikirating/service/PageService.java index 981eea2..65a30e9 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/PageService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/PageService.java @@ -1,369 +1,369 @@ /** * */ 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.model.graph.Page; +import org.wikitolearn.wikirating.model.graph.Revision; 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 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); 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/ProcessService.java b/src/main/java/org/wikitolearn/wikirating/service/ProcessService.java index c90ed14..fd5b6cf 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/ProcessService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/ProcessService.java @@ -1,96 +1,96 @@ package org.wikitolearn.wikirating.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.wikitolearn.wikirating.exception.AddProcessException; import org.wikitolearn.wikirating.exception.PreviousProcessOngoingException; -import org.wikitolearn.wikirating.model.Process; +import org.wikitolearn.wikirating.model.graph.Process; import org.wikitolearn.wikirating.repository.ProcessRepository; import org.wikitolearn.wikirating.util.enums.ProcessStatus; import org.wikitolearn.wikirating.util.enums.ProcessType; import java.util.Date; /** * Created by valsdav on 29/03/17. */ @Service public class ProcessService { private static final Logger LOG = LoggerFactory.getLogger(ProcessService.class); @Autowired private ProcessRepository processRepository; /** * This method creates a new process of the specified * type and adds it on the top of the processes chain. * @param type Type of process requested * @return returns the created process * @throws AddProcessException */ public Process addProcess(ProcessType type) throws AddProcessException, PreviousProcessOngoingException{ try { Process process = new Process(type); Process previousProcess = processRepository.getLatestProcess(); if (previousProcess != null) { // Check if the process is still ONGOING if (previousProcess.getProcessStatus() == ProcessStatus.ONGOING) { LOG.error("Cannot create new Process: the previous one is still ONGOING"); throw new PreviousProcessOngoingException(); } process.setPreviousProcess(previousProcess); } processRepository.save(process); LOG.info("Created new process: {}", process.toString()); return process; }catch (PreviousProcessOngoingException p){ throw p; }catch(Exception e){ LOG.error("An error occurred during process creation: {}", e.getMessage()); throw new AddProcessException(); } } /** * This method modify the status of the last opened process * and saves it. * @param status Final status of the process * @return returns the closed process */ public Process closeCurrentProcess(ProcessStatus status){ Process currentProcess = processRepository.getOnGoingProcess(); currentProcess.setProcessStatus(status); currentProcess.setEndOfProcess(new Date()); processRepository.save(currentProcess); LOG.info("Update the status of the latest process: {}", currentProcess.toString()); return currentProcess; } /** * Get the start date of the latest process * @return the start date of the latest process */ public Date getLastProcessStartDate(){ Process latestProcess = processRepository.getLatestProcess(); if(latestProcess != null){ return latestProcess.getStartOfProcess(); } return null; } /** * Get the start date of the latest process of the specified type * @param type the process type requested * @return the start date of the latest process of the specified type */ public Date getLastProcessStartDateByType(ProcessType type){ Process latestProcess = processRepository.getLatestProcessByType(type); if(latestProcess != null){ return latestProcess.getStartOfProcess(); } return null; } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java b/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java index 3d259f6..1716243 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/RevisionService.java @@ -1,197 +1,197 @@ /** * */ 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.model.graph.Page; +import org.wikitolearn.wikirating.model.graph.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 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; // Get the previous Revision Revision previousRevision = revision.getPreviousRevision(); if (previousRevision == null){ previousLength = 1; changeCoefficient = 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; }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; } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/UserService.java b/src/main/java/org/wikitolearn/wikirating/service/UserService.java index 8b9ca42..06197e5 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/UserService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/UserService.java @@ -1,174 +1,174 @@ /** * */ package org.wikitolearn.wikirating.service; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; 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.GetNewUsersException; import org.wikitolearn.wikirating.exception.UpdateUsersException; import org.wikitolearn.wikirating.exception.UserNotFoundException; -import org.wikitolearn.wikirating.model.Author; -import org.wikitolearn.wikirating.model.Revision; import org.wikitolearn.wikirating.model.UpdateInfo; -import org.wikitolearn.wikirating.model.User; +import org.wikitolearn.wikirating.model.graph.Author; +import org.wikitolearn.wikirating.model.graph.Revision; +import org.wikitolearn.wikirating.model.graph.User; import org.wikitolearn.wikirating.repository.RevisionRepository; import org.wikitolearn.wikirating.repository.UserRepository; import org.wikitolearn.wikirating.service.mediawiki.UpdateMediaWikiService; import org.wikitolearn.wikirating.service.mediawiki.UserMediaWikiService; /** * * @author aletundo, valsdav * */ @Service public class UserService { private static final Logger LOG = LoggerFactory.getLogger(UserService.class); @Autowired private UserMediaWikiService userMediaWikiService; @Autowired private UpdateMediaWikiService updateMediaWikiService; @Autowired private UserRepository userRepository; @Autowired private RevisionRepository revisionRepository; /** * Initialize the graph for the first time querying the MediaWiki API * to get the all the users and then insert them. * @param apiUrl the MediaWiki API url * @return CompletableFuture */ @Async public CompletableFuture initUsers(String apiUrl){ List users = userMediaWikiService.getAll(apiUrl); addUsers(users); return CompletableFuture.completedFuture(true); } /** * Initialize relationships between users and their created revisions for the first time * @return CompletableFuture */ @Async public CompletableFuture initAuthorship(){ Iterable users = userRepository.findAll(); for(User user : users){ Set revisions = revisionRepository.findByUserId(user.getUserId()); this.setAuthorship(revisions, user); LOG.info("Set revisions authorship for user {}", user.getUserId()); } // Get revisions with userid = 0 (anonymous authors) Set anonRevisions = revisionRepository.findByUserId(0); User anonymousUser = new User("AnonymousUser", 0, 0.0, 0.0, 0.0); this.setAuthorship(anonRevisions, anonymousUser); LOG.info("Set revisions authorship for anonymous revisions"); return CompletableFuture.completedFuture(true); } /** * Insert users into the graph * @param users the list of users to be inserted * @return the list of inserted users */ public List addUsers(List users){ userRepository.save(users); LOG.info("Inserted users: {}", users); return users; } /** * Set the authorship for a list a Revisions for a selected User * @param revisions * @param user */ public void setAuthorship(Collection revisions, User user){ for (Revision rev : revisions){ Author author = new Author(user.getTotalReliability()); author.setRevision(rev); author.setUser(user); user.addAuthorship(author); } userRepository.save(user); } /** * Set the authorship for a revision creating a new Author relationship * that saves the reliability of the user. * @param revision the revision */ public void setAuthorship(Revision revision){ User user = userRepository.findByUserId(revision.getUserId()); Author author = new Author(user.getTotalReliability()); author.setRevision(revision); author.setUser(user); user.addAuthorship(author); userRepository.save(user); } /** * Set the authorship for a list of revisions * @param revisions the list of revisions */ /*public void setAuthorship(Collection revisions){ for(Revision revision : revisions){ setAuthorship(revision); } }*/ /** * Get the requested user. * @param userId the user id * @return the requested user * @throws UserNotFoundException */ public User getUser(int userId) throws UserNotFoundException{ User user = userRepository.findByUserId(userId); if(user == null){ LOG.error("User {} not found", userId); throw new UserNotFoundException(); } return user; } /** * * @param apiUrl * @param start * @param end * @throws UpdateUsersException */ @Async public CompletableFuture updateUsers(String apiUrl, Date start, Date end) throws UpdateUsersException{ try{ List usersUpdateInfo = updateMediaWikiService.getNewUsers(apiUrl, start, end); List newUsers = new ArrayList<>(); // Build a list with new users to be added to the graph for(UpdateInfo updateInfo : usersUpdateInfo){ User user = new User(); user.setUserId(updateInfo.getUserid()); user.setUsername(updateInfo.getUser()); newUsers.add(user); } addUsers(newUsers); }catch(GetNewUsersException e){ LOG.error("An error occurred while updating users: {}", e.getMessage()); throw new UpdateUsersException(); } return CompletableFuture.completedFuture(true); } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/VoteService.java b/src/main/java/org/wikitolearn/wikirating/service/VoteService.java index 8b0fc90..4bf8e6b 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/VoteService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/VoteService.java @@ -1,82 +1,82 @@ /** * */ package org.wikitolearn.wikirating.service; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; 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.exception.TemporaryVoteValidationException; import org.wikitolearn.wikirating.exception.UserNotFoundException; -import org.wikitolearn.wikirating.model.Revision; -import org.wikitolearn.wikirating.model.TemporaryVote; -import org.wikitolearn.wikirating.model.User; -import org.wikitolearn.wikirating.model.Vote; +import org.wikitolearn.wikirating.model.graph.Revision; +import org.wikitolearn.wikirating.model.graph.TemporaryVote; +import org.wikitolearn.wikirating.model.graph.User; +import org.wikitolearn.wikirating.model.graph.Vote; import org.wikitolearn.wikirating.repository.TemporaryVoteRepository; import org.wikitolearn.wikirating.repository.VoteRepository; /** * @author aletundo * */ @Service public class VoteService { private static final Logger LOG = LoggerFactory.getLogger(VoteService.class); @Autowired private TemporaryVoteRepository temporaryVoteRepository; @Autowired private VoteRepository voteRepository; @Autowired private UserService userService; @Autowired private RevisionService revisionService; /** * Validate temporary votes added before the given timestamp * @param timestamp the timestamp used for comparison * @throws TemporaryVoteValidationException */ @Async public CompletableFuture validateTemporaryVotes(Date timestamp) throws TemporaryVoteValidationException{ List temporaryVotes = temporaryVoteRepository.findByTimestamp(timestamp); for(TemporaryVote temporaryVote: temporaryVotes){ try{ User user = userService.getUser(temporaryVote.getUserId()); Revision revision = revisionService.getRevision(temporaryVote.getLangRevId()); Vote vote = new Vote(temporaryVote.getValue(), temporaryVote.getReliability(), temporaryVote.getTimestamp()); vote.setRevision(revision); vote.setUser(user); revision.addVote(vote); revisionService.updateRevision(revision); }catch(UserNotFoundException | RevisionNotFoundException e){ LOG.error("An error occurred during temporary vote validation: {}", temporaryVote); throw new TemporaryVoteValidationException(e.getMessage()); } } return CompletableFuture.completedFuture(true); } /** * Get all votes of the requested revision * @param langRevId the langRevId of the revision * @return the list of votes */ public List getAllVotesOfRevision(String langRevId){ // FIXME: Check the different return type when there are no votes // but the revision exists and change the logic. List votes; votes = voteRepository.getAllVotesOfRevision(langRevId); if(votes == null){ return new ArrayList(); } return votes; } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/PageMediaWikiService.java b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/PageMediaWikiService.java index 641f31a..c16bf03 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/PageMediaWikiService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/PageMediaWikiService.java @@ -1,106 +1,106 @@ /** * */ package org.wikitolearn.wikirating.service.mediawiki; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.stereotype.Service; import org.wikidata.wdtk.wikibaseapi.ApiConnection; import org.wikitolearn.wikirating.exception.GenericException; import org.wikitolearn.wikirating.model.CourseTree; -import org.wikitolearn.wikirating.model.Page; +import org.wikitolearn.wikirating.model.graph.Page; import com.fasterxml.jackson.core.type.TypeReference; /** * * @author aletundo, valsdav * */ @Service public class PageMediaWikiService extends MediaWikiService{ /** * Get all the pages from a specified namespace of MediaWiki instance through its API. * @param apiUrl the MediaWiki API url * @return pages a list that contains all the fetched pages */ public List getAll(String apiUrl){ ApiConnection connection = mediaWikiApiUtils.getApiConnection(apiUrl); Map parameters = mediaWikiApiUtils.getListAllPagesParams(namespace); InputStream response; boolean morePages = true; JSONArray pagesJson = new JSONArray(); List toBeConcat = new ArrayList<>(); List pages = new ArrayList<>(); try { while(morePages){ response = mediaWikiApiUtils.sendRequest(connection, "GET", parameters); JSONObject responseJson = mediaWikiApiUtils.streamToJson(response); toBeConcat.add(responseJson.getJSONObject("query").getJSONArray("allpages")); if(responseJson.has("continue")){ String continueFrom = responseJson.getJSONObject("continue").getString("apcontinue"); parameters.put("apfrom", continueFrom); }else{ morePages = false; pagesJson = concatArrays(toBeConcat); } } pages = mapper.readValue(pagesJson.toString(), new TypeReference>(){}); return pages; } catch (JSONException e){ LOG.error("An error occurred while a JSONObject or JSONArray. {}", e.getMessage()); } catch(IOException e){ LOG.error("An error occurred while converting an InputStream to JSONObject. {}", e.getMessage()); } return pages; } /** * Get the course tree structure through the MediaWiki API * @param apiUrl the MediaWiki API url * @param pageTitle the title of the root course page * @return the course tree */ public CourseTree getCourseTree(String apiUrl, String pageTitle) { ApiConnection connection = mediaWikiApiUtils.getApiConnection(apiUrl); Map parameters = mediaWikiApiUtils.getCourseTreeParams(pageTitle); InputStream response; response = mediaWikiApiUtils.sendRequest(connection, "GET", parameters); JSONObject responseJson = mediaWikiApiUtils.streamToJson(response); try { JSONObject jsonTree = responseJson.getJSONObject("coursetree").getJSONObject("response"); List> levelsThree = new ArrayList<>(); JSONArray levelsThreeJson = jsonTree.getJSONArray("levelsThree"); levelsThree = mapper.readValue(levelsThreeJson.toString(), new TypeReference>>(){}); // Build course tree manually cause difficulties with serialization of nested JSON arrays CourseTree courseTree = new CourseTree(); courseTree.setRoot(jsonTree.getString("root")); courseTree.setLevelsTwo( mapper.readValue( jsonTree.getJSONArray("levelsTwo").toString(), new TypeReference>(){} ) ); courseTree.setLevelsTree(levelsThree); LOG.info("Got course tree for page {}: {}", pageTitle, courseTree.toString()); return courseTree; } catch (JSONException | IOException e) { LOG.error("An error occurred: {}", e.getMessage()); throw new GenericException(e.getMessage()); } } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/RevisionMediaWikiService.java b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/RevisionMediaWikiService.java index f0989d1..49a4f79 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/RevisionMediaWikiService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/RevisionMediaWikiService.java @@ -1,89 +1,89 @@ package org.wikitolearn.wikirating.service.mediawiki; import com.fasterxml.jackson.core.type.TypeReference; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.stereotype.Service; import org.wikidata.wdtk.wikibaseapi.ApiConnection; import org.wikitolearn.wikirating.exception.GetDiffPreviousRevisionExeception; -import org.wikitolearn.wikirating.model.Revision; +import org.wikitolearn.wikirating.model.graph.Revision; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * * @author aletundo, valsdav * */ @Service public class RevisionMediaWikiService extends MediaWikiService{ /** * Get the revision * @param apiUrl * @return the revision with the diff information * @throws GetDiffPreviousRevisionExeception */ public String getDiffPreviousRevision(String apiUrl, int oldRevId, int newRevId) throws GetDiffPreviousRevisionExeception { try { Map parameters = mediaWikiApiUtils.getDiffRevisionParams(oldRevId, newRevId); ApiConnection connection = mediaWikiApiUtils.getApiConnection(apiUrl); InputStream response = mediaWikiApiUtils.sendRequest(connection, "GET", parameters); JSONObject responseJson = mediaWikiApiUtils.streamToJson(response); String diffText = responseJson.getJSONObject("compare").getString("*"); return diffText; } catch (JSONException e) { LOG.error("An error occurred getting diff of previous revision of revision with id: {}. {}", newRevId, e.getMessage()); throw new GetDiffPreviousRevisionExeception(); } } /** * Get all the revisions for a specific page querying MediaWiki API * @param apiUrl the MediaWiki API url * @param pageId the id the page of which getting the revisions * @return revisions a list that contains all the fetched revisions */ public List getAllRevisionByPageId(String apiUrl, int pageId) { Map parameters = mediaWikiApiUtils.getRevisionParams(pageId); ApiConnection connection = mediaWikiApiUtils.getApiConnection(apiUrl); InputStream response; boolean moreRevs = true; JSONArray revsJson = new JSONArray(); List toBeConcat = new ArrayList<>(); List revs = new ArrayList<>(); try { while(moreRevs){ response = mediaWikiApiUtils.sendRequest(connection, "GET", parameters); JSONObject responseJson = mediaWikiApiUtils.streamToJson(response); toBeConcat.add(responseJson.getJSONObject("query").getJSONObject("pages"). getJSONObject(parameters.get("pageids")).getJSONArray("revisions")); if(responseJson.has("continue")){ String continueFrom = responseJson.getJSONObject("continue").getString("rvcontinue"); parameters.put("rvcontinue", continueFrom); }else{ moreRevs = false; revsJson = concatArrays(toBeConcat); } } revs = mapper.readValue(revsJson.toString(), new TypeReference>(){}); return revs; } catch (JSONException e){ LOG.error("An error occurred while a JSONObject or JSONArray. {}", e.getMessage()); } catch(IOException e){ LOG.error("An error occurred while converting an InputStream to JSONObject. {}", e.getMessage()); } return revs; } } diff --git a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/UserMediaWikiService.java b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/UserMediaWikiService.java index a370d8d..92fcdd7 100644 --- a/src/main/java/org/wikitolearn/wikirating/service/mediawiki/UserMediaWikiService.java +++ b/src/main/java/org/wikitolearn/wikirating/service/mediawiki/UserMediaWikiService.java @@ -1,62 +1,62 @@ package org.wikitolearn.wikirating.service.mediawiki; import com.fasterxml.jackson.core.type.TypeReference; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.stereotype.Service; import org.wikidata.wdtk.wikibaseapi.ApiConnection; -import org.wikitolearn.wikirating.model.User; +import org.wikitolearn.wikirating.model.graph.User; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * * @author aletundo, valsdav * */ @Service public class UserMediaWikiService extends MediaWikiService{ /** * Get all the users from MediaWiki instance through its API. * @param apiUrl the MediaWiki API url * @return users a list that contains all the fetched users */ public List getAll(String apiUrl){ ApiConnection connection = mediaWikiApiUtils.getApiConnection(apiUrl); Map parameters = mediaWikiApiUtils.getUserParams(); InputStream response; boolean moreUsers = true; JSONArray usersJson = new JSONArray(); List toBeConcat = new ArrayList<>(); List users = new ArrayList<>(); try { while(moreUsers){ response = mediaWikiApiUtils.sendRequest(connection, "GET", parameters); JSONObject responseJson = mediaWikiApiUtils.streamToJson(response); toBeConcat.add(responseJson.getJSONObject("query").getJSONArray("allusers")); if(responseJson.has("continue")){ String continueFrom = responseJson.getJSONObject("continue").getString("aufrom"); parameters.put("aufrom", continueFrom); }else{ moreUsers = false; usersJson = concatArrays(toBeConcat); } } users = mapper.readValue(usersJson.toString(), new TypeReference>(){}); return users; } catch (JSONException e){ LOG.error("An error occurred while a JSONObject or JSONArray. {}", e.getMessage()); } catch(IOException e){ LOG.error("An error occurred while converting an InputStream to JSONObject. {}", e.getMessage()); } return users; } } \ No newline at end of file diff --git a/src/test/java/org/wikitolearn/wikirating/service/PageServiceTest.java b/src/test/java/org/wikitolearn/wikirating/service/PageServiceTest.java index e6a941f..5ee92e8 100644 --- a/src/test/java/org/wikitolearn/wikirating/service/PageServiceTest.java +++ b/src/test/java/org/wikitolearn/wikirating/service/PageServiceTest.java @@ -1,146 +1,146 @@ /** * */ package org.wikitolearn.wikirating.service; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.wikitolearn.wikirating.exception.PageNotFoundException; -import org.wikitolearn.wikirating.model.Page; -import org.wikitolearn.wikirating.model.Revision; +import org.wikitolearn.wikirating.model.graph.Page; +import org.wikitolearn.wikirating.model.graph.Revision; import org.wikitolearn.wikirating.repository.PageRepository; /** * @author aletundo * */ @RunWith(SpringJUnit4ClassRunner.class) public class PageServiceTest { @Mock private PageRepository pageRepository; @InjectMocks private PageService pageService; @Before public void setup(){ MockitoAnnotations.initMocks(this); } @Test public void testGetPageByLangPageId(){ Page page = new Page(1, "Title", "en", "en_1"); when(pageRepository.findByLangPageId("en_1")).thenReturn(page); Page result = pageService.getPage("en_1"); assertEquals(1, result.getPageId()); assertEquals("Title", result.getTitle()); assertEquals("en", result.getLang()); assertEquals("en_1", result.getLangPageId()); } @Test public void testGetPageByPageIdAndLang(){ Page page = new Page(1, "Title", "en", "en_1"); when(pageRepository.findByLangPageId("en_1")).thenReturn(page); Page result = pageService.getPage(1, "en"); assertEquals(1, result.getPageId()); assertEquals("Title", result.getTitle()); assertEquals("en", result.getLang()); assertEquals("en_1", result.getLangPageId()); } @Test(expected = PageNotFoundException.class) public void testGetPageByPageIdAndLangNotFound(){ when(pageRepository.findByLangPageId("en_1")).thenReturn(null); pageService.getPage(1, "en"); } @Test(expected = PageNotFoundException.class) public void testGetPageByLangPageIdNotFound(){ when(pageRepository.findByLangPageId("en_1")).thenReturn(null); pageService.getPage("en_1"); } @Test(expected = PageNotFoundException.class) public void testDeletePageNotFound(){ when(pageRepository.findByTitleAndLang("Title", "en")).thenReturn(null); pageService.deletePage("Title", "en"); } @Test public void testDeletePage(){ Page page = new Page(1, "Title", "en", "en_1"); when(pageRepository.findByTitleAndLang("Title", "en")).thenReturn(page); pageService.deletePage("Title", "en"); verify(pageRepository, times(1)).delete(page); } @Test(expected = PageNotFoundException.class) public void testMovePageNotFound(){ when(pageRepository.findByLangPageId("en_1")).thenReturn(null); pageService.movePage("Title", "NewTitle", "en"); } @Test public void testMovePage(){ Page page = new Page(1, "Title", "en", "en_1"); when(pageRepository.findByTitleAndLang("Title", "en")).thenReturn(page); Page result = pageService.movePage("Title", "NewTitle", "en"); assertEquals("NewTitle", result.getTitle()); } @Test public void testAddPage(){ Page page = new Page(1, "Title", "en", "en_1"); when(pageRepository.save(page)).thenReturn(page); Page result = pageService.addPage(1, "Title", "en", new Revision()); assertEquals(1, result.getPageId()); assertEquals("Title", result.getTitle()); assertEquals("en", result.getLang()); assertEquals("en_1", result.getLangPageId()); } @Test public void testGetUncategorizedPages(){ List uncategorizedPages = new ArrayList(); uncategorizedPages.add(new Page(1, "Title", "en", "en_1")); uncategorizedPages.add(new Page(2, "Title2", "en", "en_2")); uncategorizedPages.add(new Page(3, "Title3", "en", "en_4")); when(pageRepository.findAllUncategorizedPages("en")).thenReturn(uncategorizedPages); List result = pageService.getUncategorizedPages("en"); assertEquals(3, result.size()); } @Test public void testGetCourseRootPages(){ List uncategorizedPages = new ArrayList(); Page p1 = new Page(1, "Title", "en", "en_1"); p1.addLabel("CourseRoot"); Page p2 = new Page(2, "Title2", "en", "en_2"); p2.addLabel("CourseRoot"); Page p3 = new Page(3, "Title3", "en", "en_3"); p3.addLabel("CourseRoot"); uncategorizedPages.add(p1); uncategorizedPages.add(p2); uncategorizedPages.add(p3); when(pageRepository.findAllCourseRootPages("en")).thenReturn(uncategorizedPages); List result = pageService.getCourseRootPages("en"); assertEquals(3, result.size()); } } diff --git a/src/test/java/org/wikitolearn/wikirating/service/RevisionServiceTest.java b/src/test/java/org/wikitolearn/wikirating/service/RevisionServiceTest.java index 781995e..76ff5c2 100644 --- a/src/test/java/org/wikitolearn/wikirating/service/RevisionServiceTest.java +++ b/src/test/java/org/wikitolearn/wikirating/service/RevisionServiceTest.java @@ -1,133 +1,133 @@ /** * */ package org.wikitolearn.wikirating.service; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Date; import java.util.HashSet; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.wikitolearn.wikirating.exception.RevisionNotFoundException; -import org.wikitolearn.wikirating.model.Revision; +import org.wikitolearn.wikirating.model.graph.Revision; import org.wikitolearn.wikirating.repository.RevisionRepository; /** * @author aletundo * */ @RunWith(SpringJUnit4ClassRunner.class) public class RevisionServiceTest { @Mock private RevisionRepository revisionRepository; @InjectMocks private RevisionService revisionService; @Before public void setup(){ MockitoAnnotations.initMocks(this); } @Test public void testGetRevision(){ Date now = new Date(); Revision revision = new Revision(2, "en", 1, 1, 123456, now); when(revisionRepository.findByLangRevId("en_2")).thenReturn(revision); Revision result = revisionService.getRevision("en_2"); assertEquals(2, result.getRevId()); assertEquals("en", result.getLang()); assertEquals(1, result.getParentId()); assertEquals(1, result.getUserId()); assertEquals(123456, result.getLength()); assertEquals(now, result.getTimestamp()); } @Test(expected = RevisionNotFoundException.class) public void testGetRevisionNotFound(){ when(revisionRepository.findByLangRevId("en_1")).thenReturn(null); revisionService.getRevision("en_1"); } @Test public void testAddRevision(){ Date now = new Date(); Revision revision = new Revision(2, "en", 1, 1, 123456, now); when(revisionRepository.save(revision)).thenReturn(revision); Revision result = revisionService.addRevision(2, "en", 1, 1, 123456, now); assertEquals(2, result.getRevId()); assertEquals("en", result.getLang()); assertEquals(1, result.getParentId()); assertEquals(1, result.getUserId()); assertEquals(123456, result.getLength()); assertEquals(now, result.getTimestamp()); } @Test public void testUpdateRevision(){ Date now = new Date(); Revision revision = new Revision(2, "en", 1, 1, 123456, now); when(revisionRepository.save(revision)).thenReturn(revision); Revision result = revisionService.updateRevision(revision); assertEquals(2, result.getRevId()); assertEquals("en", result.getLang()); assertEquals(1, result.getParentId()); assertEquals(1, result.getUserId()); assertEquals(123456, result.getLength()); assertEquals(now, result.getTimestamp()); } @Test public void testDeleteRevisionsOfPage(){ Set revisions = new HashSet(); Revision r1 = new Revision(2, "en", 1, 1, 123456, new Date()); Revision r2 = new Revision(3, "en", 2, 1, 123456, new Date()); Revision r3 = new Revision(4, "en", 3, 1, 123456, new Date()); revisions.add(r1); revisions.add(r2); revisions.add(r3); when(revisionRepository.findAllRevisionOfPage("en_1")).thenReturn(revisions); revisionService.deleteRevisionsOfPage("en_1"); verify(revisionRepository, times(1)).delete(revisions); } @Test(expected = RevisionNotFoundException.class) public void testDeleteRevisionsOfPageNotFound(){ Set revisions = new HashSet(); when(revisionRepository.findAllRevisionOfPage("en_1")).thenReturn(revisions); revisionService.deleteRevisionsOfPage("en_1"); } @Test public void testGetRevisionsOfPage(){ Set revisions = new HashSet(); Revision r1 = new Revision(2, "en", 1, 1, 123456, new Date()); Revision r2 = new Revision(3, "en", 2, 1, 123456, new Date()); Revision r3 = new Revision(4, "en", 3, 1, 123456, new Date()); revisions.add(r1); revisions.add(r2); revisions.add(r3); when(revisionRepository.findAllRevisionOfPage("en_1")).thenReturn(revisions); Set result = revisionService.getRevisionsOfPage("en_1"); assertEquals(3, result.size()); } @Test(expected = RevisionNotFoundException.class) public void testGetRevisionsOfPageNotFound(){ Set revisions = new HashSet(); when(revisionRepository.findAllRevisionOfPage("en_1")).thenReturn(revisions); revisionService.getRevisionsOfPage("en_1"); } } diff --git a/src/test/java/org/wikitolearn/wikirating/service/UserServiceTest.java b/src/test/java/org/wikitolearn/wikirating/service/UserServiceTest.java index 6e9759c..b445e47 100644 --- a/src/test/java/org/wikitolearn/wikirating/service/UserServiceTest.java +++ b/src/test/java/org/wikitolearn/wikirating/service/UserServiceTest.java @@ -1,169 +1,169 @@ /** * */ package org.wikitolearn.wikirating.service; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.wikitolearn.wikirating.exception.UserNotFoundException; -import org.wikitolearn.wikirating.model.Revision; -import org.wikitolearn.wikirating.model.User; +import org.wikitolearn.wikirating.model.graph.Revision; +import org.wikitolearn.wikirating.model.graph.User; import org.wikitolearn.wikirating.repository.RevisionRepository; import org.wikitolearn.wikirating.repository.UserRepository; import org.wikitolearn.wikirating.service.mediawiki.UserMediaWikiService; /** * @author aletundo * */ @RunWith(SpringJUnit4ClassRunner.class) public class UserServiceTest { @Mock private UserRepository userRepository; @Mock private RevisionRepository revisionRepository; @Mock private UserMediaWikiService userMediaWikiService; @InjectMocks private UserService userService; @Before public void setup(){ MockitoAnnotations.initMocks(this); } @Test public void testGetUser(){ User user = new User("User", 1, 0.0, 0.0, 0.0); when(userRepository.findByUserId(1)).thenReturn(user); User result = userService.getUser(1); assertEquals(1, result.getUserId()); assertEquals("User", result.getUsername()); assertEquals(0.0, result.getContributesReliability(), 0.1); assertEquals(0.0, result.getTotalReliability(), 0.1); assertEquals(0.0, result.getVotesReliability(), 0.1); } @Test(expected = UserNotFoundException.class) public void testGetUserNotFound(){ when(userRepository.findByUserId(1)).thenReturn(null); userService.getUser(1); } @Test public void testAddUsers(){ User u1 = new User("User", 1, 0.0, 0.0, 0.0); User u2 = new User("User2", 2, 0.0, 0.0, 0.0); User u3 = new User("User3", 3, 0.0, 0.0, 0.0); List users = new ArrayList<>(); users.add(u1); users.add(u2); users.add(u3); when(userRepository.save(users)).thenReturn(users); List result = userService.addUsers(users); assertEquals(3, result.size()); } @Test public void testSetAuthroshipSingleRevision(){ User user = new User("User", 1, 0.0, 0.0, 0.0); Revision revision = new Revision(2, "en", 1, 1, 123456, new Date()); when(userRepository.findByUserId(revision.getUserId())).thenReturn(user); userService.setAuthorship(revision); assertEquals(1, user.getAuthorship().size()); verify(userRepository, times(1)).save(user); } @Test public void testSetAuthroshipRevisionsGivenUser(){ User user = new User("User", 1, 0.0, 0.0, 0.0); Set revisions = new HashSet(); Revision r1 = new Revision(2, "en", 1, 1, 123456, new Date()); Revision r2 = new Revision(3, "en", 2, 1, 123456, new Date()); Revision r3 = new Revision(4, "en", 3, 1, 123456, new Date()); revisions.add(r1); revisions.add(r2); revisions.add(r3); userService.setAuthorship(revisions, user); assertEquals(3, user.getAuthorship().size()); verify(userRepository, times(1)).save(user); } @Test public void testInitUsers() throws InterruptedException, ExecutionException{ User u1 = new User("User", 1, 0.0, 0.0, 0.0); User u2 = new User("User2", 2, 0.0, 0.0, 0.0); User u3 = new User("User3", 3, 0.0, 0.0, 0.0); List users = new ArrayList<>(); users.add(u1); users.add(u2); users.add(u3); String apiUrl = "https://en.domain.org/api.php"; when(userMediaWikiService.getAll(apiUrl)).thenReturn(users); CompletableFuture result = userService.initUsers(apiUrl); verify(userRepository, times(1)).save(users); assertTrue(result.get()); } @Test public void testInitAuthorship() throws InterruptedException, ExecutionException{ User u1 = new User("User", 1, 0.0, 0.0, 0.0); User u2 = new User("User2", 2, 0.0, 0.0, 0.0); User u3 = new User("User3", 3, 0.0, 0.0, 0.0); List users = new ArrayList<>(); users.add(u1); users.add(u2); users.add(u3); Set revisionsU0 = new HashSet(); Set revisionsU1 = new HashSet(); Set revisionsU2 = new HashSet(); Set revisionsU3 = new HashSet(); Revision r1 = new Revision(2, "en", 1, 1, 123456, new Date()); Revision r2 = new Revision(3, "en", 2, 1, 123456, new Date()); Revision r3 = new Revision(4, "en", 3, 1, 123456, new Date()); Revision r4 = new Revision(5, "en", 4, 2, 123456, new Date()); Revision r5 = new Revision(6, "en", 5, 3, 123456, new Date()); Revision r6 = new Revision(7, "en", 6, 3, 123456, new Date()); Revision r7 = new Revision(8, "en", 7, 3, 123456, new Date()); Revision r8 = new Revision(9, "en", 0, 0, 123456, new Date()); revisionsU0.add(r8); revisionsU1.add(r1); revisionsU1.add(r2); revisionsU1.add(r3); revisionsU2.add(r4); revisionsU3.add(r5); revisionsU3.add(r6); revisionsU3.add(r7); when(userRepository.findAll()).thenReturn(users); when(revisionRepository.findByUserId(0)).thenReturn(revisionsU0); when(revisionRepository.findByUserId(1)).thenReturn(revisionsU1); when(revisionRepository.findByUserId(2)).thenReturn(revisionsU2); when(revisionRepository.findByUserId(3)).thenReturn(revisionsU3); CompletableFuture result = userService.initAuthorship(); assertTrue(result.get()); assertEquals(3, u1.getAuthorship().size()); assertEquals(1, u2.getAuthorship().size()); assertEquals(3, u3.getAuthorship().size()); } } \ No newline at end of file