wip #272
This commit is contained in:
		
							parent
							
								
									24f2b238ba
								
							
						
					
					
						commit
						9a4395f3dc
					
				
					 5 changed files with 166 additions and 0 deletions
				
			
		| 
						 | 
					@ -109,6 +109,9 @@ const endpoints: Endpoint[] = [
 | 
				
			||||||
		withCredential: true,
 | 
							withCredential: true,
 | 
				
			||||||
		secure: true
 | 
							secure: true
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							name: 'aggregation/users/activity',
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		name: 'aggregation/users/post',
 | 
							name: 'aggregation/users/post',
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										112
									
								
								src/api/endpoints/aggregation/users/activity.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/api/endpoints/aggregation/users/activity.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,112 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Module dependencies
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import $ from 'cafy';
 | 
				
			||||||
 | 
					import User from '../../../models/user';
 | 
				
			||||||
 | 
					import Post from '../../../models/post';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: likeやfollowも集計
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Aggregate activity of a user
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {any} params
 | 
				
			||||||
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (params) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						// Get 'user_id' parameter
 | 
				
			||||||
 | 
						const [userId, userIdErr] = $(params.user_id).id().$;
 | 
				
			||||||
 | 
						if (userIdErr) return rej('invalid user_id param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Lookup user
 | 
				
			||||||
 | 
						const user = await User.findOne({
 | 
				
			||||||
 | 
							_id: userId
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							fields: {
 | 
				
			||||||
 | 
								_id: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (user === null) {
 | 
				
			||||||
 | 
							return rej('user not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const datas = await Post
 | 
				
			||||||
 | 
							.aggregate([
 | 
				
			||||||
 | 
								{ $match: { user_id: user._id } },
 | 
				
			||||||
 | 
								{ $project: {
 | 
				
			||||||
 | 
									repost_id: '$repost_id',
 | 
				
			||||||
 | 
									reply_to_id: '$reply_to_id',
 | 
				
			||||||
 | 
									created_at: { $add: ['$created_at', 9 * 60 * 60 * 1000] } // Convert into JST
 | 
				
			||||||
 | 
								}},
 | 
				
			||||||
 | 
								{ $project: {
 | 
				
			||||||
 | 
									date: {
 | 
				
			||||||
 | 
										year: { $year: '$created_at' },
 | 
				
			||||||
 | 
										month: { $month: '$created_at' },
 | 
				
			||||||
 | 
										day: { $dayOfMonth: '$created_at' }
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									type: {
 | 
				
			||||||
 | 
										$cond: {
 | 
				
			||||||
 | 
											if: { $ne: ['$repost_id', null] },
 | 
				
			||||||
 | 
											then: 'repost',
 | 
				
			||||||
 | 
											else: {
 | 
				
			||||||
 | 
												$cond: {
 | 
				
			||||||
 | 
													if: { $ne: ['$reply_to_id', null] },
 | 
				
			||||||
 | 
													then: 'reply',
 | 
				
			||||||
 | 
													else: 'post'
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{ $group: { _id: {
 | 
				
			||||||
 | 
									date: '$date',
 | 
				
			||||||
 | 
									type: '$type'
 | 
				
			||||||
 | 
								}, count: { $sum: 1 } } },
 | 
				
			||||||
 | 
								{ $group: {
 | 
				
			||||||
 | 
									_id: '$_id.date',
 | 
				
			||||||
 | 
									data: { $addToSet: {
 | 
				
			||||||
 | 
										type: '$_id.type',
 | 
				
			||||||
 | 
										count: '$count'
 | 
				
			||||||
 | 
									}}
 | 
				
			||||||
 | 
								} }
 | 
				
			||||||
 | 
							]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						datas.forEach(data => {
 | 
				
			||||||
 | 
							data.date = data._id;
 | 
				
			||||||
 | 
							delete data._id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							data.posts = (data.data.filter(x => x.type == 'post')[0] || { count: 0 }).count;
 | 
				
			||||||
 | 
							data.reposts = (data.data.filter(x => x.type == 'repost')[0] || { count: 0 }).count;
 | 
				
			||||||
 | 
							data.replies = (data.data.filter(x => x.type == 'reply')[0] || { count: 0 }).count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delete data.data;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const graph = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (let i = 0; i < 365; i++) {
 | 
				
			||||||
 | 
							let day = new Date(new Date().setDate(new Date().getDate() - i));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const data = datas.filter(d =>
 | 
				
			||||||
 | 
								d.date.year == day.getFullYear() && d.date.month == day.getMonth() + 1 && d.date.day == day.getDate()
 | 
				
			||||||
 | 
							)[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (data) {
 | 
				
			||||||
 | 
								graph.push(data);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								graph.push({
 | 
				
			||||||
 | 
									date: {
 | 
				
			||||||
 | 
										year: day.getFullYear(),
 | 
				
			||||||
 | 
										month: day.getMonth() + 1, // In JavaScript, month is zero-based.
 | 
				
			||||||
 | 
										day: day.getDate()
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									posts: 0,
 | 
				
			||||||
 | 
									reposts: 0,
 | 
				
			||||||
 | 
									replies: 0
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res(graph);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/web/app/common/tags/activity-table.tag
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/web/app/common/tags/activity-table.tag
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					<mk-activity-table>
 | 
				
			||||||
 | 
						<svg if={ data } ref="canvas" viewBox="0 0 53 6.85">
 | 
				
			||||||
 | 
							<rect each={ d, i in data } width="0.8" height="0.8"
 | 
				
			||||||
 | 
								x={ 52 - (i / 7) } y={ d.date.weekday }
 | 
				
			||||||
 | 
								fill={ d.color }></rect>
 | 
				
			||||||
 | 
						</svg>
 | 
				
			||||||
 | 
						<style>
 | 
				
			||||||
 | 
							:scope
 | 
				
			||||||
 | 
								display block
 | 
				
			||||||
 | 
								max-width 600px
 | 
				
			||||||
 | 
								margin 0 auto
 | 
				
			||||||
 | 
								background #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> svg
 | 
				
			||||||
 | 
									display block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</style>
 | 
				
			||||||
 | 
						<script>
 | 
				
			||||||
 | 
							this.mixin('api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.user = this.opts.user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.on('mount', () => {
 | 
				
			||||||
 | 
								this.api('aggregation/users/activity', {
 | 
				
			||||||
 | 
									user_id: this.user.id
 | 
				
			||||||
 | 
								}).then(data => {
 | 
				
			||||||
 | 
									data.forEach(d => d.total = d.posts + d.replies + d.reposts);
 | 
				
			||||||
 | 
									this.peak = Math.max.apply(null, data.map(d => d.total));
 | 
				
			||||||
 | 
									data.forEach(d => {
 | 
				
			||||||
 | 
										d.v = d.total / this.peak;
 | 
				
			||||||
 | 
										d.color = d.v > 0.75
 | 
				
			||||||
 | 
											? '#196127'
 | 
				
			||||||
 | 
											: d.v > 0.5
 | 
				
			||||||
 | 
												? '#239a3b'
 | 
				
			||||||
 | 
												: d.v > 0.25
 | 
				
			||||||
 | 
													? '#7bc96f'
 | 
				
			||||||
 | 
													: d.v > 0
 | 
				
			||||||
 | 
														? '#c6e48b'
 | 
				
			||||||
 | 
														: '#eee';
 | 
				
			||||||
 | 
										d.date.weekday = (new Date(d.date.year + '-' + d.date.month + '-' + d.date.day)).getDay();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									this.update({ data });
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						</script>
 | 
				
			||||||
 | 
					</mk-activity-table>
 | 
				
			||||||
| 
						 | 
					@ -25,3 +25,4 @@ require('./messaging/index.tag');
 | 
				
			||||||
require('./messaging/form.tag');
 | 
					require('./messaging/form.tag');
 | 
				
			||||||
require('./stream-indicator.tag');
 | 
					require('./stream-indicator.tag');
 | 
				
			||||||
require('./public-timeline.tag');
 | 
					require('./public-timeline.tag');
 | 
				
			||||||
 | 
					require('./activity-table.tag');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@
 | 
				
			||||||
						<i>フォロワー</i>
 | 
											<i>フォロワー</i>
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
									<mk-activity-table user={ user }></mk-activity-table>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<nav>
 | 
								<nav>
 | 
				
			||||||
				<a data-is-active={ page == 'posts' } onclick={ go.bind(null, 'posts') }>タイムライン</a>
 | 
									<a data-is-active={ page == 'posts' } onclick={ go.bind(null, 'posts') }>タイムライン</a>
 | 
				
			||||||
| 
						 | 
					@ -146,6 +147,9 @@
 | 
				
			||||||
								> i
 | 
													> i
 | 
				
			||||||
									font-size 14px
 | 
														font-size 14px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											> mk-activity-table
 | 
				
			||||||
 | 
												margin 12px 0 0 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					> nav
 | 
										> nav
 | 
				
			||||||
						display flex
 | 
											display flex
 | 
				
			||||||
						justify-content center
 | 
											justify-content center
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue