Implement #771
This commit is contained in:
parent
99db09b702
commit
5c37b9cef3
5 changed files with 172 additions and 0 deletions
|
@ -2,6 +2,10 @@ ChangeLog (Release Notes)
|
|||
=========================
|
||||
主に notable な changes を書いていきます
|
||||
|
||||
unreleased
|
||||
----------
|
||||
* New: ユーザーページによく使うドメインを表示 (#771)
|
||||
|
||||
2566 (2017/09/07)
|
||||
-----------------
|
||||
* New: 投稿することの多いキーワードをユーザーページに表示する (#768)
|
||||
|
|
|
@ -498,6 +498,7 @@ mobile:
|
|||
images: "Images"
|
||||
activity: "Activity"
|
||||
keywords: "Keywords"
|
||||
domains: "Domains"
|
||||
followers-you-know: "Followers you know"
|
||||
last-used-at: "Latest used at"
|
||||
|
||||
|
@ -512,6 +513,9 @@ mobile:
|
|||
mk-user-overview-keywords:
|
||||
no-keywords: "No keywords"
|
||||
|
||||
mk-user-overview-domains:
|
||||
no-domains: "No domains"
|
||||
|
||||
mk-user-overview-followers-you-know:
|
||||
loading: "Loading"
|
||||
no-users: "No users"
|
||||
|
|
|
@ -498,6 +498,7 @@ mobile:
|
|||
images: "画像"
|
||||
activity: "アクティビティ"
|
||||
keywords: "キーワード"
|
||||
domains: "頻出ドメイン"
|
||||
followers-you-know: "知り合いのフォロワー"
|
||||
last-used-at: "最終ログイン"
|
||||
|
||||
|
@ -512,6 +513,9 @@ mobile:
|
|||
mk-user-overview-keywords:
|
||||
no-keywords: "キーワードはありません(十分な数の投稿をしていない可能性があります)"
|
||||
|
||||
mk-user-overview-domains:
|
||||
no-domains: "よく表れるドメインは検出されませんでした"
|
||||
|
||||
mk-user-overview-followers-you-know:
|
||||
loading: "読み込み中"
|
||||
no-users: "知り合いのユーザーはいません"
|
||||
|
|
120
src/tools/analysis/extract-user-domains.ts
Normal file
120
src/tools/analysis/extract-user-domains.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
import * as URL from 'url';
|
||||
|
||||
import Post from '../../api/models/post';
|
||||
import User from '../../api/models/user';
|
||||
import parse from '../../api/common/text';
|
||||
|
||||
process.on('unhandledRejection', console.dir);
|
||||
|
||||
function tokenize(text: string) {
|
||||
if (text == null) return [];
|
||||
|
||||
// パース
|
||||
const ast = parse(text);
|
||||
|
||||
const domains = ast
|
||||
// URLを抽出
|
||||
.filter(t => t.type == 'url' || t.type == 'link')
|
||||
.map(t => URL.parse(t.url).hostname);
|
||||
|
||||
return domains;
|
||||
}
|
||||
|
||||
// Fetch all users
|
||||
User.find({}, {
|
||||
fields: {
|
||||
_id: true
|
||||
}
|
||||
}).then(users => {
|
||||
let i = -1;
|
||||
|
||||
const x = cb => {
|
||||
if (++i == users.length) return cb();
|
||||
extractDomainsOne(users[i]._id).then(() => x(cb), err => {
|
||||
console.error(err);
|
||||
setTimeout(() => {
|
||||
i--;
|
||||
x(cb);
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
x(() => {
|
||||
console.log('complete');
|
||||
});
|
||||
});
|
||||
|
||||
function extractDomainsOne(id) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
process.stdout.write(`extracting domains of ${id} ...`);
|
||||
|
||||
// Fetch recent posts
|
||||
const recentPosts = await Post.find({
|
||||
user_id: id,
|
||||
text: {
|
||||
$exists: true
|
||||
}
|
||||
}, {
|
||||
sort: {
|
||||
_id: -1
|
||||
},
|
||||
limit: 10000,
|
||||
fields: {
|
||||
_id: false,
|
||||
text: true
|
||||
}
|
||||
});
|
||||
|
||||
// 投稿が少なかったら中断
|
||||
if (recentPosts.length < 100) {
|
||||
process.stdout.write(' >>> -\n');
|
||||
return resolve();
|
||||
}
|
||||
|
||||
const domains = {};
|
||||
|
||||
// Extract domains from recent posts
|
||||
recentPosts.forEach(post => {
|
||||
const domainsOfPost = tokenize(post.text);
|
||||
|
||||
domainsOfPost.forEach(domain => {
|
||||
if (domains[domain]) {
|
||||
domains[domain]++;
|
||||
} else {
|
||||
domains[domain] = 1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Calc peak
|
||||
let peak = 0;
|
||||
Object.keys(domains).forEach(domain => {
|
||||
if (domains[domain] > peak) peak = domains[domain];
|
||||
});
|
||||
|
||||
// Sort domains by frequency
|
||||
const domainsSorted = Object.keys(domains).sort((a, b) => domains[b] - domains[a]);
|
||||
|
||||
// Lookup top 10 domains
|
||||
const topDomains = domainsSorted.slice(0, 10);
|
||||
|
||||
process.stdout.write(' >>> ' + topDomains.join(', ') + '\n');
|
||||
|
||||
// Make domains object (includes weights)
|
||||
const domainsObj = topDomains.map(domain => ({
|
||||
domain: domain,
|
||||
weight: domains[domain] / peak
|
||||
}));
|
||||
|
||||
// Save
|
||||
User.update({ _id: id }, {
|
||||
$set: {
|
||||
domains: domainsObj
|
||||
}
|
||||
}).then(() => {
|
||||
resolve();
|
||||
}, err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -240,6 +240,12 @@
|
|||
<mk-user-overview-keywords user={ user }/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="domains">
|
||||
<h2><i class="fa fa-globe"></i>%i18n:mobile.tags.mk-user-overview.domains%</h2>
|
||||
<div>
|
||||
<mk-user-overview-domains user={ user }/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="followers-you-know" if={ SIGNIN && I.id !== user.id }>
|
||||
<h2><i class="fa fa-users"></i>%i18n:mobile.tags.mk-user-overview.followers-you-know%</h2>
|
||||
<div>
|
||||
|
@ -579,6 +585,40 @@
|
|||
</script>
|
||||
</mk-user-overview-keywords>
|
||||
|
||||
<mk-user-overview-domains>
|
||||
<div if={ user.domains != null && user.domains.length > 1 }>
|
||||
<virtual each={ domain in user.domains }>
|
||||
<a style="opacity: { 0.5 + (domain.weight / 2) }">{ domain.domain }</a>
|
||||
</virtual>
|
||||
</div>
|
||||
<p class="empty" if={ user.domains == null || user.domains.length == 0 }>%i18n:mobile.tags.mk-user-overview-domains.no-domains%</p>
|
||||
<style>
|
||||
:scope
|
||||
display block
|
||||
|
||||
> div
|
||||
padding 4px
|
||||
|
||||
> a
|
||||
display inline-block
|
||||
margin 4px
|
||||
color #555
|
||||
|
||||
> .empty
|
||||
margin 0
|
||||
padding 16px
|
||||
text-align center
|
||||
color #aaa
|
||||
|
||||
> i
|
||||
margin-right 4px
|
||||
|
||||
</style>
|
||||
<script>
|
||||
this.user = this.opts.user;
|
||||
</script>
|
||||
</mk-user-overview-domains>
|
||||
|
||||
<mk-user-overview-followers-you-know>
|
||||
<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>%i18n:mobile.tags.mk-user-overview-followers-you-know.loading%<mk-ellipsis/></p>
|
||||
<div if={ !initializing && users.length > 0 }>
|
||||
|
|
Loading…
Reference in a new issue