diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java new file mode 100644 index 0000000000000000000000000000000000000000..646c64fde3fa65c2490d606f71e123751f4824fa --- /dev/null +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java @@ -0,0 +1,104 @@ +package de.vipra.rest.resource; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletContext; +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import de.vipra.rest.Messages; +import de.vipra.rest.model.APIError; +import de.vipra.rest.model.ResponseWrapper; +import de.vipra.util.Config; +import de.vipra.util.StringUtils; +import de.vipra.util.ex.ConfigException; +import de.vipra.util.model.TopicModel; +import de.vipra.util.model.WordFull; +import de.vipra.util.service.MongoService; +import de.vipra.util.service.Service.QueryBuilder; + +@Path("words") +public class WordResource { + + @Context + UriInfo uri; + + final MongoService<WordFull, String> dbWords; + + public WordResource(@Context final ServletContext servletContext) throws ConfigException, IOException { + final Config config = Config.getConfig(); + dbWords = MongoService.getDatabaseService(config, WordFull.class); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getWords(@QueryParam("topicModel") final String topicModel, @QueryParam("skip") final Integer skip, + @QueryParam("limit") final Integer limit, @QueryParam("sort") @DefaultValue("id") final String sortBy, + @QueryParam("fields") final String fields) { + final ResponseWrapper<List<WordFull>> res = new ResponseWrapper<>(); + + if (res.hasErrors()) + return res.badRequest(); + + try { + final QueryBuilder query = QueryBuilder.builder().skip(skip).limit(limit).sortBy(sortBy); + if (fields != null && !fields.isEmpty()) + query.fields(true, StringUtils.getFields(fields)); + + if (topicModel != null && !topicModel.isEmpty()) + query.criteria("topicModel", new TopicModel(topicModel)); + + final List<WordFull> words = dbWords.getMultiple(query); + + if ((skip != null && skip > 0) || (limit != null && limit > 0)) + res.addHeader("total", dbWords.count(null)); + else + res.addHeader("total", words.size()); + + return res.ok(words); + } catch (final Exception e) { + e.printStackTrace(); + res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage())); + return res.badRequest(); + } + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("{id}") + public Response getWord(@PathParam("id") final String id, @QueryParam("fields") final String fields) { + final ResponseWrapper<WordFull> res = new ResponseWrapper<>(); + if (id == null || id.trim().length() == 0) { + res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty", String.format(Messages.BAD_REQUEST, "id cannot be empty"))); + return res.badRequest(); + } + + WordFull word; + try { + word = dbWords.getSingle(id, StringUtils.getFields(fields)); + } catch (final Exception e) { + e.printStackTrace(); + res.addError(new APIError(Response.Status.BAD_REQUEST, "Error", e.getMessage())); + return res.badRequest(); + } + + if (word != null) { + return res.ok(word); + } else { + res.addError(new APIError(Response.Status.NOT_FOUND, "Resource not found", String.format(Messages.NOT_FOUND, "word", id))); + return res.notFound(); + } + } + +} diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/file/Filebase.java b/vipra-cmd/src/main/java/de/vipra/cmd/file/Filebase.java index 09b461eb8d916b47852c879976e4c5721744a51b..09141da0ab88216799fc84f4a0103eed44dc8242 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/file/Filebase.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/file/Filebase.java @@ -97,4 +97,16 @@ public class Filebase { windowIndex.sync(); } + public FilebaseIDDateIndex getIdDateIndex() { + return idDateIndex; + } + + public FilebaseWordIndex getWordIndex() { + return wordIndex; + } + + public FilebaseWindowIndex getWindowIndex() { + return windowIndex; + } + } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseWordIndex.java b/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseWordIndex.java index fc1cb496dd7d6f8d5cb08796b0d478f4fa02ae07..c939f635cdd9dfcd0e6bb2e6b64ceb1563776160 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseWordIndex.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/file/FilebaseWordIndex.java @@ -7,10 +7,12 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import de.vipra.util.Constants; import de.vipra.util.CountMap; @@ -26,10 +28,12 @@ public class FilebaseWordIndex implements Iterable<String> { private final List<String> words; private final Map<String, Integer> wordIndex; private final CountMap<String> wordDocumentCount; + private final Set<String> newWords; private int nextIndex = 0; public FilebaseWordIndex(final File modelDir) throws IOException { file = new File(modelDir, FILE_NAME); + newWords = new HashSet<>(); if (file.exists()) { final List<String> lines = FileUtils.readFile(file); words = new ArrayList<>(lines.size()); @@ -51,8 +55,8 @@ public class FilebaseWordIndex implements Iterable<String> { public void sync() throws IOException { if (!dirty) return; - BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false))); - for (String word : words) { + final BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false))); + for (final String word : words) { out.write(word); out.write(","); out.write(Integer.toString(wordDocumentCount.get(word))); @@ -63,14 +67,16 @@ public class FilebaseWordIndex implements Iterable<String> { } public void countWords(final List<ArticleWord> articleWords) { - for (ArticleWord articleWord : articleWords) - wordDocumentCount.count(articleWord.getWord()); + for (final ArticleWord articleWord : articleWords) + wordDocumentCount.count(articleWord.getId()); } public String transform(final String[] words) { final CountMap<String> countMap = new CountMap<>(); - for (final String word : words) + for (final String word : words) { countMap.count(word); + newWords.add(word); + } final StringBuilder sb = new StringBuilder(); sb.append(countMap.size()); @@ -107,6 +113,10 @@ public class FilebaseWordIndex implements Iterable<String> { return words.size(); } + public Set<String> getNewWords() { + return newWords; + } + @Override public Iterator<String> iterator() { return words.iterator(); diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java index 57775a174799df32a197e30ae759310aa9e20209..1882c49eda2e72ffba6e3640f24725a808c14765 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java @@ -300,11 +300,11 @@ public class Analyzer { final List<TopicWord> newTopicWords = new ArrayList<>(modelConfig.getkTopWords()); for (final SequenceWord sequenceWord : topTopicWordsList) { final TopicWord newTopicWord = new TopicWord(); - newTopicWord.setWord(sequenceWord.getWord()); + newTopicWord.setId(sequenceWord.getId()); final List<Double> sequenceProbabilities = new ArrayList<>(windowCount); final List<Double> sequenceProbabilitiesChange = new ArrayList<>(windowCount); double prevProbability = 0; - for (final double probability : probabilities[wordIndex.index(sequenceWord.getWord())]) { + for (final double probability : probabilities[wordIndex.index(sequenceWord.getId())]) { sequenceProbabilities.add(probability); sequenceProbabilitiesChange.add(probability - prevProbability); prevProbability = probability; diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java index 8c771a0a0835c1e2fd10e10c631ea259aedf5f19..af9f91b3a7bb56fda75ff772459da7d79e3987db 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/DeleteModelCommand.java @@ -3,10 +3,18 @@ package de.vipra.cmd.option; import java.io.File; import java.util.Arrays; +import org.bson.types.ObjectId; + import de.vipra.util.Config; import de.vipra.util.ConsoleUtils; +import de.vipra.util.model.ArticleFull; +import de.vipra.util.model.SequenceFull; +import de.vipra.util.model.TopicFull; import de.vipra.util.model.TopicModel; +import de.vipra.util.model.WindowFull; +import de.vipra.util.model.WordFull; import de.vipra.util.service.MongoService; +import de.vipra.util.service.Service.QueryBuilder; public class DeleteModelCommand implements Command { @@ -20,6 +28,11 @@ public class DeleteModelCommand implements Command { public void run() throws Exception { final Config config = Config.getConfig(); final MongoService<TopicModel, String> dbTopicModels = MongoService.getDatabaseService(config, TopicModel.class); + final MongoService<ArticleFull, ObjectId> dbArticles = MongoService.getDatabaseService(config, ArticleFull.class); + final MongoService<TopicFull, ObjectId> dbTopics = MongoService.getDatabaseService(config, TopicFull.class); + final MongoService<WindowFull, Integer> dbWindows = MongoService.getDatabaseService(config, WindowFull.class); + final MongoService<SequenceFull, ObjectId> dbSequences = MongoService.getDatabaseService(config, SequenceFull.class); + final MongoService<WordFull, String> dbWords = MongoService.getDatabaseService(config, WordFull.class); for (final String name : names) { final File modelDir = new File(config.getDataDirectory(), name); @@ -28,7 +41,17 @@ public class DeleteModelCommand implements Command { ConsoleUtils.info("model deleted: " + name); } } + dbTopicModels.deleteMultiple(Arrays.asList(names)); + + for (final String name : names) { + final QueryBuilder builder = QueryBuilder.builder().criteria("topicModel", new TopicModel(name)); + dbArticles.deleteMultiple(builder); + dbTopics.deleteMultiple(builder); + dbWindows.deleteMultiple(builder); + dbSequences.deleteMultiple(builder); + dbWords.deleteMultiple(builder); + } } } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java index 17a18c6cccc48ef0dff6613698e000cc56022d8d..c6220dcc38b1440be8d4dd498fe3b7ddd6b64d88 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java @@ -17,6 +17,7 @@ import org.json.simple.parser.ParseException; import de.vipra.cmd.file.Filebase; import de.vipra.cmd.file.FilebaseException; +import de.vipra.cmd.file.FilebaseWordIndex; import de.vipra.cmd.text.ProcessedText; import de.vipra.cmd.text.Processor; import de.vipra.cmd.text.ProcessorException; @@ -37,6 +38,7 @@ import de.vipra.util.model.TextEntity; import de.vipra.util.model.TopicModel; import de.vipra.util.model.TopicModelConfig; import de.vipra.util.model.TopicModelFull; +import de.vipra.util.model.WordFull; import de.vipra.util.service.MongoService; public class ImportCommand implements Command { @@ -71,6 +73,7 @@ public class ImportCommand implements Command { private Config config; private MongoService<ArticleFull, ObjectId> dbArticles; private MongoService<TopicModelFull, String> dbTopicModels; + private MongoService<WordFull, String> dbWords; private TopicModelConfig modelConfig; private SpotlightAnalyzer spotlightAnalyzer; private Filebase filebase; @@ -260,6 +263,19 @@ public class ImportCommand implements Command { */ filebase.sync(); + /* + * add new words + */ + final FilebaseWordIndex wordIndex = filebase.getWordIndex(); + final List<WordFull> newWords = new ArrayList<>(wordIndex.getNewWords().size()); + final TopicModel topicModelRef = new TopicModel(topicModel.getId()); + for (final String word : wordIndex.getNewWords()) { + final WordFull newWord = new WordFull(word); + newWord.setTopicModel(topicModelRef); + newWords.add(newWord); + } + dbWords.createMultiple(newWords); + /* * run information */ @@ -271,6 +287,7 @@ public class ImportCommand implements Command { config = Config.getConfig(); dbArticles = MongoService.getDatabaseService(config, ArticleFull.class); dbTopicModels = MongoService.getDatabaseService(config, TopicModelFull.class); + dbWords = MongoService.getDatabaseService(config, WordFull.class); processor = new Processor(); for (final TopicModelConfig modelConfig : config.getTopicModelConfigs(models)) { importForModel(modelConfig); diff --git a/vipra-ui/app/html/directives/word-link.html b/vipra-ui/app/html/directives/word-link.html index 0c2dff7ccf68eafa959c3e130c27b1e41e741286..73cc32b89ac34457edabe71bfba083099e2b9c45 100644 --- a/vipra-ui/app/html/directives/word-link.html +++ b/vipra-ui/app/html/directives/word-link.html @@ -1,4 +1,4 @@ <span> <word-menu word="word" /> - <span ng-bind="word.word"></span> + <a ui-sref="words.show({id: word.id})" ng-bind="word.id"></a> </span> \ No newline at end of file diff --git a/vipra-ui/app/html/directives/word-menu.html b/vipra-ui/app/html/directives/word-menu.html index fe6a7ef3940ebc348eded14b24a8384d20429333..103531b1bdf35424d72d8e2f229ec0b716aac5f8 100644 --- a/vipra-ui/app/html/directives/word-menu.html +++ b/vipra-ui/app/html/directives/word-menu.html @@ -3,7 +3,7 @@ <i class="fa fa-caret-down"></i> </a> <ul class="dropdown-menu" ng-class="{'dropdown-menu-right':dropdownRight}"> - <li><a ui-sref="words.topics({word:word.word})">Topics</a></li> - <li><a ui-sref="words.articles({word:word.word})">Articles</a></li> + <li><a ui-sref="words.show.topics({id:word.id})">Topics</a></li> + <li><a ui-sref="words.show.articles({id:word.id})">Articles</a></li> </ul> </div> \ No newline at end of file diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html index 387f7bda18f177dba20300c994944cfe6e64aeae..5c74517925bcaa8a75c9497a4d49374503e9157e 100644 --- a/vipra-ui/app/html/topics/show.html +++ b/vipra-ui/app/html/topics/show.html @@ -90,8 +90,8 @@ <ul class="list-unstyled"> <li ng-repeat="word in topic.words"> <div class="checkbox checkbox-condensed" ng-class="{selected:word.selected}"> - <input tabindex="0" type="checkbox" ng-model="word.selected" ng-attr-id="{{::word.word}}" ng-change="redrawWordEvolutionChart()"> - <label class="check" ng-attr-for="{{::word.word}}"> + <input tabindex="0" type="checkbox" ng-model="word.selected" ng-attr-id="{{::word.id}}" ng-change="redrawWordEvolutionChart()"> + <label class="check" ng-attr-for="{{::word.id}}"> <word-link word="word" /> </label> </div> @@ -123,8 +123,7 @@ <tbody> <tr ng-repeat="word in sequence.words | orderBy:topicsShowModels.seqSortWords"> <td> - <word-menu word="word"/> - <span ng-bind="::word.word"></span> + <word-link word="word" /> </td> <td ng-bind="word.probability.toFixed(4)"></td> </tr> diff --git a/vipra-ui/app/html/words/articles.html b/vipra-ui/app/html/words/articles.html index 03dce56951193bb886e94512579c39a675f4f5e5..3674b93e2fb6e7888564e4ca49c9988f4feed8b0 100644 --- a/vipra-ui/app/html/words/articles.html +++ b/vipra-ui/app/html/words/articles.html @@ -1,4 +1,4 @@ -<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words.articles'"> +<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words.show.articles'"> <div class="row"> <div class="col-md-12"> <div class="page-header"> diff --git a/vipra-ui/app/html/words/index.html b/vipra-ui/app/html/words/index.html new file mode 100644 index 0000000000000000000000000000000000000000..16a03a7f1a5257919683c26f594ec07c999660a3 --- /dev/null +++ b/vipra-ui/app/html/words/index.html @@ -0,0 +1,42 @@ +<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words'"> + <div class="row"> + <div class="col-md-12 text-center"> + <pagination total="wordsTotal" page="wordsIndexModels.page" limit="wordsIndexModels.limit" /> + </div> + </div> + <div class="row"> + <div class="col-md-12"> + <div class="panel panel-default"> + <div class="panel-heading"> + Found + <ng-pluralize count="wordsTotal||0" when="{0:'no words',1:'1 word',other:'{} words'}"></ng-pluralize> in the database. + <span ng-show="wordsTotal"> + Sort by + <ol class="nya-bs-select nya-bs-condensed" ng-model="wordsIndexModels.sortkey"> + <li value="id" class="nya-bs-option"><a>Word</a></li> + </ol> + <sort-dir ng-model="wordsIndexModels.sortdir" /> + </span> + </div> + <table class="table table-hover table-condensed"> + <tbody> + <tr ng-repeat="word in words"> + <td> + <word-link word="word" /> + </td> + </tr> + </tbody> + </table> + <div class="panel-footer"> + Page <span ng-bind="wordsIndexModels.page||1"></span> of <span ng-bind="maxPage||1"></span> + </div> + </div> + </div> + </div> + <div class="row"> + <div class="col-md-12 text-center"> + <pagination total="wordsTotal" page="wordsIndexModels.page" limit="wordsIndexModels.limit" /> + </div> + </div> +</div> +<div ng-cloak ui-view></div> diff --git a/vipra-ui/app/html/words/show.html b/vipra-ui/app/html/words/show.html new file mode 100644 index 0000000000000000000000000000000000000000..515f6e9c2fecc07d0d800cdd89681a47a92ce6e0 --- /dev/null +++ b/vipra-ui/app/html/words/show.html @@ -0,0 +1,4 @@ +<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words.show'"> + <h1 ng-bind="word.id"></h1> +</div> +<div ng-cloak ui-view></div> diff --git a/vipra-ui/app/html/words/topics.html b/vipra-ui/app/html/words/topics.html index 46a20f18289a83eeeaf1dae8f614fa67aac1f987..f96c969688c897993e79b626cf2ed51319a43aea 100644 --- a/vipra-ui/app/html/words/topics.html +++ b/vipra-ui/app/html/words/topics.html @@ -1,4 +1,4 @@ -<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words.topics'"> +<div class="container" ng-cloak ng-hide="!rootModels.topicModel || state.name !== 'words.show.topics'"> <div class="row"> <div class="col-md-12"> <div class="page-header"> diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html index fb7e6846ca7daaa0f0dacfd6d6abe7e6d21956a8..ddb769d732a3e0a1fbc3b308571acac77027fd50 100644 --- a/vipra-ui/app/index.html +++ b/vipra-ui/app/index.html @@ -64,6 +64,9 @@ <li ui-sref-active="active"> <a tabindex="0" ui-sref="topics"><span class="mnemonic">T</span>opics</a> </li> + <li ui-sref-active="active"> + <a tabindex="0" ui-sref="words"><span class="mnemonic">W</span>ords</a> + </li> </ul> <form class="navbar-form navbar-left" role="search" ng-hide="state.name === 'index'"> <div class="form-group has-feedback"> diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index 94aefbb11a67505e3cabaf212b6a8e24960174e5..64b34a2d4b85c76a288ad9f635509bc89cfe524c 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -91,18 +91,24 @@ // states: words $stateProvider.state('words', { - abstract: true, - url: '/words/:word', - template: '<ui-view/>' + url: '/words?p', + templateUrl: 'html/words/index.html', + controller: 'WordsIndexController' }); - $stateProvider.state('words.topics', { + $stateProvider.state('words.show', { + url: '/:id', + templateUrl: 'html/words/show.html', + controller: 'WordsShowController' + }); + + $stateProvider.state('words.show.topics', { url: '/topics', templateUrl: 'html/words/topics.html', controller: 'WordsTopicsController' }); - $stateProvider.state('words.articles', { + $stateProvider.state('words.show.articles', { url: '/articles', templateUrl: 'html/words/articles.html', controller: 'WordsArticlesController' diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index d466d9afb7c06207c25c94eda0d2aaac62b87d27..92b9c03c8acd3003408ff37b0829dfc52643d8e4 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -9,8 +9,8 @@ var app = angular.module('vipra.controllers', []); - app.controller('RootController', ['$scope', '$state', '$location', '$window', 'hotkeys', 'TopicModelFactory', - function($scope, $state, $location, $window, hotkeys, TopicModelFactory) { + app.controller('RootController', ['$scope', '$state', '$window', 'hotkeys', 'TopicModelFactory', + function($scope, $state, $window, hotkeys, TopicModelFactory) { $scope.rootModels = { topicModel: null, @@ -94,6 +94,15 @@ } }); + hotkeys.add({ + combo: 'w', + description: 'Go to words', + callback: function() { + if ($scope.rootModels.topicModel && $state.current.name !== 'words') + $state.transitionTo('words'); + } + }); + hotkeys.add({ combo: 'm', description: 'Choose a topic model', @@ -803,7 +812,7 @@ probs.push([new Date($scope.topic.sequences[j].window.startDate).getTime(), prob]); } evolutions.push({ - name: word.word, + name: word.id, data: probs }); } @@ -904,13 +913,13 @@ /** * Topic Show Articles route */ - app.controller('TopicsArticlesController', ['$scope', '$stateParams', '$location', 'TopicFactory', - function($scope, $stateParams, $location, TopicFactory) { + app.controller('TopicsArticlesController', ['$scope', '$stateParams', 'TopicFactory', + function($scope, $stateParams, TopicFactory) { $scope.topicsArticlesModels = { sortkey: 'title', sortdir: true, - page: Math.max($location.search().page || 1, 1), + page: 1, limit: 100 }; @@ -934,10 +943,48 @@ * Word Controllers ****************************************************************************/ + app.controller('WordsIndexController', ['$scope', '$state', 'WordFactory', + function($scope, $state, WordFactory) { + + // page was reloaded, choose topic model + if (!$scope.rootModels.topicModel && $state.current.name === 'words') + $scope.chooseTopicModel(); + + $scope.wordsIndexModels = { + sortkey: 'id', + sortdir: true, + page: 1, + limit: 100 + }; + + $scope.$watchGroup(['wordsIndexModels.page', 'wordsIndexModels.sortkey', 'wordsIndexModels.sortdir', 'rootModels.topicModel'], function() { + if (!$scope.rootModels.topicModel) return; + + WordFactory.query({ + topicModel: $scope.rootModels.topicModel.id, + skip: ($scope.wordsIndexModels.page - 1) * $scope.wordsIndexModels.limit, + limit: $scope.wordsIndexModels.limit, + sort: ($scope.wordsIndexModels.sortdir ? '' : '-') + $scope.wordsIndexModels.sortkey + }, function(data, headers) { + $scope.words = data; + $scope.wordsTotal = headers("V-Total"); + $scope.maxPage = Math.ceil($scope.wordsTotal / $scope.wordsIndexModels.limit); + }); + }); + + } + ]); + + app.controller('WordsShowController', ['$scope', + function($scope) { + + } + ]); + app.controller('WordsTopicsController', ['$scope', '$state', '$stateParams', 'TopicFactory', function($scope, $state, $stateParams, TopicFactory) { - $scope.word = $stateParams.word; + $scope.word = $stateParams.id; // page was reloaded, choose topic model if (!$scope.rootModels.topicModel && $state.current.name === 'words.topics') @@ -972,7 +1019,7 @@ app.controller('WordsArticlesController', ['$scope', '$state', '$stateParams', 'ArticleFactory', function($scope, $state, $stateParams, ArticleFactory) { - $scope.word = $stateParams.word; + $scope.word = $stateParams.id; // page was reloaded, choose topic model if (!$scope.rootModels.topicModel && $state.current.name === 'words.articles') diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js index 31f9b64b4016ec220007ca70220b2990cc375541..b331235d3af51ceed292382395325e7a49d0eb7d 100644 --- a/vipra-ui/app/js/factories.js +++ b/vipra-ui/app/js/factories.js @@ -72,4 +72,8 @@ return $myResource(Vipra.config.restUrl + '/topicmodels/:id'); }]); + app.factory('WordFactory', ['$myResource', function($myResource) { + return $myResource(Vipra.config.restUrl + '/words/:id'); + }]); + })(); \ No newline at end of file diff --git a/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java b/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java index 96c0407fb9722640778574c0fb2ab6e39a9c2e1d..3feb126a7662f10e97a0b0cff905e01ad2a992cc 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/ArticleFull.java @@ -175,7 +175,7 @@ public class ArticleFull implements Model<ObjectId>, Serializable { public void setTopics(final List<TopicShare> topics) { this.topics = topics; - this.topicsCount = topics == null ? 0 : topics.size(); + topicsCount = topics == null ? 0 : topics.size(); } public int getTopicsCount() { diff --git a/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java b/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java index f0fc0cab9160b32f79601e622d08e5566b01d253..7aa0f7becc05544b61d6a0411e7e54f1807c51d6 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/ArticleWord.java @@ -11,23 +11,23 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Embedded public class ArticleWord implements Comparable<ArticleWord>, Serializable { - private String word; + private String id; private Integer count; public ArticleWord() {} - public ArticleWord(final String word, final int count) { - this.word = word; + public ArticleWord(final String id, final int count) { + this.id = id; this.count = count; } - public String getWord() { - return word; + public String getId() { + return id; } - public void setWord(final String word) { - this.word = word; + public void setId(final String id) { + this.id = id; } public Integer getCount() { @@ -45,7 +45,7 @@ public class ArticleWord implements Comparable<ArticleWord>, Serializable { @Override public String toString() { - return "ArticleWord [word=" + word + ", count=" + count + "]"; + return "ArticleWord [id=" + id + ", count=" + count + "]"; } } diff --git a/vipra-util/src/main/java/de/vipra/util/model/SequenceWord.java b/vipra-util/src/main/java/de/vipra/util/model/SequenceWord.java index 7499f8b8da606ff71dea08bd1274941d90638916..3e1637741e1c95ff6d2b1f2f914e5f319f3a7186 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/SequenceWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/SequenceWord.java @@ -11,23 +11,23 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @Embedded public class SequenceWord implements Comparable<SequenceWord>, Serializable { - private String word; + private String id; private Double probability; public SequenceWord() {} public SequenceWord(final String word, final Double probability) { - this.word = word; + id = word; this.probability = probability; } - public String getWord() { - return word; + public String getId() { + return id; } - public void setWord(final String word) { - this.word = word; + public void setId(final String id) { + this.id = id; } public Double getProbability() { @@ -45,14 +45,14 @@ public class SequenceWord implements Comparable<SequenceWord>, Serializable { @Override public String toString() { - return "SequenceWord [word=" + word + ", probability=" + probability + "]"; + return "SequenceWord [id=" + id + ", probability=" + probability + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((word == null) ? 0 : word.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @@ -65,10 +65,10 @@ public class SequenceWord implements Comparable<SequenceWord>, Serializable { if (getClass() != obj.getClass()) return false; final SequenceWord other = (SequenceWord) obj; - if (word == null) { - if (other.word != null) + if (id == null) { + if (other.id != null) return false; - } else if (!word.equals(other.word)) + } else if (!id.equals(other.id)) return false; return true; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java index c687c3b228bf545559c00c45006f3ca11ea6bd10..337e3b88c5265f97dbd237fdd43218e206e1ea29 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java @@ -154,7 +154,7 @@ public class TopicFull implements Model<ObjectId>, Serializable { return articlesCount; } - public void setArticlesCount(int articlesCount) { + public void setArticlesCount(final int articlesCount) { this.articlesCount = articlesCount; } @@ -187,7 +187,7 @@ public class TopicFull implements Model<ObjectId>, Serializable { final int size = Math.min(wordsNum, words.size()); final List<String> topWords = new ArrayList<>(size); for (int i = 0; i < size; i++) { - topWords.add(words.get(i).getWord()); + topWords.add(words.get(i).getId()); } name = StringUtils.join(topWords); } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java index 58dc063a49b2f7ed0f7f584b91228c2bfbae8714..e0dee92240bc839ad886df3563629761a8cca3b4 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelConfig.java @@ -166,7 +166,7 @@ public class TopicModelConfig implements Serializable { return minTopicShare; } - public void setMinTopicShare(double minTopicShare) { + public void setMinTopicShare(final double minTopicShare) { this.minTopicShare = minTopicShare; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java index 308336f42f465f2513065ae56635e187bb1f7017..a12ee4fede85b97f16863a448a06ad9b8af6286b 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicModelFull.java @@ -98,7 +98,7 @@ public class TopicModelFull implements Model<String>, Comparable<TopicModelFull> return lastGenerated; } - public void setLastGenerated(Date lastGenerated) { + public void setLastGenerated(final Date lastGenerated) { this.lastGenerated = lastGenerated; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java index 74bbe6cf814e31d8bc606ab8c363268bad171741..6fa9aaf2c12cb57278347ce9867d0b07de655bf6 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java @@ -9,18 +9,18 @@ import org.mongodb.morphia.annotations.Embedded; @Embedded public class TopicWord implements Comparable<TopicWord>, Serializable { - private String word; + private String id; private List<Double> sequenceProbabilities; private List<Double> sequenceProbabilitiesChange; - public String getWord() { - return word; + public String getId() { + return id; } - public void setWord(final String word) { - this.word = word; + public void setId(final String id) { + this.id = id; } public List<Double> getSequenceProbabilities() { @@ -35,13 +35,13 @@ public class TopicWord implements Comparable<TopicWord>, Serializable { return sequenceProbabilitiesChange; } - public void setSequenceProbabilitiesChange(List<Double> sequenceProbabilitiesChange) { + public void setSequenceProbabilitiesChange(final List<Double> sequenceProbabilitiesChange) { this.sequenceProbabilitiesChange = sequenceProbabilitiesChange; } @Override public int compareTo(final TopicWord o) { - return word.compareTo(o.getWord()); + return id.compareTo(o.getId()); } } diff --git a/vipra-util/src/main/java/de/vipra/util/model/WordFull.java b/vipra-util/src/main/java/de/vipra/util/model/WordFull.java new file mode 100644 index 0000000000000000000000000000000000000000..504deeef1dd27080d49d8e03b69f0fca497bbbf1 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/model/WordFull.java @@ -0,0 +1,51 @@ +package de.vipra.util.model; + +import java.io.Serializable; + +import org.mongodb.morphia.annotations.Entity; +import org.mongodb.morphia.annotations.Id; +import org.mongodb.morphia.annotations.Reference; + +import de.vipra.util.an.QueryIgnore; + +@SuppressWarnings("serial") +@Entity(value = "words", noClassnameStored = true) +public class WordFull implements Model<String>, Comparable<WordFull>, Serializable { + + @Id + private String id; + + @Reference + @QueryIgnore(multi = true) + private TopicModel topicModel; + + public WordFull() {} + + public WordFull(final String word) { + id = word; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(final String id) { + this.id = id; + } + + public TopicModel getTopicModel() { + return topicModel; + } + + public void setTopicModel(final TopicModel topicModel) { + this.topicModel = topicModel; + } + + @Override + public int compareTo(final WordFull o) { + return id.compareTo(o.getId()); + } + +}