src/JSerStat.js
// LICENSE : MIT
"use strict";
require('array.prototype.find');
import Item from "./models/JSerItem"
import Post from "./models/JSerPost"
import Week from "./models/JSerWeek"
import AlgoItem from "./algo/AlgoItem"
import AlgoPost from "./algo/AlgoPost.js"
import NaturalSearcher from "./natural/NaturalSearcher"
const sortBy = require("lodash.sortby");
function sortByDate(items) {
return sortBy(items, (item) => {
return item.date;
});
}
function filterJSerCategory(article) {
return /jser/i.test(article.category);
}
module.exports = class JSerStat {
constructor(rawItems, rawPosts) {
this._rawItems = rawItems;
this._rawPosts = rawPosts;
/**
* 日付で昇順にsortされたItems
* @type {JSerItem[]}
* */
this.items = sortByDate(this._rawItems).map(function(item) {
return new Item(item);
});
/**
* 日付で昇順にsortされてposts
* @type {JSerPost[]}
**/
this.posts = sortByDate(this._rawPosts)
.filter(filterJSerCategory)
.map((post, index) => {
return new Post(index + 1, post);
});
/**
*
* @type {JSerWeek[]}
* @private
*/
this._weeks = [];
/**
* @type {AlgoItem}
* @private
**/
this._algoItem = new AlgoItem(this.items);
/**
* @type {AlgoPost}
* @private
*/
this._algoPost = new AlgoPost(this.posts);
/**
* @type {NaturalSearcher}
*/
this.naturalSearch = null;
}
/**
* 全部で何週あるかを返す(投稿記事の数と一致)
* @returns {number}
*/
getTotalWeekCount() {
return this.posts.length;
}
/**
* beginからendの範囲のJSerItemの配列を返す
* @param {Date} beginDate
* @param {Date} endDate
* @returns {JSerItem[]}
*/
findItemsBetween(beginDate, endDate) {
return this._algoItem.findItemsBetween(beginDate, endDate);
}
// deprecated
getItemsBetWeen(beginDate, endDate) {
return this.findItemsBetween(beginDate, endDate)
}
/**
* 全てのJSerWeekの配列を返す
* @returns {JSerWeek[]}
*/
getJSerWeeks() {
if (this._weeks.length === 0) {
this._weeks = this.posts.reduce((results, currentPost, index) => {
var prevPost = this.posts[index - 1];
var jserWeek = new Week(currentPost, prevPost, this._algoItem);
results.push(jserWeek);
return results;
}, []);
}
return this._weeks;
}
/**
* beginからendの範囲に含まれるJSerWeekの配列を返す
* JSerWeek#beginDate または JSerWeek#endDate どちらかがかかれば含まれると判断される
* @param {Date} beginDate
* @param {Date} endDate
* @returns {JSerWeek[]}
*/
findJSerWeeksBetween(beginDate, endDate) {
const weeks = this.getJSerWeeks();
const beginTime = beginDate.getTime();
const endTime = endDate.getTime();
return weeks.filter(week => {
const weekBeginTime = week.beginDate.getTime();
const weekEndTime = week.endDate.getTime();
if (beginTime <= weekBeginTime && weekBeginTime <= endTime) {
return true;
}
if (beginTime <= weekEndTime && weekEndTime <= endTime) {
return true;
}
return false;
});
}
// deprecated
getJSerWeeksBetWeen(beginDate, endDate) {
return this.findJSerWeeksBetween(beginDate, endDate)
}
/**
* JSer.info #xxx を返す
* @param {number} number number start with 1
* @returns {JSerWeek}
*/
findJSerWeek(number) {
if (number <= 0) {
throw new Error(`number:${number} should be >= 1`);
}
if (number > this.posts.length) {
return null;
}
var targetPost = this.posts[number - 1];
var prevPost = this.posts[number - 2];
return new Week(targetPost, prevPost, this._algoItem);
}
/**
* `postURL`に一致するJSerWeekを返す
* @param {string} postURL
* @returns {JSerWeek}
*/
findJSerWeekWithURL(postURL) {
const weeks = this.getJSerWeeks().filter(week => {
return week.post.url === postURL;
});
if (weeks.length > 0) {
return weeks[0];
}
}
// deprecated
getJSerWeek(number) {
return this.findJSerWeek(number);
}
/**
* JSerItemを含んでいるJSerWeekを検索して返す.
* @param {Object} jserItem the jserItem is raw object for JSerItem
* @return {JSerWeek|null} The week contain this jserItem.
* 未来の記事などJSerWeekに所属していない場合もある
*/
findWeekWithItem(jserItem) {
const targetItem = new Item(jserItem);
const tenDaysAfter = new Date(targetItem.date);
tenDaysAfter.setDate(targetItem.date.getDate() + 10);
const jSerWeeks = this.findJSerWeeksBetween(targetItem.date, tenDaysAfter);
return jSerWeeks.find(week => {
if (week.post.date < targetItem.date) {
return false;
}
return week.items.some(item => {
return targetItem.isEqualItem(item);
});
});
}
/**
* URLとマッチするJSerItemを返す
* @param {string} URL
* @return {JSerItem}
*/
findItemWithURL(URL) {
return this.items.find(item => {
return item.url === URL;
});
}
/**
* `item` と関連するJSerItemの配列を返す
* @param {JSerItem} item
* @param {number} limit
* @returns {JSerItem[]}
*/
findRelatedItems(item, limit = 10) {
if (this.naturalSearch == null) {
this.naturalSearch = new NaturalSearcher(this.items);
}
return this.naturalSearch.findRelatedItems(item, limit);
}
}