Implement following stats

This commit is contained in:
syuilo 2018-10-21 17:51:35 +09:00
parent 81bdd97ca9
commit fa0d5e665f
4 changed files with 139 additions and 9 deletions

View File

@ -7,6 +7,7 @@ import renderFollow from '../../remote/activitypub/renderer/follow';
import renderAccept from '../../remote/activitypub/renderer/accept'; import renderAccept from '../../remote/activitypub/renderer/accept';
import { deliver } from '../../queue'; import { deliver } from '../../queue';
import createFollowRequest from './requests/create'; import createFollowRequest from './requests/create';
import { followingStats } from '../stats';
export default async function(follower: IUser, followee: IUser, requestId?: string) { export default async function(follower: IUser, followee: IUser, requestId?: string) {
// フォロー対象が鍵アカウントである or // フォロー対象が鍵アカウントである or
@ -52,6 +53,8 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri
}); });
//#endregion //#endregion
followingStats.update(follower, followee, true);
// Publish follow event // Publish follow event
if (isLocalUser(follower)) { if (isLocalUser(follower)) {
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed)); packUser(followee, follower).then(packed => publishMainStream(follower._id, 'follow', packed));

View File

@ -5,6 +5,7 @@ import pack from '../../remote/activitypub/renderer';
import renderFollow from '../../remote/activitypub/renderer/follow'; import renderFollow from '../../remote/activitypub/renderer/follow';
import renderUndo from '../../remote/activitypub/renderer/undo'; import renderUndo from '../../remote/activitypub/renderer/undo';
import { deliver } from '../../queue'; import { deliver } from '../../queue';
import { followingStats } from '../stats';
export default async function(follower: IUser, followee: IUser) { export default async function(follower: IUser, followee: IUser) {
const following = await Following.findOne({ const following = await Following.findOne({
@ -37,6 +38,8 @@ export default async function(follower: IUser, followee: IUser) {
}); });
//#endregion //#endregion
followingStats.update(follower, followee, false);
// Publish unfollow event // Publish unfollow event
if (isLocalUser(follower)) { if (isLocalUser(follower)) {
packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed)); packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed));

View File

@ -6,6 +6,7 @@ import renderAccept from '../../../remote/activitypub/renderer/accept';
import { deliver } from '../../../queue'; import { deliver } from '../../../queue';
import Following from '../../../models/following'; import Following from '../../../models/following';
import { publishMainStream } from '../../../stream'; import { publishMainStream } from '../../../stream';
import { followingStats } from '../../stats';
export default async function(followee: IUser, follower: IUser) { export default async function(followee: IUser, follower: IUser) {
await Following.insert({ await Following.insert({
@ -57,6 +58,8 @@ export default async function(followee: IUser, follower: IUser) {
}); });
//#endregion //#endregion
followingStats.update(follower, followee, true);
await User.update({ _id: followee._id }, { await User.update({ _id: followee._id }, {
$inc: { $inc: {
pendingReceivedFollowRequestsCount: -1 pendingReceivedFollowRequestsCount: -1

View File

@ -10,6 +10,7 @@ import Note, { INote } from '../models/note';
import User, { isLocalUser, IUser } from '../models/user'; import User, { isLocalUser, IUser } from '../models/user';
import DriveFile, { IDriveFile } from '../models/drive-file'; import DriveFile, { IDriveFile } from '../models/drive-file';
import { ICollection } from 'monk'; import { ICollection } from 'monk';
import Following from '../models/following';
type Obj = { [key: string]: any }; type Obj = { [key: string]: any };
@ -58,7 +59,7 @@ type Log<T extends Obj> = {
*/ */
abstract class Stats<T> { abstract class Stats<T> {
protected collection: ICollection<Log<T>>; protected collection: ICollection<Log<T>>;
protected abstract async generateTemplate(init: boolean, latestLog?: T): Promise<T>; protected abstract async getTemplate(init: boolean, latestLog?: T, group?: any): Promise<T>;
constructor(name: string) { constructor(name: string) {
this.collection = db.get<Log<T>>(`stats.${name}`); this.collection = db.get<Log<T>>(`stats.${name}`);
@ -127,7 +128,7 @@ abstract class Stats<T> {
if (latestLog) { if (latestLog) {
// 現在の統計を初期挿入 // 現在の統計を初期挿入
const data = await this.generateTemplate(false, latestLog.data); const data = await this.getTemplate(false, latestLog.data);
const log = await this.collection.insert({ const log = await this.collection.insert({
group: group, group: group,
@ -142,7 +143,7 @@ abstract class Stats<T> {
// * Misskeyインスタンスを建てて初めてのチャート更新時など // * Misskeyインスタンスを建てて初めてのチャート更新時など
// 空の統計を作成 // 空の統計を作成
const data = await this.generateTemplate(true); const data = await this.getTemplate(true, null, group);
const log = await this.collection.insert({ const log = await this.collection.insert({
group: group, group: group,
@ -237,7 +238,7 @@ abstract class Stats<T> {
promisedChart.unshift(Promise.resolve(log.data)); promisedChart.unshift(Promise.resolve(log.data));
} else { // 隙間埋め } else { // 隙間埋め
const latest = logs.find(l => l.date.getTime() < current.getTime()); const latest = logs.find(l => l.date.getTime() < current.getTime());
promisedChart.unshift(this.generateTemplate(false, latest ? latest.data : null)); promisedChart.unshift(this.getTemplate(false, latest ? latest.data : null));
} }
} }
@ -315,7 +316,7 @@ class UsersStats extends Stats<UsersLog> {
} }
@autobind @autobind
protected async generateTemplate(init: boolean, latestLog?: UsersLog): Promise<UsersLog> { protected async getTemplate(init: boolean, latestLog?: UsersLog): Promise<UsersLog> {
const [localCount, remoteCount] = init ? await Promise.all([ const [localCount, remoteCount] = init ? await Promise.all([
User.count({ host: null }), User.count({ host: null }),
User.count({ host: { $ne: null } }) User.count({ host: { $ne: null } })
@ -406,7 +407,7 @@ class NotesStats extends Stats<NotesLog> {
} }
@autobind @autobind
protected async generateTemplate(init: boolean, latestLog?: NotesLog): Promise<NotesLog> { protected async getTemplate(init: boolean, latestLog?: NotesLog): Promise<NotesLog> {
const [localCount, remoteCount] = init ? await Promise.all([ const [localCount, remoteCount] = init ? await Promise.all([
Note.count({ '_user.host': null }), Note.count({ '_user.host': null }),
Note.count({ '_user.host': { $ne: null } }) Note.count({ '_user.host': { $ne: null } })
@ -516,7 +517,7 @@ class DriveStats extends Stats<DriveLog> {
} }
@autobind @autobind
protected async generateTemplate(init: boolean, latestLog?: DriveLog): Promise<DriveLog> { protected async getTemplate(init: boolean, latestLog?: DriveLog): Promise<DriveLog> {
const calcSize = (local: boolean) => DriveFile const calcSize = (local: boolean) => DriveFile
.aggregate([{ .aggregate([{
$match: { $match: {
@ -628,7 +629,7 @@ class NetworkStats extends Stats<NetworkLog> {
} }
@autobind @autobind
protected async generateTemplate(init: boolean, latestLog?: NetworkLog): Promise<NetworkLog> { protected async getTemplate(init: boolean, latestLog?: NetworkLog): Promise<NetworkLog> {
return { return {
incomingRequests: 0, incomingRequests: 0,
outgoingRequests: 0, outgoingRequests: 0,
@ -671,7 +672,7 @@ class HashtagStats extends Stats<HashtagLog> {
} }
@autobind @autobind
protected async generateTemplate(init: boolean, latestLog?: HashtagLog): Promise<HashtagLog> { protected async getTemplate(init: boolean, latestLog?: HashtagLog): Promise<HashtagLog> {
return { return {
count: 0 count: 0
}; };
@ -689,4 +690,124 @@ class HashtagStats extends Stats<HashtagLog> {
export const hashtagStats = new HashtagStats(); export const hashtagStats = new HashtagStats();
//#endregion //#endregion
//#region Following stats
/**
*
*/
type FollowingLog = {
local: {
/**
*
*/
followings: {
/**
*
*/
total: number;
/**
*
*/
inc: number;
/**
*
*/
dec: number;
};
/**
*
*/
followers: {
/**
*
*/
total: number;
/**
*
*/
inc: number;
/**
*
*/
dec: number;
};
};
remote: FollowingLog['local'];
};
class FollowingStats extends Stats<FollowingLog> {
constructor() {
super('following');
}
@autobind
protected async getTemplate(init: boolean, latestLog?: FollowingLog, group?: any): Promise<FollowingLog> {
const [localFollowings, localFollowers, remoteFollowings, remoteFollowers] = init ? await Promise.all([
Following.count({ followerId: group, '_followee.host': null }),
Following.count({ followeeId: group, '_user.host': null }),
Following.count({ followerId: group, '_followee.host': { $ne: null } }),
Following.count({ followeeId: group, '_user.host': { $ne: null } })
]) : [
latestLog ? latestLog.local.followings.total : 0,
latestLog ? latestLog.local.followers.total : 0,
latestLog ? latestLog.remote.followings.total : 0,
latestLog ? latestLog.remote.followers.total : 0
];
return {
local: {
followings: {
total: localFollowings,
inc: 0,
dec: 0
},
followers: {
total: localFollowers,
inc: 0,
dec: 0
}
},
remote: {
followings: {
total: remoteFollowings,
inc: 0,
dec: 0
},
followers: {
total: remoteFollowers,
inc: 0,
dec: 0
}
}
};
}
@autobind
public async update(follower: IUser, followee: IUser, isFollow: boolean) {
const update: Obj = {};
update.total = isFollow ? 1 : -1;
if (isFollow) {
update.inc = 1;
} else {
update.dec = 1;
}
this.inc({
[isLocalUser(follower) ? 'local' : 'remote']: { followings: update }
});
this.inc({
[isLocalUser(followee) ? 'local' : 'remote']: { followers: update }
});
}
}
export const followingStats = new FollowingStats();
//#endregion //#endregion