<!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
	<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
	<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
	<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
	<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
	<script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script>
	<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
	<script src="https://unpkg.com/vuewordcloud"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.33/moment-timezone.min.js" integrity="sha512-jkvef+BAlqJubZdUhcyvaE84uD9XOoLR3e5GGX7YW7y8ywt0rwcGmTQHoxSMRzrJA3+Jh2T8Uy6f8TLU3WQhpQ==" crossorigin="anonymous"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/locale/ja.min.js" integrity="sha512-rElveAU5iG1CzHqi7KbG1T4DQIUCqhitISZ9nqJ2Z4TP0z4Aba64xYhwcBhHQMddRq27/OKbzEFZLOJarNStLg==" crossorigin="anonymous"></script>
	<script src="https://cdn.jsdelivr.net/npm/vue-wordcloud@1.1.1/dist/word-cloud.min.js"></script>
	<style>
		[v-cloak] {
			display: none;
		}
        
		html,body{
            
		}
		
		body {
			padding: 25px;
			font-size: 16px;
			-webkit-print-color-adjust: exact;
            color-adjust: exact;
		}
		
		h1 {
			font-size: 1.5em;
			font-weight: bold;
			margin-bottom: 1em;
		}

		h2 {
			font-size: 1.3em;
			font-weight: bold;
			margin-bottom: 1em;
		}

		.apexcharts-heatmap-rect[color^="rgba(25"]+.apexcharts-data-labels text {
			fill: #ddd !important;
		}

		.apexcharts-heatmap-rect[color^="rgba(24"]+.apexcharts-data-labels text {
			fill: #ddd !important;
		}
		
		.grid{
			display: grid;
			grid-gap: 25px;
			grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); 
		}
		
	</style>
</head>

<body>
	<div id="app" v-cloak>
		<v-app>
			<v-container>
			    <div class="page">
                    <h1>
                        レポート
                    </h1>
                    <h2>
                        対象ハッシュタグ
                    </h2>
                    <p class="mb-8">{{json.tag}}</p>
                    <h2>
                        実行日時
                    </h2>
                    <p class="mb-8">{{json.start}}</p>
                    <h2>
                        調査範囲
                    </h2>
                    <p class="mb-8">上限:{{!json.range? 'なし':json.range }} , {{json.since ? json.since : ''}} 〜 {{json.until ? json.until : ''}}</p>
                </div>
                <div class="page">
                    <h2>
                        投稿一覧
                    </h2>
                    <v-data-iterator class="mb-8" :items="medias" item-key="id" :items-per-page="12" :footer-props="{itemsPerPageOptions:[6,12,24,48],showFirstLastPage: true}">
                        <template v-slot:default="{ items, isExpanded, expand }">
                            <div class="grid">
                                <div v-for="item in items" :key="item.name">
                                    <v-card>
                                        <v-list-item>
                                            <v-list-item-avatar color="grey darken-3">
                                                <v-img :src="'https://kikakushitsu.com/labs/insta/echo.php?'+encodeURI(item.profile_pic_url)" aspect-ratio="1" class="grey lighten-2 pointer">
                                                    <template v-slot:placeholder>
                                                        <v-row class="fill-height ma-0" align="center" justify="center">
                                                            <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
                                                        </v-row>
                                                    </template>
                                                </v-img>
                                            </v-list-item-avatar>
                                            <v-list-item-content>
                                                <v-list-item-title>{{item.full_name ? item.full_name : item.username}}</v-list-item-title>
                                            </v-list-item-content>
                                        </v-list-item>
                                        <a :href="`https://www.instagram.com/p/${item.code}/`" target="_blank">
                                            <v-img :src="'https://kikakushitsu.com/labs/insta/echo.php?'+encodeURI(item.thumbnail)" aspect-ratio="1" class="grey lighten-2 pointer">
                                                <template v-slot:placeholder>
                                                    <v-row class="fill-height ma-0" align="center" justify="center">
                                                        <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
                                                    </v-row>
                                                </template>
                                            </v-img>
                                        </a>
                                        <v-card-subtitle class="pb-0">
                                            {{item.datetime}}
                                        </v-card-subtitle>

                                        <v-card-text class="text--primary" style="white-space: pre-wrap;height:100px;overflow-y: scroll;" v-html="item.caption"></v-card-text>
                                        <v-card-actions>
                                            <v-list-item class="grow">
                                                <v-row align="center" justify="end">
                                                    <v-icon class="mr-1">
                                                        mdi-heart-outline
                                                    </v-icon>
                                                    <span class="subheading mr-2">{{item.like_count}}</span>
                                                    <v-icon class="mr-1">
                                                        mdi-comment-outline
                                                    </v-icon>
                                                    <span class="subheading">{{item.comment_count}}</span>
                                                </v-row>
                                            </v-list-item>
                                        </v-card-actions>
                                    </v-card>
                                </div>
                            </div>
                        </template>
                    </v-data-iterator>
                    <v-data-table class="mb-8" multi-sort :headers="mediasHeaders" :items="medias" :sort-by="`date`" :sort-desc="true" :items-per-page="10" :footer-props="{itemsPerPageOptions:[10,25,50],showFirstLastPage: true}" mobile-breakpoint="0">
                        <template v-slot:item.thumbnail="{ item }">
                            <v-img :src="'https://kikakushitsu.com/labs/insta/echo.php?'+encodeURI(item.thumbnail)" aspect-ratio="1" class="grey lighten-2 pointer">
                                <template v-slot:placeholder>
                                    <v-row class="fill-height ma-0" align="center" justify="center">
                                        <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
                                    </v-row>
                                </template>
                            </v-img>
                        </template>
                        <template v-slot:item.full_name="{ item }">
                            <a :href="`https://www.instagram.com/p/${item.code}/`" target="_blank">{{item.full_name ? item.full_name : item.username}}</a>
                        </template>
                        <template v-slot:item.media_type="{ item }">
                            {{ ['Image','Slider',null,null,null,null,null,'Video'][item.media_type-1] }}
                        </template>
                    </v-data-table>
                </div>
				<div class="page">
                    <h2>
                        全体集計
                    </h2>
                    <v-simple-table class="mb-8">
                        <template v-slot:default>
                            <thead>
                                <tr>
                                    <th class="text-left">
                                        項目
                                    </th>
                                    <th class="text-left">
                                        値
                                    </th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-for="item in items" :key="item.name">
                                    <td>{{ item.name }}</td>
                                    <td>{{ item.value }}</td>
                                </tr>
                            </tbody>
                        </template>
                    </v-simple-table>
				</div>
				<div class="page">
                    <h2>
                        日付別集計
                    </h2>
                    <div class="mb-8">
                        <apexchart :options="datesChartOptions1" :series="dates_counts" :height="datesChartOptions1.chart.height"></apexchart>
                    </div>
                    <div class="mb-8">
                        <apexchart :options="datesChartOptions2" :series="dates_likes" :height="datesChartOptions2.chart.height"></apexchart>
                    </div>
                    <div class="mb-8">
                        <apexchart :options="datesChartOptions3" :series="dates_comments" :height="datesChartOptions3.chart.height"></apexchart>
                    </div>
                    <v-data-table class="mb-8" multi-sort :headers="datesHeaders" :items="dates" :sort-by="`date`" :sort-desc="true" :footer-props="{itemsPerPageOptions:[10,25,50],showFirstLastPage: true}">
                        <template v-slot:item.engagement="{ item }">
                            {{Math.round(item.engagement* 100) / 100}}
                        </template>
                        <template v-slot:item.engagement="{ item }">
                            {{Math.round(item.engagement* 100) / 100}}
                        </template>
                    </v-data-table>
				</div>
				<div class="page">
                    <h2>
                        投稿された曜日別時間帯
                    </h2>
                    <div class="mb-8">
                        <apexchart ref="chart" :options="chartOptions" :series="times" :height="chartOptions.chart.height"></apexchart>
                    </div>
                </div>
                <div class="page">
                    <h2>
                        ユーザー別集計
                    </h2>
                    <v-data-table class="mb-8" multi-sort :headers="usersHeaders" :items="users" :sort-by="`count`" :sort-desc="true" :footer-props="{itemsPerPageOptions:[10,25,50],showFirstLastPage: true}" mobile-breakpoint="0">
                        <template v-slot:item.profile_pic_url="{ item }">
                            <v-img :src="'https://kikakushitsu.com/labs/insta/echo.php?'+encodeURI(item.profile_pic_url)" aspect-ratio="1" class="grey lighten-2 pointer">
                                <template v-slot:placeholder>
                                    <v-row class="fill-height ma-0" align="center" justify="center">
                                        <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
                                    </v-row>
                                </template>
                            </v-img>
                        </template>
                        <template v-slot:item.full_name="{ item }">
                            <a :href="`https://www.instagram.com/${item.username}/`" target="_blank">{{item.full_name ? item.full_name : item.username}}</a>
                        </template>
                        <template v-slot:item.engagement="{ item }">
                            {{Math.round(item.engagement* 100) / 100}}
                        </template>
                    </v-data-table>
                </div>
                <div class="page">
				<h2>
					関連タグ集計
				</h2>
                    <div class="mb-8">
                        <word-cloud style="height: 650px;" :data="words" :show-tooltip="false" :font-size="[5, 60]" :rotate="{from: 0, to: 0, numOfOrientation: 0 }" name-key="name" value-key="count" :word-click="()=>{}"></word-cloud>
                    </div>
                    <v-data-table class="mb-8" multi-sort :headers="tagsHeaders" :items="tags" :sort-by="`count`" :sort-desc="true" :footer-props="{itemsPerPageOptions:[10,25,50],showFirstLastPage: true}" mobile-breakpoint="0">
                        <template v-slot:item.name="{ item }">
                            <a :href="`https://www.instagram.com/explore/tags/${item.name}/`" target="_blank">{{ item.name }}</a>
                        </template>
                        <template v-slot:item.engagement="{ item }">
                            {{Math.round(item.engagement* 100) / 100}}
                        </template>
                    </v-data-table>
                </div>
			</v-container>
		</v-app>
	</div>
	<script>
		(async () => {
			let ja = await (await fetch('https://cdn.jsdelivr.net/npm/apexcharts@3.26.1/dist/locales/ja.json')).json();
			new Vue({
				el: '#app',
				vuetify: new Vuetify(),
				components: {
					apexchart: VueApexCharts,
					[VueWordCloud.name]: VueWordCloud,
					[WordCloud.name]: WordCloud
				},
				data: {
					json: {},
					medias: [],
					items: [],
					dates: [],
					users: [],
					tags: [],
					mediasHeaders: [{
							text: '投稿',
							value: 'thumbnail',
							width: '80px',
							sortable: false,
						}, {
							text: '日時',
							value: 'datetime'
						},
						{
							text: 'ユーザー名',
							value: 'full_name'
						},
						{
							text: 'いいね数',
							value: 'like_count'
						},
						{
							text: 'コメント数',
							value: 'comment_count'
						},
						{
							text: 'エンゲージメント数',
							value: 'engagement'
						},
						{
							text: 'タイプ',
							value: 'media_type'
						},
					],
					datesHeaders: [{
							text: '日付',
							value: 'date'
						},
						{
							text: '投稿数',
							value: 'count'
						},
						{
							text: '投稿者数',
							value: 'owners'
						},
						{
							text: 'いいね数',
							value: 'likes'
						},
						{
							text: 'コメント数',
							value: 'comments'
						},
						{
							text: 'エンゲージメント数',
							value: 'engagement'
						},
					],
					usersHeaders: [{
							text: '投稿',
							value: 'profile_pic_url',
							width: '80px',
							sortable: false,
						}, {
							text: 'ユーザー名',
							value: 'full_name'
						},
						{
							text: '投稿数',
							value: 'count'
						},
						{
							text: '総いいね数',
							value: 'likes'
						},
						{
							text: '総コメント数',
							value: 'comments'
						},
						{
							text: 'エンゲージメント数',
							value: 'engagement'
						},

					],
					tagsHeaders: [{
							text: 'タグ名',
							value: 'name'
						},
						{
							text: '出現回数',
							value: 'count'
						},
						{
							text: 'いいね数',
							value: 'likes'
						},
						{
							text: 'コメント数',
							value: 'comments'
						},
						{
							text: 'エンゲージメント数',
							value: 'engagement'
						},
					],
					chartOptions: {
						animations: {
							enabled: false,
						},
						chart: {
							height: 650,
							type: 'heatmap',
							toolbar: {
								show: false
							},
						},
						colors: ["#008FFB"],
						xaxis: {
							type: 'category',
							categories: ['日曜', '月曜', '火曜', '水曜', '木曜', '金曜', '土曜']
						},
						plotOptions: {
							heatmap: {
								enableShades: true,
								shadeIntensity: 1
							}
						}
					},
					dates_counts: [],
					dates_likes: [],
					dates_comments: [],
					datesChartOptions1: {
						animations: {
							enabled: false,
						},
						title: {
							text: '投稿数'
						},
						chart: {
							animations: {
								enabled: false,
							},
							id: 'counts',
							group: 'social',
							locales: [ja],
							defaultLocale: 'ja',
							height: 150,
							type: 'line',
							toolbar: {
								tools: {
									download: false
								}
							},
						},
						colors: ['#77B6EA'],
						stroke: {
							width: 2
						},
						dataLabels: {
							enabled: false,
						},
						tooltip: {
							x: {
								format: 'yyyy-MM-dd'
							}
						},
						xaxis: {
							type: 'datetime',
							labels: {
								datetimeFormatter: {
									year: 'yyyy',
									month: 'yyyy-MM',
									day: 'MM-dd',
									hour: 'HH:mm'
								}
							}
						}
					},
					datesChartOptions2: {
						title: {
							text: 'いいね数'
						},
						chart: {
							animations: {
								enabled: false,
							},
							id: 'likes',
							group: 'social',
							locales: [ja],
							defaultLocale: 'ja',
							height: 150,
							type: 'line',
							toolbar: {
								tools: {
									download: false
								}
							},
						},
						colors: ['#5bc352'],
						stroke: {
							width: 2
						},
						dataLabels: {
							enabled: false,
						},
						tooltip: {
							x: {
								format: 'yyyy-MM-dd'
							}
						},
						xaxis: {
							type: 'datetime',
							labels: {
								datetimeFormatter: {
									year: 'yyyy',
									month: 'yyyy-MM',
									day: 'MM-dd',
									hour: 'HH:mm'
								}
							}
						}
					},
					datesChartOptions3: {
						title: {
							text: 'コメント数'
						},
						chart: {
							animations: {
								enabled: false,
							},
							id: 'comments',
							group: 'social',
							locales: [ja],
							defaultLocale: 'ja',
							height: 150,
							type: 'line',
							toolbar: {
								tools: {
									download: false
								}
							},
						},
						colors: ['#b1ad44'],
						stroke: {
							width: 2
						},
						dataLabels: {
							enabled: false,
						},
						tooltip: {
							x: {
								format: 'yyyy-MM-dd'
							}
						},
						xaxis: {
							type: 'datetime',
							labels: {
								datetimeFormatter: {
									year: 'yyyy',
									month: 'yyyy-MM',
									day: 'MM-dd',
									hour: 'HH:mm'
								}
							}
						}
					},
					times: [{
						name: '00:00',
						data: [1, 2, 3, 4, 5, 6, 7]
					}],
					words: []
				},
				async mounted() {

					this.json = await (await fetch('data.json')).json();
					this.medias = this.json.medias;
					this.medias.sort((a, b) => {
						if (a.taken_at > b.taken_at) return -1;
						if (a.taken_at < b.taken_at) return 1;
						return 0;
					});
					let dates = [];
					let users = [];
					let tags = [];
					let times = [];
					for (let t = 0; t < 24; t++) {
						times.push({
							name: `${(t).toString().padStart(2,'0')}:00`,
							data: [0, 0, 0, 0, 0, 0, 0]
						});
					}
					moment.locale("ja");
					moment.tz.setDefault("Asia/Tokyo");

					for (const media of this.medias) {
						media.comment_count = media.comment_count ? parseInt(media.comment_count) : 0;
						media.like_count = media.like_count ? parseInt(media.like_count) : 0;
						media.caption = media.caption.normalize('NFC');
						media.engagement = media.like_count + media.comment_count / 1;

						let m = moment(media.datetime);
						let time = times.find(v => v.name === `${m.format('HH')}:00`);
						time.data[m.format('e')]++;

						//日付集計
						let date = dates.find(v => v.date === m.format('YYYY-MM-DD'));
						if (!date) {
							dates.push({
								date: m.format('YYYY-MM-DD'),
								count: 1,
								likes: media.like_count,
								comments: media.comment_count,
								owners: [media.username],
								engagement: media.engagement,
							});
						} else {
							date.count++;
							date.likes += media.like_count;
							date.comments += media.comment_count;
							date.engagement = (date.likes + date.comments) / date.count;
							if (!date.owners.includes(media.username)) {
								date.owners.push(media.username);
							}
						}

						//ユーザー集計
						let user = users.find(v => v.username === media.username);
						if (!user) {
							users.push({
								username: media.username,
								full_name: media.full_name,
								count: 1,
								likes: media.like_count,
								comments: media.comment_count,
								profile_pic_url: media.profile_pic_url,
								engagement: media.engagement,
							});
						} else {
							user.count++;
							user.likes += media.like_count;
							user.comments += media.comment_count;
							user.engagement = (user.likes + user.comments) / user.count;
						}
						let _tags = media.caption.match(/[#＃][Ａ-Ｚａ-ｚA-Za-z一-鿆0-9０-９ぁ-ヶｦ-ﾟー._-]+/gm);
						if (_tags) {
							_tags = _tags.filter(function(x, i, self) {
								return self.indexOf(x) === i;
							});
							for (let tag of _tags) {
								let _tag = tags.find(v => v.name === tag.slice(1));
								if (!_tag) {
									tags.push({
										name: tag.slice(1),
										count: 1,
										likes: media.like_count,
										comments: media.comment_count,
										engagement: media.engagement,
									});
								} else {
									_tag.count++;
									_tag.likes += media.like_count;
									_tag.comments += media.comment_count;
									_tag.engagement = (_tag.likes + _tag.comments) / _tag.count;
								}
							}
						}
					}

					dates.forEach(v => {
						v.owners = v.owners.length;
					});

					let total = {
						likes: 0,
						comments: 0,
					};

					users.forEach(user => {
						total.likes += user.likes;
						total.comments += user.comments;
					});

					this.items = [{
							name: "投稿件数",
							value: this.medias.length
						},
						{
							name: "投稿者数",
							value: users.length
						},
						{
							name: "いいね合計",
							value: total.likes
						},
						{
							name: "平均いいね数",
							value: Math.round(total.likes / this.medias.length * 100) / 100
						},
						{
							name: "コメント合計",
							value: total.comments
						},
						{
							name: "平均コメント数",
							value: Math.round(total.comments / this.medias.length * 100) / 100
						},
						{
							name: "エンゲージメント数",
							value: Math.round((total.likes + total.comments) / this.medias.length * 100) / 100
						},
						{
							name: "期間",
							value: `${this.medias[this.medias.length-1].datetime} 〜 ${this.medias[0].datetime}`
						},
					];


					this.dates = dates;
					this.dates_counts.push({
						name: "投稿数",
						data: this.dates.map(date => [moment.utc(date.date).valueOf(), date.count])
					});
					this.dates_likes.push({
						name: "いいね",
						data: this.dates.map(date => [moment.utc(date.date).valueOf(), date.likes])
					});
					this.dates_comments.push({
						name: "コメント",
						data: this.dates.map(date => [moment.utc(date.date).valueOf(), date.comments])
					});

					times.reverse();
					this.times = times;
					this.users = users;
					this.tags = tags;
					this.tags.sort(function(a, b) {
						if (a.count > b.count) return -1;
						if (a.count < b.count) return 1;
						return 0;
					});
					this.tags.shift();
					this.words = this.tags.slice(0,500);
				}
			});
		})();
	</script>
</body>

</html>