├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── data_processing.py ├── demo ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png └── 7.png ├── music_people_processing.py ├── music_processing.py ├── people_processing.py ├── scrapy ├── .DS_Store ├── 0_filter_movie.py ├── music_run.sh ├── people_run.sh ├── run.sh ├── scrapy.cfg └── tutorial │ ├── .DS_Store │ ├── __init__.py │ ├── items.py │ ├── pipelines.py │ ├── settings.py │ └── spiders │ ├── __init__.py │ ├── music.py │ ├── people.py │ └── spider.py ├── server.py ├── static ├── Thumbs.db ├── color.png ├── color_ko.png ├── insight.png ├── insight_ko.png ├── logo.png ├── main.png ├── main2.png ├── materialize.min.css ├── music.js ├── music_color.png ├── music_color_ko.png ├── music_logo.png ├── screenshot.png ├── script.js ├── style.css ├── weight.png └── weight_ko.png └── templates ├── index.html ├── layout.html └── music.html /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.tar* 3 | *.jar 4 | *.json 5 | *.sql 6 | *.sqlite3 7 | 8 | # Created by https://www.gitignore.io 9 | 10 | #!! ERROR: py is undefined. Use list command to see defined gitignore types !!# 11 | 12 | ### Python ### 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | *.py[cod] 16 | 17 | # C extensions 18 | *.so 19 | 20 | # Distribution / packaging 21 | .Python 22 | env/ 23 | build/ 24 | develop-eggs/ 25 | dist/ 26 | downloads/ 27 | eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .coverage 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | 69 | ### vim ### 70 | [._]*.s[a-w][a-z] 71 | [._]s[a-w][a-z] 72 | *.un~ 73 | Session.vim 74 | .netrwhist 75 | *~ 76 | 77 | 78 | ### Django ### 79 | *.log 80 | *.pot 81 | *.pyc 82 | __pycache__/ 83 | local_settings.py 84 | 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 by Taehoon Kim. 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VoxOffice 2 | ========= 3 | 4 | A Data Visualization of Box office history. [link](http://pail.unist.ac.kr/carpedm20/vox/) 5 | 6 | 7 | Screenshot 8 | ---------- 9 | 10 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/static/main.png) 11 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/static/main2.png) 12 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/demo/7.png) 13 | 14 | 15 | VoxMusic 16 | ========= 17 | 18 | A Data Visualization of Music chart history. [link](http://pail.unist.ac.kr/carpedm20/music/) 19 | 20 | 21 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/demo/1.png) 22 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/demo/3.png) 23 | ![alt_tag](https://raw.githubusercontent.com/carpedm20/voxoffice/master/demo/2.png) 24 | 25 | Copyright 26 | --------- 27 | 28 | Copyright :copyright: 2015 [Kim Tae Hoon](carpedm20.github.io) 29 | 30 | The MIT License (MIT) 31 | -------------------------------------------------------------------------------- /data_processing.py: -------------------------------------------------------------------------------- 1 | import json 2 | import numpy as np 3 | from bs4 import BeautifulSoup 4 | import requests 5 | from collections import defaultdict 6 | 7 | MAX_RANK = 10 8 | SKIP_DATE = 7 9 | 10 | for genre in ['action', 'comedy', 'thriller', 'romance', 'all']: 11 | print " ******** %s ********* " % genre 12 | 13 | j = json.loads(open('static/movie-%s.json' % genre).read()) 14 | 15 | years = range(2007, 2015) 16 | 17 | poster_dict = {} 18 | 19 | def poster_url(code): 20 | b = BeautifulSoup(requests.get('http://movie.naver.com/movie/bi/mi/photoViewPopup.nhn?movieCode='+str(code)).text) 21 | try: 22 | img = b.find('img') 23 | return [img['src']+"?type=m203_290_2", img['alt']] 24 | except: 25 | return ['http://static.naver.net/movie/2012/06/dft_img203x290.png',''] 26 | 27 | for year in years: 28 | print year 29 | 30 | movies = defaultdict(int) 31 | dates = defaultdict(int) 32 | 33 | movie_title = {} 34 | 35 | start_year = '%s0101' % year 36 | end_year = '%s1231' % year 37 | 38 | for i in j: 39 | dd = int(i['date'][-2:]) 40 | if dd % SKIP_DATE == 0 or dd in [28,29,30,31]: 41 | if start_year < i['date'] < end_year: 42 | if i['rank'] > MAX_RANK: 43 | continue 44 | url = i['url'] 45 | code = int(url[url.index('code=')+5:]) 46 | movies[code] += 1 47 | movie_title[code] = i['name'] 48 | dates[i['date']] += 1 49 | 50 | movies_key = movies.keys() 51 | movies_key.sort() 52 | 53 | dates = dates.keys() 54 | dates.sort() 55 | 56 | print "Distinct movies : %s" % len(movies_key) 57 | print "Distinct dates : %s" % len(dates) 58 | 59 | y1 = np.zeros((len(movies_key),len(dates)), dtype='int16') 60 | 61 | new_movie_title = {} 62 | 63 | movies_key = [i[0] for i in sorted(movies.iteritems(), key=lambda (k,v): v,reverse=True)] 64 | 65 | for i in j: 66 | dd = int(i['date'][-2:]) 67 | if dd % SKIP_DATE == 0 or dd in [28,29,30,31]: 68 | if start_year < i['date'] < end_year: 69 | url = i['url'] 70 | code = int(url[url.index('code=')+5:]) 71 | try: 72 | url = poster_dict[code] 73 | except: 74 | url = poster_dict[code] = poster_url(code) 75 | try: 76 | new_code = movies_key.index(code) 77 | except: 78 | continue 79 | date = dates.index(i['date']) 80 | new_movie_title[new_code] = [code, url, movie_title[code]] 81 | y1[new_code][date] = int(i['rank']) 82 | 83 | ans = {'movies' : new_movie_title, 84 | 'mindate' : dates[0], 85 | 'maxdate' : dates[-1], 86 | 'skipdate' : SKIP_DATE, 87 | 'y1' : y1.tolist() } 88 | 89 | with open('%s-%s.json' % (genre, year),'w') as f: 90 | json.dump(ans, f) 91 | -------------------------------------------------------------------------------- /demo/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/1.png -------------------------------------------------------------------------------- /demo/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/2.png -------------------------------------------------------------------------------- /demo/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/3.png -------------------------------------------------------------------------------- /demo/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/4.png -------------------------------------------------------------------------------- /demo/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/5.png -------------------------------------------------------------------------------- /demo/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/6.png -------------------------------------------------------------------------------- /demo/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/demo/7.png -------------------------------------------------------------------------------- /music_people_processing.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import json 3 | import numpy as np 4 | from bs4 import BeautifulSoup 5 | import requests 6 | from collections import defaultdict 7 | 8 | MAX_RANK = 20 9 | 10 | #for chart_type in ['total', 'domestic', 'oversea']: 11 | chart_type = 'total' 12 | j = json.loads(open('scrapy/music_%s.json' % chart_type).read()) 13 | 14 | artist_name = {} 15 | 16 | for i in j: 17 | code = i['artist_id'] 18 | if code != -1: 19 | artist_name[i['artist']] = code 20 | 21 | for year in xrange(2008, 2015): 22 | print year 23 | 24 | artists = defaultdict(int) 25 | dates = defaultdict(int) 26 | 27 | artist_id = {} 28 | artist = {} 29 | 30 | start_year = '%s0101' % year 31 | end_year = '%s1231' % year 32 | 33 | for i in j: 34 | dd = int(i['date'][:4]) 35 | if dd == year: 36 | code = i['artist_id'] 37 | 38 | if i['rank'] > MAX_RANK: 39 | continue 40 | 41 | if code == -1: 42 | try: 43 | code = i['artist_id'] = artist_name[i['artist']] 44 | except: 45 | try: 46 | item = [item for item in artist_name.items() if i['artist'][:-2] in item[0]][0] 47 | code = item[1] 48 | except: 49 | if i['artist'] == u'HIGH4': 50 | code = i['artist_id'] = 326950 51 | elif i['artist'] == u'신용재(포맨)': 52 | code = i['artist_id'] = 125963 53 | 54 | if code == -1: 55 | continue 56 | 57 | artists[code] += 1 58 | 59 | artist[code] = i['artist'] 60 | artist_id[code] = i['artist_id'] 61 | 62 | dates[int(i['date'])] += 1 63 | 64 | artists_key = artists.keys() 65 | artists_key.sort() 66 | 67 | dates = dates.keys() 68 | dates.sort() 69 | 70 | print "Distinct artists : %s" % len(artists_key) 71 | print "Distinct dates : %s" % len(dates) 72 | 73 | y1 = np.zeros((len(artists_key),len(dates)), dtype='int16') 74 | 75 | new_music_title = {} 76 | 77 | artists_key = [i[0] for i in sorted(artists.iteritems(), key=lambda (k,v): v,reverse=True)] 78 | 79 | for i in j: 80 | dd = int(i['date'][:4]) 81 | if dd == year: 82 | code = i['artist_id'] 83 | 84 | if i['rank'] > MAX_RANK: 85 | continue 86 | if i['artist'] == u'Various Artists': 87 | continue 88 | 89 | try: 90 | new_code = artists_key.index(code) 91 | except: 92 | continue 93 | 94 | date = dates.index(int(i['date'])) 95 | new_music_title[new_code] = [code, artist[code] ] 96 | 97 | tmp = y1[new_code][date] 98 | 99 | if tmp == 0: 100 | y1[new_code][date] = int(i['rank']) 101 | else: 102 | if (int(i['rank']) > tmp): 103 | pass 104 | 105 | if y1[new_code][date]: 106 | pass 107 | #print y1[new_code][date] 108 | 109 | ans = {'musics' : new_music_title, 110 | 'y1' : y1.tolist() } 111 | 112 | with open('artist-%s.json' % (year),'w') as f: 113 | json.dump(ans, f) 114 | -------------------------------------------------------------------------------- /music_processing.py: -------------------------------------------------------------------------------- 1 | import json 2 | import numpy as np 3 | from bs4 import BeautifulSoup 4 | import requests 5 | from collections import defaultdict 6 | 7 | MAX_RANK = 10 8 | 9 | for chart_type in ['total', 'domestic', 'oversea']: 10 | print " ******** %s ********* " % chart_type 11 | 12 | j = json.loads(open('scrapy/music_%s.json' % chart_type).read()) 13 | 14 | for year in xrange(2008, 2015): 15 | print year 16 | 17 | musics = defaultdict(int) 18 | dates = defaultdict(int) 19 | 20 | music_title = {} 21 | album_id = {} 22 | artist_id = {} 23 | track_id = {} 24 | artist = {} 25 | 26 | start_year = '%s0101' % year 27 | end_year = '%s1231' % year 28 | 29 | for i in j: 30 | dd = int(i['date'][:4]) 31 | if dd == year: 32 | if i['rank'] > MAX_RANK: 33 | continue 34 | code = i['track_id'] 35 | musics[code] += 1 36 | music_title[code] = i['name'] 37 | album_id[code] = i['album_id'] 38 | artist_id[code] = i['artist_id'] 39 | artist[code] = i['artist'] 40 | track_id[code] = i['track_id'] 41 | dates[int(i['date'])] += 1 42 | 43 | musics_key = musics.keys() 44 | musics_key.sort() 45 | 46 | dates = dates.keys() 47 | dates.sort() 48 | 49 | print "Distinct musics : %s" % len(musics_key) 50 | print "Distinct dates : %s" % len(dates) 51 | 52 | y1 = np.zeros((len(musics_key),len(dates)), dtype='int16') 53 | 54 | new_music_title = {} 55 | 56 | musics_key = [i[0] for i in sorted(musics.iteritems(), key=lambda (k,v): v,reverse=True)] 57 | 58 | for i in j: 59 | dd = int(i['date'][:4]) 60 | if dd == year: 61 | code = i['track_id'] 62 | 63 | try: 64 | new_code = musics_key.index(code) 65 | except: 66 | continue 67 | 68 | date = dates.index(int(i['date'])) 69 | new_music_title[new_code] = [code, 70 | music_title[code], 71 | artist[code], 72 | album_id[code], 73 | artist_id[code], 74 | track_id[code], 75 | ] 76 | y1[new_code][date] = int(i['rank']) 77 | 78 | ans = {'musics' : new_music_title, 79 | 'y1' : y1.tolist() } 80 | 81 | with open('%s-%s.json' % (chart_type, year),'w') as f: 82 | json.dump(ans, f) 83 | -------------------------------------------------------------------------------- /people_processing.py: -------------------------------------------------------------------------------- 1 | import json 2 | import numpy as np 3 | from bs4 import BeautifulSoup 4 | import requests 5 | from collections import defaultdict 6 | 7 | MAX_RANK = 10 8 | SKIP_DATE = 7 9 | 10 | j = json.loads(open('static/people.json').read()) 11 | 12 | years = range(2007, 2015) 13 | 14 | poster_dict = {} 15 | 16 | def poster_url(code): 17 | b = BeautifulSoup(requests.get('http://movie.naver.com/movie/bi/pi/basic.nhn?st=1&code='+str(code)).text) 18 | try: 19 | img = b.findAll('div', {'class':'poster'})[0].find('img') 20 | src = img.attrs['src'] 21 | src = src[src.find('&q=')+3:] 22 | print src, img.attrs['alt'] 23 | return [src, img.attrs['alt']] 24 | except: 25 | return ['http://static.naver.net/movie/2012/06/dft_img111x139.png',''] 26 | 27 | for year in years: 28 | print year 29 | 30 | movies = defaultdict(int) 31 | dates = defaultdict(int) 32 | 33 | movie_title = {} 34 | 35 | start_year = '%s0101' % year 36 | end_year = '%s1231' % year 37 | 38 | for i in j: 39 | dd = int(i['date'][-2:]) 40 | if dd % SKIP_DATE == 0 or dd in [28,29,30,31]: 41 | if start_year < i['date'] < end_year: 42 | if i['rank'] > MAX_RANK: 43 | continue 44 | url = i['url'] 45 | code = int(url[url.index('code=')+5:]) 46 | movies[code] += 1 47 | movie_title[code] = i['name'] 48 | dates[i['date']] += 1 49 | 50 | movies_key = movies.keys() 51 | movies_key.sort() 52 | 53 | dates = dates.keys() 54 | dates.sort() 55 | 56 | print "Distinct movies : %s" % len(movies_key) 57 | print "Distinct dates : %s" % len(dates) 58 | 59 | y1 = np.zeros((len(movies_key),len(dates)), dtype='int16') 60 | 61 | new_movie_title = {} 62 | 63 | movies_key = [i[0] for i in sorted(movies.iteritems(), key=lambda (k,v): v,reverse=True)] 64 | 65 | for i in j: 66 | dd = int(i['date'][-2:]) 67 | if dd % SKIP_DATE == 0 or dd in [28,29,30,31]: 68 | if start_year < i['date'] < end_year: 69 | url = i['url'] 70 | code = int(url[url.index('code=')+5:]) 71 | try: 72 | url = poster_dict[code] 73 | except: 74 | url = poster_dict[code] = poster_url(code) 75 | try: 76 | new_code = movies_key.index(code) 77 | except: 78 | continue 79 | date = dates.index(i['date']) 80 | new_movie_title[new_code] = [code, url, movie_title[code]] 81 | y1[new_code][date] = int(i['rank']) 82 | 83 | ans = {'movies' : new_movie_title, 84 | 'mindate' : dates[0], 85 | 'maxdate' : dates[-1], 86 | 'skipdate' : SKIP_DATE, 87 | 'y1' : y1.tolist() } 88 | 89 | with open('people-%s.json' % (year),'w') as f: 90 | json.dump(ans, f) 91 | -------------------------------------------------------------------------------- /scrapy/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/scrapy/.DS_Store -------------------------------------------------------------------------------- /scrapy/0_filter_movie.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import json 3 | import sys 4 | 5 | if len(sys.argv) < 2: 6 | print "Error: need 1 argument" 7 | sys.exit(1) 8 | 9 | f=open(sys.argv[1], 'r') 10 | j=json.loads(f.read()) 11 | f.close() 12 | 13 | movie_dict = {} 14 | 15 | for idx, i in enumerate(j): 16 | print "[%s/%s] %s. %s" % (idx, len(j), i['rank'], i['name']) 17 | 18 | i_copy = i.copy() 19 | i_copy.pop('date') 20 | 21 | try: 22 | ovie_dict[i['date']].append(i_copy) 23 | except: 24 | movie_dict[i['date']] = [i_copy] 25 | 26 | f_name = "filtered_%s" % sys.argv[1] 27 | 28 | f=open(f_name, 'w') 29 | json.dump(movie_dict, f) 30 | f.close() 31 | 32 | print "Finished!" 33 | -------------------------------------------------------------------------------- /scrapy/music_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #mode = TOTAL, DOMESTIC, OVERSEA 3 | scrapy crawl music -a mode=TOTAL -o music_total.json 4 | scrapy crawl music -a mode=DOMESTIC -o music_domestic.json 5 | scrapy crawl music -a mode=OVERSEA -o music_oversea.json 6 | -------------------------------------------------------------------------------- /scrapy/people_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | scrapy crawl people -o people.json 3 | -------------------------------------------------------------------------------- /scrapy/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in 0 1 2 3 4 5 6 7 8 10 11 12 13 14 15 16 17 18 19 3 | do 4 | scrapy crawl rank -a tg=$i -o movie$i.json 5 | done 6 | -------------------------------------------------------------------------------- /scrapy/scrapy.cfg: -------------------------------------------------------------------------------- 1 | # Automatically created by: scrapy startproject 2 | # 3 | # For more information about the [deploy] section see: 4 | # http://doc.scrapy.org/en/latest/topics/scrapyd.html 5 | 6 | [settings] 7 | default = tutorial.settings 8 | 9 | [deploy] 10 | #url = http://localhost:6800/ 11 | project = tutorial 12 | -------------------------------------------------------------------------------- /scrapy/tutorial/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/scrapy/tutorial/.DS_Store -------------------------------------------------------------------------------- /scrapy/tutorial/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/scrapy/tutorial/__init__.py -------------------------------------------------------------------------------- /scrapy/tutorial/items.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Define here the models for your scraped items 4 | # 5 | # See documentation in: 6 | # http://doc.scrapy.org/en/latest/topics/items.html 7 | 8 | import scrapy 9 | 10 | 11 | class TutorialItem(scrapy.Item): 12 | # define the fields for your item here like: 13 | # name = scrapy.Field() 14 | pass 15 | -------------------------------------------------------------------------------- /scrapy/tutorial/pipelines.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Define your item pipelines here 4 | # 5 | # Don't forget to add your pipeline to the ITEM_PIPELINES setting 6 | # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html 7 | 8 | 9 | class TutorialPipeline(object): 10 | def process_item(self, item, spider): 11 | return item 12 | -------------------------------------------------------------------------------- /scrapy/tutorial/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Scrapy settings for tutorial project 4 | # 5 | # For simplicity, this file contains only the most important settings by 6 | # default. All the other settings are documented here: 7 | # 8 | # http://doc.scrapy.org/en/latest/topics/settings.html 9 | # 10 | 11 | BOT_NAME = 'tutorial' 12 | 13 | SPIDER_MODULES = ['tutorial.spiders'] 14 | NEWSPIDER_MODULE = 'tutorial.spiders' 15 | 16 | # Crawl responsibly by identifying yourself (and your website) on the user-agent 17 | #USER_AGENT = 'tutorial (+http://www.yourdomain.com)' 18 | -------------------------------------------------------------------------------- /scrapy/tutorial/spiders/__init__.py: -------------------------------------------------------------------------------- 1 | # This package will contain the spiders of your Scrapy project 2 | # 3 | # Please refer to the documentation for information on how to create and manage 4 | # your spiders. 5 | -------------------------------------------------------------------------------- /scrapy/tutorial/spiders/music.py: -------------------------------------------------------------------------------- 1 | __author__ = 'carpedm20' 2 | __date__ = '2014.07.25' 3 | 4 | from scrapy.spider import BaseSpider 5 | 6 | # http://music.naver.com/listen/history/index.nhn?type=TOTAL&year=2008&month=01&week=3 7 | 8 | from scrapy.item import Item, Field 9 | 10 | class Music(Item): 11 | name = Field() 12 | artist = Field() 13 | artist_id = Field() 14 | track_id = Field() 15 | album_id = Field() 16 | rank = Field() 17 | date = Field() 18 | 19 | #tgs = range(20) 20 | #tgs.remove(9) 21 | 22 | def make_urls(mode = "TOTAL"): 23 | # mode = TOTAL, DOMESTIC, OVERSEA 24 | base = "http://music.naver.com/listen/history/index.nhn?type=%s&year=%s&month=%s&week=%s" 25 | urls = [] 26 | 27 | for year in xrange(2008, 2015): 28 | for month in xrange(1, 13): 29 | for week in xrange(1,5): 30 | url = base % (mode, year, month, week) 31 | 32 | urls.append(url) 33 | 34 | print "[*] length of urls : %s" % len(urls) 35 | 36 | return urls 37 | 38 | import re 39 | import urlparse 40 | 41 | class RankSpider(BaseSpider): 42 | name = "music" 43 | allowed_domains = ["movie.naver.com"] 44 | start_urls = None 45 | 46 | def __init__(self, mode="TOTAL"): 47 | self.start_urls = make_urls(mode) 48 | 49 | def parse(self, response): 50 | parsed = urlparse.urlparse(response.url) 51 | dic = urlparse.parse_qs(parsed.query) 52 | 53 | date = dic['year'][0] + dic['month'][0] + dic['week'][0] 54 | 55 | items = [] 56 | 57 | hrefs = response.xpath("//tbody/tr/td[@class='title']/a/@href").extract() 58 | titles = response.xpath("//tbody/tr/td[@class='name']//span[@class='ellipsis']/text()").extract() 59 | 60 | for idx, elem in enumerate(response.xpath("//tbody/tr")[1:]): 61 | music = elem.xpath("./td[@class='name']") 62 | 63 | href = music.xpath("./a/@href")[0].extract() 64 | album_id = int(re.findall(r'\d+',href)[0]) 65 | 66 | href = music.xpath("./a/@href")[-1].extract() 67 | track_id = int(re.findall(r'\d+',href)[0]) 68 | 69 | artist = elem.xpath("./td[@class='_artist artist']") 70 | 71 | if len(artist) == 0: 72 | artist = elem.xpath("./td[@class='_artist artist no_ell2']") 73 | artist_id = -1 74 | 75 | artist_name = artist.xpath("./a/text()")[0].extract() 76 | else: 77 | try: 78 | href = artist.xpath("./a/@href")[0].extract() 79 | artist_id = int(href[href.find('artistId=')+9:]) 80 | 81 | artist_name = artist.xpath("./a/span/text()")[0].extract().strip() 82 | except: 83 | artist_name = artist.xpath("./span/span/text()")[0].extract().strip() 84 | artist_id = -1 85 | 86 | try: 87 | music_name = music.xpath("./a/span/text()")[0].extract() 88 | except: 89 | music_name = music.xpath("./span/span/text()")[0].extract() 90 | #print idx 91 | 92 | music= Music() 93 | music['name'] = music_name 94 | music['artist'] = artist_name 95 | music['artist_id'] = artist_id 96 | music['track_id'] = track_id 97 | music['album_id'] = album_id 98 | music['rank'] = idx + 1 99 | music['date'] = date 100 | 101 | items.append(music) 102 | 103 | return items 104 | -------------------------------------------------------------------------------- /scrapy/tutorial/spiders/people.py: -------------------------------------------------------------------------------- 1 | __author__ = 'carpedm20' 2 | __date__ = '2014.07.25' 3 | 4 | from scrapy.spider import BaseSpider 5 | 6 | # http://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cnt&date=20050207&tg=0 7 | 8 | from scrapy.item import Item, Field 9 | 10 | class Movie(Item): 11 | name = Field() 12 | url = Field() 13 | rank = Field() 14 | date = Field() 15 | 16 | #tgs = range(20) 17 | #tgs.remove(9) 18 | 19 | def make_urls(): 20 | #url = "http://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cnt&date=%s&tg=%s" 21 | url = "http://movie.naver.com/movie/sdb/rank/rpeople.nhn?date=%s" 22 | urls = [] 23 | 24 | from datetime import date, timedelta 25 | 26 | current_date = date(2005, 2, 7) 27 | end_date = date.today() 28 | delta = timedelta(days=1) 29 | 30 | while current_date <= end_date: 31 | urls.append(url % (current_date.strftime("%Y%m%d"))) 32 | current_date += delta 33 | 34 | print "[*] length of urls : %s" % len(urls) 35 | 36 | return urls 37 | 38 | import urlparse 39 | 40 | class RankSpider(BaseSpider): 41 | name = "people" 42 | allowed_domains = ["movie.naver.com"] 43 | start_urls = None 44 | 45 | def __init__(self): 46 | self.start_urls = make_urls() 47 | 48 | def parse(self, response): 49 | parsed = urlparse.urlparse(response.url) 50 | date = urlparse.parse_qs(parsed.query)['date'] 51 | 52 | items = [] 53 | 54 | hrefs = response.xpath("//tbody/tr/td[@class='title']/a/@href").extract() 55 | titles = response.xpath("//tbody/tr/td[@class='title']/a/text()").extract() 56 | 57 | for index, href in enumerate(hrefs): 58 | movie = Movie() 59 | movie['url'] = href 60 | movie['name'] = titles[index] 61 | movie['rank'] = index + 1 62 | movie['date'] = date[0] 63 | items.append(movie) 64 | 65 | return items 66 | -------------------------------------------------------------------------------- /scrapy/tutorial/spiders/spider.py: -------------------------------------------------------------------------------- 1 | __author__ = 'carpedm20' 2 | __date__ = '2014.07.25' 3 | 4 | from scrapy.spider import BaseSpider 5 | from scrapy.selector import HtmlXPathSelector 6 | 7 | # http://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cnt&date=20050207&tg=0 8 | 9 | from scrapy.item import Item, Field 10 | 11 | class Movie(Item): 12 | name = Field() 13 | url = Field() 14 | rank = Field() 15 | date = Field() 16 | 17 | #tgs = range(20) 18 | #tgs.remove(9) 19 | 20 | def make_urls(tg): 21 | url = "http://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=cnt&date=%s&tg=%s" 22 | urls = [] 23 | 24 | from datetime import date, timedelta 25 | 26 | current_date = date(2005, 2, 7) 27 | end_date = date.today() 28 | delta = timedelta(days=1) 29 | 30 | while current_date <= end_date: 31 | urls.append(url % (current_date.strftime("%Y%m%d"), tg)) 32 | current_date += delta 33 | 34 | print "[*] length of urls : %s" % len(urls) 35 | 36 | return urls 37 | 38 | import urlparse 39 | 40 | class RankSpider(BaseSpider): 41 | name = "rank" 42 | allowed_domains = ["movie.naver.com"] 43 | start_urls = None 44 | 45 | def __init__(self, tg='0'): 46 | self.tg = tg 47 | self.start_urls = make_urls(self.tg) 48 | 49 | def parse(self, response): 50 | parsed = urlparse.urlparse(response.url) 51 | date = urlparse.parse_qs(parsed.query)['date'] 52 | 53 | hxs = HtmlXPathSelector(response) 54 | items = [] 55 | 56 | hrefs = hxs.xpath("//tbody/tr/td/div/a/@href").extract() 57 | titles = hxs.xpath("//tbody/tr/td/div/a/text()").extract() 58 | 59 | for index, href in enumerate(hrefs): 60 | movie = Movie() 61 | movie['url'] = href 62 | movie['name'] = titles[index] 63 | movie['rank'] = index + 1 64 | movie['date'] = date[0] 65 | items.append(movie) 66 | 67 | return items 68 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import url_for, redirect, render_template 3 | app = Flask(__name__, static_url_path="/carpedm20/vox/static",) 4 | 5 | import re 6 | from glob import glob 7 | 8 | PREFIX = "carpedm20/" 9 | STATIC = "%sstatic" % PREFIX 10 | 11 | @app.route('/') 12 | @app.route('/carpedm20/') 13 | def root(): 14 | return redirect(url_for('index')) 15 | 16 | @app.route('/carpedm20/vox/') 17 | def index(): 18 | global PREFIX, STATIC 19 | 20 | years = glob("./static/all-*.json") 21 | years = [re.findall(r'\d+',year)[0] for year in years] 22 | years.sort(reverse=True) 23 | 24 | return render_template('index.html', years = years, 25 | fb_href = "http://pail.unist.ac.kr/carpedm20/vox/", 26 | body_class = 'yir-generic', 27 | nav_bar_class = 'nav-bar', 28 | footer = 'darken-1') 29 | 30 | @app.route('/carpedm20/music/') 31 | def music(): 32 | global PREFIX, STATIC 33 | 34 | years = glob("./static/total-*.json") 35 | years = [re.findall(r'\d+',year)[0] for year in years] 36 | years.sort(reverse=True) 37 | 38 | return render_template('music.html', years = years, 39 | fb_href = "http://pail.unist.ac.kr/carpedm20/music/", 40 | body_class = 'yir-generic-music', 41 | nav_bar_class = 'nav-bar-music', 42 | footer = 'lighten-2') 43 | 44 | if __name__ == '__main__': 45 | app.run(host='0.0.0.0', debug=True, port=5000) 46 | -------------------------------------------------------------------------------- /static/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/Thumbs.db -------------------------------------------------------------------------------- /static/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/color.png -------------------------------------------------------------------------------- /static/color_ko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/color_ko.png -------------------------------------------------------------------------------- /static/insight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/insight.png -------------------------------------------------------------------------------- /static/insight_ko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/insight_ko.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/logo.png -------------------------------------------------------------------------------- /static/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/main.png -------------------------------------------------------------------------------- /static/main2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/main2.png -------------------------------------------------------------------------------- /static/music.js: -------------------------------------------------------------------------------- 1 | window.mobilecheck = function() { 2 | var check = false; 3 | (function(a,b){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); 4 | return check; 5 | } 6 | 7 | if (window.mobilecheck()) { 8 | alert("VoxMusic은 모바일 브라우저를 지원하지 않습니다"); 9 | } 10 | 11 | progressJs().start(); 12 | 13 | current_section = 3; 14 | global_type = 'wiggle'; 15 | 16 | window.onresize = function(event) { 17 | $("#sticker").css('width', $("#base").width()+20); 18 | $("#sticker").css('height', $(".section").height()); 19 | 20 | $("#sticker").sticky({ 21 | topSpacing : 100, 22 | bottomSpacing: document.getElementById('footer').scrollHeight + 10, 23 | }); 24 | 25 | $("svg").each(function() { 26 | /*var width = $(this).width(); 27 | 28 | array = this.getAttribute("viewBox").match(/\d+/g); 29 | array[2] = width; 30 | this.setAttribute("viewBox",array.join(" "));*/ 31 | }); 32 | }; 33 | 34 | var move_to = function(id) { 35 | $('html,body').animate({scrollTop: $(id).offset().top - 40 - $("h2").height()}); 36 | }; 37 | 38 | $(document).ready(function() { 39 | $("#fullpage").fullpage({ 40 | autoScrolling: false, 41 | onLeave: function(index, nextIndex, direction){ 42 | current_section = nextIndex; 43 | console.log(current_section); 44 | 45 | var chart = charts[current_section-3]; 46 | 47 | if (global_type == 'zero' && chart.type == 'wiggle') { 48 | chart.zero_transition(); 49 | } else if (global_type == 'wiggle' && chart.type == 'zero') { 50 | chart.wiggle_transition(); 51 | } 52 | 53 | idx = chart.get_idx(); 54 | $("#title").text(chart.get_layer()[idx].title); 55 | 56 | var node = chart.get_layer()[idx]; 57 | 58 | if (node.type == 0) 59 | $("#naver-link").attr("href", "http://music.naver.com/music/bi/mi/basic.nhn?code="+node.code); 60 | else 61 | $("#naver-link").attr("href", "http://music.naver.com/music/bi/pi/basic.nhn?code="+node.code); 62 | $("#poster").attr('src', chart.get_layer()[idx].url); 63 | } 64 | }); 65 | 66 | $("a.genre").click(function() { 67 | genre = $(this).attr('id'); 68 | 69 | $("a.genre").removeClass('indigo'); 70 | $(this).addClass('indigo'); 71 | 72 | charts = []; 73 | $(".foxoffice").each(function() { 74 | $(this).html('
'); 75 | }); 76 | 77 | update_music(genre, global_type); 78 | }); 79 | 80 | $("a.theme").click(function() { 81 | color = $(this).attr('id'); 82 | 83 | if (color == "cyan") { 84 | color1 = "#006064", 85 | color2 = "#e0f7fa"; 86 | } else if (color == "default") { 87 | color1 = "#045A8D", 88 | color2 = "#F1EEF6"; 89 | } else if (color == "bg") { 90 | color1 = "#263238", 91 | color2 = "#eceff1"; 92 | } else if (color == "teal") { 93 | color1 = "#004d40", 94 | color2 = "#e0f2f1"; 95 | } 96 | for (var idx in charts) { 97 | chart = charts[idx]; 98 | chart.update_color(color1, color2); 99 | } 100 | for (var idx in charts2) { 101 | chart = charts2[idx]; 102 | chart.update_color(color1, color2); 103 | } 104 | }); 105 | 106 | $("#sticker").sticky({ 107 | topSpacing : 100, 108 | bottomSpacing: document.getElementById('footer').scrollHeight + 10, 109 | }); 110 | 111 | $("#sticker").css('width', $("#base").width()+20); 112 | $("#sticker").css('height', $(".section").height()); 113 | 114 | $('#graph-style input:radio').change( function(){ 115 | var chart = charts[current_section-3]; 116 | 117 | var type = $(this).attr('id'); 118 | 119 | if (type == 'test1') { 120 | chart.wiggle_transition(); 121 | global_type = 'wiggle'; 122 | } else if (type == 'test2') { 123 | chart.zero_transition(); 124 | global_type = 'zero'; 125 | } 126 | }); 127 | }); 128 | 129 | var format = d3.time.format("%Y%m%d"); 130 | var format2 = d3.time.format("%b"); 131 | 132 | var charts = []; 133 | var charts2 = []; 134 | 135 | zero_stack = d3.layout.stack() 136 | .offset("zero") 137 | //.offset("silhouette") 138 | .values(function(d) { return d.values; }) 139 | .x(function(d) { return d.x; }) 140 | .y(function(d) { return d.y; }); 141 | 142 | wiggle_stack = d3.layout.stack() 143 | .offset("wiggle") 144 | //.offset("silhouette") 145 | .values(function(d) { return d.values; }) 146 | .x(function(d) { return d.x; }) 147 | .y(function(d) { return d.y; }); 148 | 149 | var Chart = function(year, class_name, genre, type) { 150 | var class_name = class_name; 151 | var year = year; 152 | var cidx; 153 | 154 | var height_base = $(window).height()*0.6; 155 | 156 | var margin = {top: 10, right: 10, bottom: 100, left: 20}, 157 | margin2 = {top: height_base, right: 10, bottom: 20, left: 20}, 158 | width = $("."+class_name).parent().width() - margin.left - margin.right - 3, 159 | height = height_base + 80 - margin.top - margin.bottom, 160 | height2 = height_base + 80 - margin2.top - margin2.bottom; 161 | 162 | var svg, context, focus; 163 | 164 | this.get_svg = function () { 165 | return svg; 166 | } 167 | 168 | var x = d3.scale.linear().range([0, width]), 169 | x2 = d3.scale.linear().range([0, width]), 170 | y = d3.scale.linear().range([height, 0]), 171 | y2 = d3.scale.linear().range([height2, 0]); 172 | 173 | var xAxis = d3.svg.axis().scale(x).orient("bottom") 174 | .ticks(3, function(d, i) {}), 175 | xAxis2 = d3.svg.axis().scale(x2).orient("bottom") 176 | .ticks(16, function(d, i) {}); 177 | 178 | var brush = d3.svg.brush() 179 | .x(x2) 180 | .on("brush", brushed); 181 | 182 | function brushed() { 183 | x.domain(brush.empty() ? x2.domain() : brush.extent()); 184 | focus.select(".x.axis").call(xAxis); 185 | focus.selectAll(".layer").attr("d", function(d) { return area(d.values); }); 186 | }; 187 | 188 | var color = d3.scale.linear() 189 | .range(["#3949ab", "#e8eaf6"]); 190 | 191 | var context_color = "#FC8D59"; 192 | var context_idx = 0; 193 | 194 | this.get_idx = function() { 195 | return context_idx; 196 | } 197 | 198 | var area = d3.svg.area() 199 | //.interpolate("basis") 200 | .x(function(d) { return x(d.x); }) 201 | .y0(function(d) { return y(d.y0); }) 202 | .y1(function(d) { return y(d.y0 + d.y); }); 203 | 204 | var area2 = d3.svg.area() 205 | .x(function(d) { return x2(d.x); }) 206 | .y0(height2) 207 | .y1(function(d) { return y2(d.y); }); 208 | 209 | /*var vertical_line = d3.select("#"+year).append("div") 210 | .attr("class", "remove") 211 | .style("position", "absolute") 212 | .style("z-index", "19") 213 | .style("width", "1px") 214 | .style("height", height+"px") 215 | .style("top", margin.top+"px") 216 | .style("bottom", margin.bottom+"px") 217 | .style("left", "0px") 218 | .style("background", "rgba(255,255,255,0.5)");*/ 219 | 220 | var tooltip = d3.select("#"+year) 221 | .append("div") 222 | .attr("class", "remove") 223 | .style("position", "absolute") 224 | .style("z-index", "20") 225 | .style("visibility", "hidden") 226 | .style("top", "20px") 227 | .style("left", "75px"); 228 | 229 | this.genre = genre; 230 | 231 | var get_year = function() { 232 | return year.match(/\d+/g)[0]; 233 | } 234 | 235 | this.download = false; 236 | 237 | this.get_json = function() { 238 | this.download = false; 239 | 240 | year_number = year.match(/\d+/g)[0]; 241 | d3.json("/carpedm20/vox/static/"+this.genre+"-"+year_number+".json", this.process); 242 | }; 243 | 244 | this.change_genre = function(genre) { 245 | this.genre = genre; 246 | year_number = year.match(/\d+/g)[0]; 247 | d3.json("/carpedm20/vox/static/"+this.genre+"-"+year_number+".json", this.process_update); 248 | }; 249 | 250 | var layers = []; 251 | var layers0 = []; 252 | 253 | this.get_layer = function() { 254 | return layers; 255 | } 256 | 257 | this.type = type; 258 | 259 | this.zero_transition = function() { 260 | this.type = 'zero'; 261 | zer = zero_stack(layers); 262 | y.domain([0, d3.max(zer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })+1.5]); 263 | focus.selectAll(".layer") 264 | .data(function() { 265 | return zer; 266 | }) 267 | .transition() 268 | .duration(2500) 269 | .attr("d", function(d) { return area(d.values); }); 270 | } 271 | 272 | this.wiggle_transition = function() { 273 | this.type = 'wiggle'; 274 | wig = wiggle_stack(layers); 275 | y.domain([0, d3.max(wig, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })]); 276 | focus.selectAll(".layer") 277 | .data(function() { 278 | return wig; 279 | }) 280 | .transition() 281 | .duration(2500) 282 | .attr("d", function(d) { return area(d.values); }); 283 | } 284 | 285 | this.transition = function() { 286 | focus.selectAll(".layer") 287 | .data(function() { 288 | d = layers0; 289 | layers0 = layers; 290 | return layers = d; 291 | }) 292 | .transition() 293 | .duration(2500) 294 | .attr("d", function(d) { return area(d.values); }); 295 | } 296 | 297 | this.update_color = function(color1, color2) { 298 | color = d3.scale.linear() 299 | .range([color1, color2]); 300 | 301 | color.domain([0, layers.length]); 302 | 303 | focus.selectAll("path") 304 | .style("fill", function(d) { 305 | if (d.idx == context_idx) 306 | return context_color; 307 | else 308 | return color(d.idx); 309 | }); 310 | }; 311 | 312 | var update_context = function(idx) { 313 | context_idx = idx; 314 | 315 | path = context.select("path"); 316 | 317 | y2.domain([0, d3.max(layers[context_idx].values, function(value) { 318 | return value.y; 319 | })]); 320 | 321 | if (path[0][0] == null) 322 | path = context.append("path"); 323 | 324 | path.datum(layers[idx]) 325 | .attr("class", "area") 326 | .attr("d", function(d) { return area2(d.values); }) 327 | .style("fill", function(d) { return context_color; }); 328 | 329 | context.selectAll("g").remove(); 330 | 331 | context.append("g") 332 | .attr("class", "x brush") 333 | .call(brush) 334 | .selectAll("rect") 335 | .attr("y", -6) 336 | .attr("height", height2 + 7); 337 | context.append("g") 338 | .attr("class", "x axis") 339 | .attr("transform", "translate(0," + height2 + ")") 340 | .call(xAxis2); 341 | 342 | focus.selectAll("path") 343 | .style("fill", function(d) { 344 | if (d.idx == idx) 345 | return context_color; 346 | else 347 | return color(d.idx); 348 | }); 349 | }; 350 | 351 | var clicked_idx = 0; 352 | 353 | this.process = function(error, data) { 354 | d3.select("#"+year).html(''); 355 | 356 | var big_width = width + margin.left + margin.right, 357 | big_height = height + margin.top + margin.bottom; 358 | 359 | svg = d3.select("#"+year).append("svg") 360 | .attr("preserveAspectRatio", "xMinYMin") 361 | .attr("viewBox", "0 0 "+big_width+" "+big_height) 362 | .attr("width", "100%") 363 | //.attr("width", big_width) 364 | .attr("height", "80%"); 365 | 366 | context = svg.append("g") 367 | .attr("class", "context") 368 | .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")"); 369 | 370 | focus = svg.append("g") 371 | .attr("class", "focus") 372 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 373 | 374 | mindate = format.parse(get_year()+'0101'); 375 | maxdate = format.parse(get_year()+'1231'); 376 | skipdate = 7; 377 | y1 = data['y1']; 378 | 379 | xAxis.tickFormat(function(d) { 380 | date = new Date(mindate); 381 | date.setDate(mindate.getDate() + d*skipdate); 382 | return format2(date); 383 | }); 384 | 385 | xAxis2.tickFormat(function(d) { 386 | date = new Date(mindate); 387 | date.setDate(mindate.getDate() + d*skipdate); 388 | return format2(date); 389 | }); 390 | 391 | if (class_name == 'people') 392 | node_type = 1; 393 | else 394 | node_type= 0; 395 | 396 | for (var idx in y1) { 397 | for (var jdx in y1[idx]) { 398 | if (typeof layers[idx] == 'undefined') { 399 | if (class_name == 'people') { 400 | artist_code = data['musics'][idx][0].toString(); 401 | } else { 402 | artist_code = data['musics'][idx][3].toString(); 403 | } 404 | 405 | if (artist_code.length == 5) 406 | a = "0"+artist_code.slice(0,2); 407 | else if (artist_code.length == 4) 408 | a = "00"+artist_code.slice(0,1); 409 | else 410 | a = artist_code.slice(0,3); 411 | 412 | if (class_name == 'people') { 413 | layers[idx] = {title: data['musics'][idx][1], 414 | code : artist_code, 415 | type : node_type, 416 | url : "http://image.music.naver.net/artist/240/000/"+a+"/"+artist_code+".jpg", 417 | idx : idx,values:[]}; 418 | layers0[idx] = {title: data['musics'][idx][1], 419 | code : artist_code, 420 | type : node_type, 421 | url : "http://image.music.naver.net/artist/240/000/"+a+"/"+artist_code+".jpg", 422 | idx : idx,values:[]}; 423 | } else { 424 | layers[idx] = {title: data['musics'][idx][1], 425 | artist: data['musics'][idx][2], 426 | album_id: data['musics'][idx][3], 427 | artist_id: data['musics'][idx][4], 428 | url : "http://image.music.naver.net/album/204/000/"+a+"/"+data['musics'][idx][3]+".jpg", 429 | code : data['musics'][idx][0], 430 | type : node_type, 431 | idx : idx,values:[]}; 432 | layers0[idx] = {title: data['musics'][idx][1], 433 | artist: data['musics'][idx][2], 434 | album_id: data['musics'][idx][3], 435 | artist_id: data['musics'][idx][4], 436 | url : "http://image.music.naver.net/album/204/000/"+a+"/"+data['musics'][idx][3]+".jpg", 437 | code : data['musics'][idx][0], 438 | type : node_type, 439 | idx : idx,values:[]}; 440 | } 441 | } 442 | tmp = y1[idx][jdx]; 443 | tmp = 1.0/tmp == Infinity ? 0 : 1.0/tmp; 444 | layers[idx].values.push({title:data['musics'][idx][1], 445 | x: Number(jdx), 446 | y: tmp}); 447 | layers0[idx].values.push({title:data['musics'][idx][1], 448 | x: Number(jdx), 449 | y: 0}); 450 | } 451 | } 452 | 453 | var n = layers.length; 454 | 455 | if (global_type == 'wiggle') { 456 | var stacked_layer = wiggle_stack(layers); 457 | y.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })]); 458 | } else { 459 | var stacked_layer = zero_stack(layers); 460 | y.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); }) + 1.5]); 461 | } 462 | 463 | x.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.x; }); })]); 464 | x2.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.x; }); })]); 465 | 466 | color.domain([0, layers.length]); 467 | 468 | focus.selectAll(".layer") 469 | .data(stacked_layer) 470 | .enter().append("path") 471 | .attr("class", "layer") 472 | .attr("d", function(d) { return area(d.values); }) 473 | .style("fill", function(d) { 474 | if (d.idx == context_idx) 475 | return context_color; 476 | else 477 | return color(d.idx); 478 | }) 479 | .append("title") 480 | .text(function (d,i) { return d.title; }); 481 | 482 | focus.append("g") 483 | .attr("class", "x axis") 484 | .attr("transform", "translate(0," + height + ")") 485 | .call(xAxis); 486 | 487 | update_context(clicked_idx); 488 | 489 | context.append("g") 490 | .attr("class", "x brush") 491 | .call(brush) 492 | .selectAll("rect") 493 | .attr("y", -6) 494 | .attr("height", height2 + 7); 495 | 496 | svg.selectAll(".layer") 497 | .attr("opacity", 1) 498 | .on("dblclick", function(d, i) { 499 | var win = window.open($("#naver-link").attr('href'), '_blank'); 500 | win.focus(); 501 | }) 502 | .on("mouseover", function(d, i) { 503 | change_poster_specific(d.title, d.code, d.url, d.artist, d.artist_id, d.album_id); 504 | /*svg.selectAll(".layer") 505 | .attr("opacity", function(d, j) { 506 | return j != i ? 0.8 : 1; 507 | }) 508 | 509 | mousex = d3.mouse(this); 510 | mousex = mousex[0] + 15; 511 | vertical_line.style("left", mousex + "px");*/ 512 | }) 513 | .on("mousemove", function(d, i){ 514 | /*mousex = d3.mouse(this); 515 | mousex = mousex[0] + 15; 516 | vertical_line.style("left", mousex + "px" ); 517 | 518 | current_x = Math.floor(x.invert(d3.mouse(this)[0])); 519 | var list = []; 520 | 521 | for (var idx in layers) { 522 | layer = layers[idx]; 523 | 524 | list.push(layer.values[current_x]); 525 | } 526 | 527 | var sorted = list.sort(function (a,b) { return (b.y > a.y); }); 528 | 529 | body = '' 530 | 531 | for (var idx in [0,1,2,3,4]) { 532 | body += sorted[idx].title[1] + '
'; 533 | } 534 | 535 | html = "

" + body + "

"; 536 | tooltip.html(html).style("visibility", "visible");*/ 537 | }) 538 | .on("click", function(d, i) { 539 | clicked_idx = d.idx; 540 | 541 | update_context(d.idx); 542 | change_poster(); 543 | 544 | cidx = get_cidx(); 545 | 546 | if (class_name == 'people') 547 | charts_to_find = charts2; 548 | else 549 | charts_to_find = charts; 550 | 551 | for (var idx in charts_to_find) { 552 | if (Number(idx) != cidx) { 553 | var chart = charts_to_find[idx]; 554 | 555 | var title = d.title; 556 | var idx = title.indexOf("("); 557 | 558 | if (idx != -1) 559 | title = title.slice(0, title.indexOf("(")).trim() 560 | result = chart.find_code(d.code, title); 561 | } 562 | } 563 | }) 564 | .on("mouseout", function(d, i) { 565 | svg.selectAll(".layer") 566 | .attr("opacity", "1"); 567 | d3.select(this) 568 | .classed("hover", false) 569 | .attr("stroke-width", "0px"); 570 | 571 | update_context(clicked_idx); 572 | change_poster(); 573 | 574 | /*html = "

" + d.title + "
" + d.code + "

"; 575 | tooltip.html(html).style("visibility", "hidden");*/ 576 | }); 577 | change_poster(); 578 | 579 | this.download = true; 580 | 581 | progressJs().increase(18); 582 | 583 | var cidx = get_cidx() + 1; 584 | 585 | if (cidx == 7) {// && class_name == 'people') { 586 | progressJs().end(); 587 | } 588 | if (cidx < charts.length) { 589 | if (class_name == 'people') { 590 | next_chart = charts2[cidx]; 591 | } else 592 | next_chart = charts[cidx]; 593 | next_chart.get_json(); 594 | } 595 | }; 596 | 597 | var get_cidx = function() { 598 | return cidx; 599 | } 600 | 601 | this.set_cidx = function(idx) { 602 | cidx = idx; 603 | } 604 | 605 | this.find_code = function(code, title) { 606 | for (var idx in layers) { 607 | layer = layers[idx]; 608 | 609 | if (layer.code == code) { 610 | var year = get_year(); 611 | 612 | if (class_name == 'people') 613 | var id = "#peo-" + year; 614 | else 615 | var id = "#mov-" + year; 616 | 617 | toast("" + title + " found in " + year + "", 4000); 618 | 619 | clicked_idx = layer.idx; 620 | update_context(layer.idx); 621 | } 622 | } 623 | } 624 | 625 | var change_poster = function() { 626 | $("#title").text(layers[context_idx].title); 627 | 628 | var data = layers[context_idx]; 629 | if (layers[context_idx].type == 0) { 630 | $("#naver-link").attr('href', "http://music.naver.com/album/index.nhn?albumId="+data.album_id+"&trackId="+data.code); 631 | $("#artist").attr('href', "http://music.naver.com/artist/home.nhn?artistId="+data.artist_id); 632 | $("#artist").text(data.artist); 633 | } else { 634 | $("#naver-link").attr('href', "http://music.naver.com/artist/home.nhn?artistId="+data.code); 635 | $("#artist").text('') 636 | $("#artist").attr('href', ""); 637 | } 638 | $("#poster").attr('src', layers[context_idx].url); 639 | } 640 | 641 | var change_poster_specific = function(title, code, url, artist, artist_id, album_id) { 642 | $("#title").text(title); 643 | if (class_name != 'people') { 644 | $("#naver-link").attr('href', "http://music.naver.com/album/index.nhn?albumId="+album_id+"&trackId="+code); 645 | $("#artist").attr('href', "http://music.naver.com/artist/home.nhn?artistId="+artist_id); 646 | $("#artist").text(artist); 647 | } else { 648 | $("#naver-link").attr('href', "http://music.naver.com/artist/home.nhn?artistId="+code); 649 | $("#artist").text('') 650 | $("#artist").attr('href', ""); 651 | } 652 | $("#poster").attr('src', url); 653 | } 654 | }; 655 | 656 | var update_music = function(genre, type) { 657 | 658 | $(".foxoffice").each(function() { 659 | year = $(this).attr('id'); 660 | 661 | chart = new Chart(year, 'foxoffice', genre, type); 662 | chart.set_cidx(charts.length); 663 | charts.push(chart); 664 | }); 665 | 666 | charts[0].get_json(); 667 | } 668 | 669 | var update_people = function(type) { 670 | $(".people").each(function() { 671 | year = $(this).attr('id'); 672 | 673 | chart = new Chart(year, 'people', 'artist', type); 674 | chart.set_cidx(charts2.length); 675 | charts2.push(chart); 676 | }); 677 | 678 | charts2[0].get_json(); 679 | } 680 | 681 | update_music('total', 'wiggle'); 682 | //update_people('people'); 683 | -------------------------------------------------------------------------------- /static/music_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/music_color.png -------------------------------------------------------------------------------- /static/music_color_ko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/music_color_ko.png -------------------------------------------------------------------------------- /static/music_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/music_logo.png -------------------------------------------------------------------------------- /static/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/screenshot.png -------------------------------------------------------------------------------- /static/script.js: -------------------------------------------------------------------------------- 1 | window.mobilecheck = function() { 2 | var check = false; 3 | (function(a,b){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); 4 | return check; 5 | } 6 | 7 | if (window.mobilecheck()) { 8 | alert("Voxoffice는 모바일 브라우저를 지원하지 않습니다"); 9 | } 10 | 11 | progressJs().start(); 12 | 13 | current_section = 3; 14 | global_type = 'wiggle'; 15 | 16 | window.onresize = function(event) { 17 | $("#sticker").css('width', $("#base").width()+20); 18 | $("#sticker").css('height', $(".section").height()); 19 | 20 | $("#sticker").sticky({ 21 | topSpacing : 100, 22 | bottomSpacing: document.getElementById('footer').scrollHeight + 10, 23 | }); 24 | 25 | $("svg").each(function() { 26 | /*var width = $(this).width(); 27 | 28 | array = this.getAttribute("viewBox").match(/\d+/g); 29 | array[2] = width; 30 | this.setAttribute("viewBox",array.join(" "));*/ 31 | }); 32 | }; 33 | 34 | var move_to = function(id) { 35 | $('html,body').animate({scrollTop: $(id).offset().top - 40 - $("h2").height()}); 36 | }; 37 | 38 | $(document).ready(function() { 39 | $("#fullpage").fullpage({ 40 | autoScrolling: false, 41 | onLeave: function(index, nextIndex, direction){ 42 | current_section = nextIndex; 43 | console.log(current_section); 44 | 45 | if (current_section % 2 == 1) { 46 | var chart = charts2[Math.floor(current_section/2)-2]; 47 | } else { 48 | var chart = charts[current_section/2-2]; 49 | } 50 | 51 | if (global_type == 'zero' && chart.type == 'wiggle') { 52 | chart.zero_transition(); 53 | } else if (global_type == 'wiggle' && chart.type == 'zero') { 54 | chart.wiggle_transition(); 55 | } 56 | 57 | idx = chart.get_idx(); 58 | $("#title").text(chart.get_layer()[idx].title); 59 | 60 | var node = chart.get_layer()[idx]; 61 | 62 | if (node.type == 0) 63 | $("#naver-link").attr("href", "http://movie.naver.com/movie/bi/mi/basic.nhn?code="+node.code); 64 | else 65 | $("#naver-link").attr("href", "http://movie.naver.com/movie/bi/pi/basic.nhn?code="+node.code); 66 | $("#poster").attr('src', chart.get_layer()[idx].url); 67 | } 68 | }); 69 | 70 | $("a.genre").click(function() { 71 | genre = $(this).attr('id'); 72 | 73 | $("a.genre").removeClass('teal'); 74 | $(this).addClass('teal'); 75 | 76 | charts = []; 77 | $(".foxoffice").each(function() { 78 | $(this).html('
'); 79 | }); 80 | 81 | update_movie(genre, global_type); 82 | }); 83 | 84 | $("a.theme").click(function() { 85 | color = $(this).attr('id'); 86 | 87 | if (color == "cyan") { 88 | color1 = "#006064", 89 | color2 = "#e0f7fa"; 90 | } else if (color == "default") { 91 | color1 = "#045A8D", 92 | color2 = "#F1EEF6"; 93 | } else if (color == "bg") { 94 | color1 = "#263238", 95 | color2 = "#eceff1"; 96 | } else if (color == "teal") { 97 | color1 = "#004d40", 98 | color2 = "#e0f2f1"; 99 | } 100 | for (var idx in charts) { 101 | chart = charts[idx]; 102 | chart.update_color(color1, color2); 103 | } 104 | for (var idx in charts2) { 105 | chart = charts2[idx]; 106 | chart.update_color(color1, color2); 107 | } 108 | }); 109 | 110 | $("#sticker").sticky({ 111 | topSpacing : 100, 112 | bottomSpacing: document.getElementById('footer').scrollHeight + 10, 113 | }); 114 | 115 | $("#sticker").css('width', $("#base").width()+20); 116 | $("#sticker").css('height', $(".section").height()); 117 | 118 | $('#graph-style input:radio').change( function(){ 119 | if (current_section % 2 == 1) { 120 | var chart = charts2[Math.floor(current_section/2)-2]; 121 | } else { 122 | var chart = charts[current_section/2-2]; 123 | } 124 | 125 | var type = $(this).attr('id'); 126 | 127 | if (type == 'test1') { 128 | chart.wiggle_transition(); 129 | global_type = 'wiggle'; 130 | } else if (type == 'test2') { 131 | chart.zero_transition(); 132 | global_type = 'zero'; 133 | } 134 | }); 135 | }); 136 | 137 | var format = d3.time.format("%Y%m%d"); 138 | var format2 = d3.time.format("%b"); 139 | 140 | var charts = []; 141 | var charts2 = []; 142 | 143 | zero_stack = d3.layout.stack() 144 | .offset("zero") 145 | //.offset("silhouette") 146 | .values(function(d) { return d.values; }) 147 | .x(function(d) { return d.x; }) 148 | .y(function(d) { return d.y; }); 149 | 150 | wiggle_stack = d3.layout.stack() 151 | .offset("wiggle") 152 | //.offset("silhouette") 153 | .values(function(d) { return d.values; }) 154 | .x(function(d) { return d.x; }) 155 | .y(function(d) { return d.y; }); 156 | 157 | var Chart = function(year, class_name, genre, type) { 158 | var class_name = class_name; 159 | var year = year; 160 | var cidx; 161 | 162 | var height_base = $(window).height()*0.6; 163 | 164 | var margin = {top: 10, right: 10, bottom: 100, left: 20}, 165 | margin2 = {top: height_base, right: 10, bottom: 20, left: 20}, 166 | width = $("."+class_name).parent().width() - margin.left - margin.right - 3, 167 | height = height_base + 80 - margin.top - margin.bottom, 168 | height2 = height_base + 80 - margin2.top - margin2.bottom; 169 | 170 | var svg, context, focus; 171 | 172 | var x = d3.scale.linear().range([0, width]), 173 | x2 = d3.scale.linear().range([0, width]), 174 | y = d3.scale.linear().range([height, 0]), 175 | y2 = d3.scale.linear().range([height2, 0]); 176 | 177 | var xAxis = d3.svg.axis().scale(x).orient("bottom") 178 | .ticks(3, function(d, i) {}), 179 | xAxis2 = d3.svg.axis().scale(x2).orient("bottom") 180 | .ticks(8, function(d, i) {}); 181 | 182 | var brush = d3.svg.brush() 183 | .x(x2) 184 | .on("brush", brushed); 185 | 186 | function brushed() { 187 | x.domain(brush.empty() ? x2.domain() : brush.extent()); 188 | focus.select(".x.axis").call(xAxis); 189 | focus.selectAll(".layer").attr("d", function(d) { return area(d.values); }); 190 | }; 191 | 192 | var color = d3.scale.linear() 193 | .range(["#045A8D", "#F1EEF6"]); 194 | 195 | var context_color = "#FC8D59"; 196 | var context_idx = 0; 197 | 198 | this.get_idx = function() { 199 | return context_idx; 200 | } 201 | 202 | var area = d3.svg.area() 203 | //.interpolate("basis") 204 | .x(function(d) { return x(d.x); }) 205 | .y0(function(d) { return y(d.y0); }) 206 | .y1(function(d) { return y(d.y0 + d.y); }); 207 | 208 | var area2 = d3.svg.area() 209 | .x(function(d) { return x2(d.x); }) 210 | .y0(height2) 211 | .y1(function(d) { return y2(d.y); }); 212 | 213 | /*var vertical_line = d3.select("#"+year).append("div") 214 | .attr("class", "remove") 215 | .style("position", "absolute") 216 | .style("z-index", "19") 217 | .style("width", "1px") 218 | .style("height", height+"px") 219 | .style("top", margin.top+"px") 220 | .style("bottom", margin.bottom+"px") 221 | .style("left", "0px") 222 | .style("background", "rgba(255,255,255,0.5)");*/ 223 | 224 | var tooltip = d3.select("#"+year) 225 | .append("div") 226 | .attr("class", "remove") 227 | .style("position", "absolute") 228 | .style("z-index", "20") 229 | .style("visibility", "hidden") 230 | .style("top", "20px") 231 | .style("left", "75px"); 232 | 233 | this.genre = genre; 234 | 235 | var get_year = function() { 236 | return year.match(/\d+/g)[0]; 237 | } 238 | 239 | this.download = false; 240 | 241 | this.get_json = function() { 242 | this.download = false; 243 | 244 | year_number = year.match(/\d+/g)[0]; 245 | d3.json("./static/"+this.genre+"-"+year_number+".json", this.process); 246 | }; 247 | 248 | this.change_genre = function(genre) { 249 | this.genre = genre; 250 | year_number = year.match(/\d+/g)[0]; 251 | d3.json("./static/"+this.genre+"-"+year_number+".json", this.process_update); 252 | }; 253 | 254 | var layers = []; 255 | var layers0 = []; 256 | 257 | this.get_layer = function() { 258 | return layers; 259 | } 260 | 261 | this.type = type; 262 | 263 | this.zero_transition = function() { 264 | this.type = 'zero'; 265 | zer = zero_stack(layers); 266 | y.domain([0, d3.max(zer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })+1.5]); 267 | focus.selectAll(".layer") 268 | .data(function() { 269 | return zer; 270 | }) 271 | .transition() 272 | .duration(2500) 273 | .attr("d", function(d) { return area(d.values); }); 274 | } 275 | 276 | this.wiggle_transition = function() { 277 | this.type = 'wiggle'; 278 | wig = wiggle_stack(layers); 279 | y.domain([0, d3.max(wig, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })]); 280 | focus.selectAll(".layer") 281 | .data(function() { 282 | return wig; 283 | }) 284 | .transition() 285 | .duration(2500) 286 | .attr("d", function(d) { return area(d.values); }); 287 | } 288 | 289 | this.transition = function() { 290 | focus.selectAll(".layer") 291 | .data(function() { 292 | d = layers0; 293 | layers0 = layers; 294 | return layers = d; 295 | }) 296 | .transition() 297 | .duration(2500) 298 | .attr("d", function(d) { return area(d.values); }); 299 | } 300 | 301 | this.update_color = function(color1, color2) { 302 | color = d3.scale.linear() 303 | .range([color1, color2]); 304 | 305 | color.domain([0, layers.length]); 306 | 307 | focus.selectAll("path") 308 | .style("fill", function(d) { 309 | if (d.idx == context_idx) 310 | return context_color; 311 | else 312 | return color(d.idx); 313 | }); 314 | }; 315 | 316 | var update_context = function(idx) { 317 | context_idx = idx; 318 | 319 | path = context.select("path"); 320 | 321 | y2.domain([0, d3.max(layers[context_idx].values, function(value) { 322 | return value.y; 323 | })]); 324 | 325 | if (path[0][0] == null) 326 | path = context.append("path"); 327 | 328 | path.datum(layers[idx]) 329 | .attr("class", "area") 330 | .attr("d", function(d) { return area2(d.values); }) 331 | .style("fill", function(d) { return context_color; }); 332 | 333 | context.selectAll("g").remove(); 334 | 335 | context.append("g") 336 | .attr("class", "x brush") 337 | .call(brush) 338 | .selectAll("rect") 339 | .attr("y", -6) 340 | .attr("height", height2 + 7); 341 | context.append("g") 342 | .attr("class", "x axis") 343 | .attr("transform", "translate(0," + height2 + ")") 344 | .call(xAxis2); 345 | 346 | focus.selectAll("path") 347 | .style("fill", function(d) { 348 | if (d.idx == idx) 349 | return context_color; 350 | else 351 | return color(d.idx); 352 | }); 353 | }; 354 | 355 | var clicked_idx = 0; 356 | 357 | this.process = function(error, data) { 358 | d3.select("#"+year).html(''); 359 | 360 | var big_width = width + margin.left + margin.right, 361 | big_height = height + margin.top + margin.bottom; 362 | 363 | svg = d3.select("#"+year).append("svg") 364 | .attr("preserveAspectRatio", "xMinYMin") 365 | .attr("viewBox", "0 0 "+big_width+" "+big_height) 366 | .attr("width", "100%") 367 | //.attr("width", big_width) 368 | .attr("height", "80%"); 369 | 370 | context = svg.append("g") 371 | .attr("class", "context") 372 | .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")"); 373 | 374 | focus = svg.append("g") 375 | .attr("class", "focus") 376 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 377 | 378 | mindate = format.parse(data['mindate']); 379 | maxdate = format.parse(data['maxdate']); 380 | skipdate = 5; 381 | y1 = data['y1']; 382 | 383 | xAxis.tickFormat(function(d) { 384 | date = new Date(mindate); 385 | date.setDate(mindate.getDate() + d*skipdate); 386 | return format2(date); 387 | }); 388 | 389 | xAxis2.tickFormat(function(d) { 390 | date = new Date(mindate); 391 | date.setDate(mindate.getDate() + d*skipdate); 392 | return format2(date); 393 | }); 394 | 395 | if (class_name == 'people') 396 | node_type = 1; 397 | else 398 | node_type= 0; 399 | 400 | for (var idx in y1) { 401 | for (var jdx in y1[idx]) { 402 | if (typeof layers[idx] == 'undefined') { 403 | a = data['movies'][idx][1]; 404 | layers[idx] = {title : data['movies'][idx][2], 405 | url : data['movies'][idx][1][0], 406 | code : data['movies'][idx][0], 407 | type : node_type, 408 | idx : idx,values:[]}; 409 | layers0[idx] = {title : data['movies'][idx][1], 410 | code : data['movies'][idx][0], 411 | type : node_type, 412 | idx : idx,values:[]}; 413 | } 414 | tmp = y1[idx][jdx]; 415 | tmp = 1.0/tmp == Infinity ? 0 : 1.0/tmp; 416 | layers[idx].values.push({title:data['movies'][idx][1], 417 | x: Number(jdx), 418 | y: tmp}); 419 | layers0[idx].values.push({title:data['movies'][idx][1], 420 | x: Number(jdx), 421 | y: 0}); 422 | } 423 | } 424 | 425 | var n = layers.length; 426 | 427 | if (global_type == 'wiggle') { 428 | var stacked_layer = wiggle_stack(layers); 429 | y.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })]); 430 | } else { 431 | var stacked_layer = zero_stack(layers); 432 | y.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); }) + 1.5]); 433 | } 434 | 435 | x.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.x; }); })]); 436 | x2.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.x; }); })]); 437 | y.domain([0, d3.max(stacked_layer, function(layer) { return d3.max(layer.values, function(d) { return d.y0 + d.y; }); })]); 438 | 439 | color.domain([0, layers.length]); 440 | 441 | focus.selectAll(".layer") 442 | .data(stacked_layer) 443 | .enter().append("path") 444 | .attr("class", "layer") 445 | .attr("d", function(d) { return area(d.values); }) 446 | .style("fill", function(d) { 447 | if (d.idx == context_idx) 448 | return context_color; 449 | else 450 | return color(d.idx); 451 | }) 452 | .append("title") 453 | .text(function (d,i) { return d.title; }); 454 | 455 | focus.append("g") 456 | .attr("class", "x axis") 457 | .attr("transform", "translate(0," + height + ")") 458 | .call(xAxis); 459 | 460 | update_context(clicked_idx); 461 | 462 | context.append("g") 463 | .attr("class", "x brush") 464 | .call(brush) 465 | .selectAll("rect") 466 | .attr("y", -6) 467 | .attr("height", height2 + 7); 468 | 469 | svg.selectAll(".layer") 470 | .attr("opacity", 1) 471 | .on("dblclick", function(d, i) { 472 | var win = window.open($("#naver-link").attr('href'), '_blank'); 473 | win.focus(); 474 | }) 475 | .on("mouseover", function(d, i) { 476 | change_poster_specific(d.title, d.code, d.url); 477 | /*svg.selectAll(".layer") 478 | .attr("opacity", function(d, j) { 479 | return j != i ? 0.8 : 1; 480 | }) 481 | 482 | mousex = d3.mouse(this); 483 | mousex = mousex[0] + 15; 484 | vertical_line.style("left", mousex + "px");*/ 485 | }) 486 | .on("mousemove", function(d, i){ 487 | /*mousex = d3.mouse(this); 488 | mousex = mousex[0] + 15; 489 | vertical_line.style("left", mousex + "px" ); 490 | 491 | current_x = Math.floor(x.invert(d3.mouse(this)[0])); 492 | var list = []; 493 | 494 | for (var idx in layers) { 495 | layer = layers[idx]; 496 | 497 | list.push(layer.values[current_x]); 498 | } 499 | 500 | var sorted = list.sort(function (a,b) { return (b.y > a.y); }); 501 | 502 | body = '' 503 | 504 | for (var idx in [0,1,2,3,4]) { 505 | body += sorted[idx].title[1] + '
'; 506 | } 507 | 508 | html = "

" + body + "

"; 509 | tooltip.html(html).style("visibility", "visible");*/ 510 | }) 511 | .on("click", function(d, i) { 512 | clicked_idx = d.idx; 513 | 514 | update_context(d.idx); 515 | change_poster(); 516 | 517 | cidx = get_cidx(); 518 | 519 | if (class_name == 'people') 520 | charts_to_find = charts2; 521 | else 522 | charts_to_find = charts; 523 | 524 | for (var idx in charts_to_find) { 525 | if (Number(idx) != cidx) { 526 | var chart = charts_to_find[idx]; 527 | 528 | var title = d.title; 529 | var idx = title.indexOf("("); 530 | 531 | if (idx != -1) 532 | title = title.slice(0, title.indexOf("(")).trim() 533 | result = chart.find_code(d.code, title); 534 | } 535 | } 536 | }) 537 | .on("mouseout", function(d, i) { 538 | svg.selectAll(".layer") 539 | .attr("opacity", "1"); 540 | d3.select(this) 541 | .classed("hover", false) 542 | .attr("stroke-width", "0px"); 543 | 544 | update_context(clicked_idx); 545 | change_poster(); 546 | 547 | /*html = "

" + d.title + "
" + d.code + "

"; 548 | tooltip.html(html).style("visibility", "hidden");*/ 549 | }); 550 | change_poster(); 551 | 552 | this.download = true; 553 | 554 | progressJs().increase(6.25); 555 | 556 | var cidx = get_cidx() + 1; 557 | 558 | if (cidx == 8 && class_name == 'people') { 559 | progressJs().end(); 560 | } 561 | if (cidx < charts.length) { 562 | if (class_name == 'people') { 563 | next_chart = charts2[cidx]; 564 | } else 565 | next_chart = charts[cidx]; 566 | next_chart.get_json(); 567 | } 568 | }; 569 | 570 | var get_cidx = function() { 571 | return cidx; 572 | } 573 | 574 | this.set_cidx = function(idx) { 575 | cidx = idx; 576 | } 577 | 578 | this.find_code = function(code, title) { 579 | for (var idx in layers) { 580 | layer = layers[idx]; 581 | 582 | if (layer.code == code) { 583 | var year = get_year(); 584 | 585 | if (class_name == 'people') 586 | var id = "#peo-" + year; 587 | else 588 | var id = "#mov-" + year; 589 | 590 | toast("" + title + " found in " + year + "", 4000); 591 | 592 | clicked_idx = layer.idx; 593 | update_context(layer.idx); 594 | } 595 | } 596 | } 597 | 598 | var change_poster = function() { 599 | $("#title").text(layers[context_idx].title); 600 | if (layers[context_idx].type == 0) 601 | $("#naver-link").attr('href', "http://movie.naver.com/movie/bi/mi/basic.nhn?code="+layers[context_idx].code); 602 | else 603 | $("#naver-link").attr('href', "http://movie.naver.com/movie/bi/pi/basic.nhn?code="+layers[context_idx].code); 604 | $("#poster").attr('src', layers[context_idx].url); 605 | } 606 | 607 | var change_poster_specific = function(title, code, url) { 608 | $("#title").text(title); 609 | if (class_name == 'people') 610 | $("#naver-link").attr('href', "http://movie.naver.com/movie/bi/pi/basic.nhn?code="+code); 611 | else 612 | $("#naver-link").attr('href', "http://movie.naver.com/movie/bi/mi/basic.nhn?code="+code); 613 | $("#poster").attr('src', url); 614 | } 615 | }; 616 | 617 | var update_movie = function(genre, type) { 618 | 619 | $(".foxoffice").each(function() { 620 | year = $(this).attr('id'); 621 | 622 | chart = new Chart(year, 'foxoffice', genre, type); 623 | chart.set_cidx(charts.length); 624 | charts.push(chart); 625 | }); 626 | 627 | charts[0].get_json(); 628 | } 629 | 630 | var update_people = function(type) { 631 | $(".people").each(function() { 632 | year = $(this).attr('id'); 633 | 634 | chart = new Chart(year, 'people', 'people', type); 635 | chart.set_cidx(charts2.length); 636 | charts2.push(chart); 637 | }); 638 | 639 | charts2[0].get_json(); 640 | } 641 | 642 | update_movie('all', 'wiggle'); 643 | update_people('wiggle'); 644 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | @import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css); 2 | 3 | .progressjs-inner { 4 | background-color: #ff9800 !important; 5 | height: 6px !important; 6 | } 7 | 8 | body { 9 | font-family: 'Ubuntu', sans-serif; 10 | min-width: 970px; 11 | font-family: sans-serif; 12 | -ms-text-size-adjust: 100%; 13 | -webkit-text-size-adjust: 100%; 14 | } 15 | 16 | p, a, h1, span, label, text { 17 | font-family: 'Ubuntu', sans-serif; 18 | } 19 | 20 | .music-genre-btn { 21 | background-color: #607d8b; 22 | } 23 | 24 | .toast { 25 | font-size: 0.9em; 26 | background: rgba(20,24,28,.95); 27 | } 28 | 29 | .ko { 30 | font-family: 'Nanum Barun Gothic', 'Nanum Gothic', serif !important; 31 | } 32 | 33 | .ko-title { 34 | font-size: 1.3em !important; 35 | } 36 | 37 | .foxoffice, .people { 38 | width: 100%; 39 | height: 100%; 40 | position: relative; 41 | } 42 | 43 | .grey li a { 44 | color: white; 45 | } 46 | 47 | .logo-text { 48 | font-size: 7rem; 49 | } 50 | 51 | nav .brand-logo { 52 | position: absolute; 53 | color: #fff; 54 | display: inline-block; 55 | font-size: 1.5rem; 56 | padding: 0; 57 | } 58 | 59 | ul.menu li a { 60 | font-size: 1.2em; 61 | } 62 | 63 | 64 | .axis { 65 | shape-rendering: crispEdges; 66 | } 67 | 68 | .x.axis line { 69 | stroke: white; 70 | } 71 | 72 | .x.axis .minor { 73 | stroke-opacity: .5; 74 | } 75 | 76 | .x.axis path { 77 | display: none; 78 | } 79 | 80 | .y.axis line, .y.axis path { 81 | fill: none; 82 | stroke: white; 83 | } 84 | 85 | .brush .extent { 86 | stroke: #fff; 87 | fill-opacity: .125; 88 | shape-rendering: crispEdges; 89 | } 90 | 91 | .yir-generic { 92 | position: relative; 93 | background: #1b2128; 94 | width: 100%; 95 | height: 100%; 96 | color: #9ab; 97 | } 98 | 99 | .nav-bar { 100 | background: rgba(20,24,28,.95); 101 | position: fixed; 102 | z-index: 1; 103 | top: 0; 104 | left: 0; 105 | width: 100%; 106 | padding-right: 50px; 107 | padding-left: 50px; 108 | } 109 | 110 | .yir-generic-music { 111 | position: relative; 112 | background: #ecf0f1; 113 | width: 100%; 114 | height: 100%; 115 | color: #9ab; 116 | } 117 | 118 | .nav-bar-music { 119 | background: rgba(65,91,118,.95); 120 | position: fixed; 121 | z-index: 1; 122 | top: 0; 123 | left: 0; 124 | width: 100%; 125 | padding-right: 50px; 126 | padding-left: 50px; 127 | } 128 | 129 | .text p { 130 | font-family: Vollkorn, Georgia,serif; 131 | font-size: 24px; 132 | font-weight: 400; 133 | line-height: 1.4; 134 | } 135 | 136 | .footnote { 137 | font-family: Ubuntu,sans-serif; 138 | font-size: 18px; 139 | font-weight: 400; 140 | margin: 0 100px; 141 | } 142 | 143 | .footnote a { 144 | color: #def; 145 | } 146 | 147 | label { 148 | font-size: 1.0rem; 149 | } 150 | 151 | .shadow { 152 | width: 25px; 153 | height: 25px; 154 | margin: 20px auto; 155 | } 156 | 157 | a.genre { 158 | height: 32px; 159 | font-size: 0.5rem; 160 | padding-left: 12px; 161 | padding-right: 12px; 162 | margin-right: 2px; 163 | } 164 | 165 | .materialboxed { 166 | width: 100%; 167 | } 168 | 169 | div.about-me { 170 | padding-left: 10px; 171 | } 172 | 173 | div.contact { 174 | padding-left: 30px; 175 | } 176 | 177 | div.footer-copyright { 178 | text-align: right; 179 | } 180 | 181 | div.insight-container { 182 | width: 90%; 183 | } 184 | 185 | div.info-card { 186 | min-height: 250px; 187 | } 188 | 189 | .ubuntu { 190 | font-family: Ubuntu,sans-serif; 191 | } 192 | 193 | .strip-overlay { 194 | background: rgba(68,85,102,.4); 195 | position: absolute; 196 | top: 0; 197 | left: 0; 198 | width: 100%; 199 | height: 100%; 200 | } 201 | 202 | .year-poster { 203 | position: absolute; 204 | top: 0; 205 | left: 0; 206 | width: 100%; 207 | height: 100%; 208 | margin-bottom: 0px; 209 | background-size: cover; 210 | background-position: center; 211 | background-repeat: no-repeat; 212 | } 213 | 214 | .year-poster-img { 215 | height:100%; 216 | } 217 | 218 | .year-poster-content { 219 | width: 90%; 220 | padding-top: 10%; 221 | box-sizing: border-box; 222 | height: 100%; 223 | font-weight: 400; 224 | position: relative; 225 | } 226 | 227 | .year-poster-text { 228 | width: 60%; 229 | float: right; 230 | text-align: right; 231 | } 232 | 233 | .year-poster-text h2 a { 234 | color: #fff; 235 | font-weight: 400; 236 | font-size: 95px; 237 | line-height: 1em; 238 | } 239 | 240 | .year-poster-content h1 span { 241 | font-weight: 400; 242 | font-size: 18px; 243 | line-height: 1; 244 | display: inline-block; 245 | background-color: rgba(0,0,0,.3); 246 | border: 1px solid rgba(255,255,255,.6); 247 | color: #fff; 248 | padding: 5px 8px 6px; 249 | text-transform: uppercase; 250 | letter-spacing: .1em; 251 | white-space: nowrap; 252 | -webkit-box-shadow: 1px 1px 2px rgba(0,0,0,.25); 253 | -moz-box-shadow: 1px 1px 2px rgba(0,0,0,.25); 254 | box-shadow: 1px 1px 2px rgba(0,0,0,.25); 255 | -webkit-border-radius: 2px; 256 | -moz-border-radius: 2px; 257 | border-radius: 2px; 258 | } 259 | 260 | .card a { 261 | margin-right: 0px; 262 | } 263 | 264 | .img-block-strip { 265 | width: 100%; 266 | height: 300px; 267 | position: absolute; 268 | bottom: 0; 269 | left: 0; 270 | overflow: hidden; 271 | } 272 | 273 | .loading { 274 | margin-top: 240px; 275 | margin-bottom: 240px; 276 | } 277 | 278 | .no-bottom { 279 | margin-bottom: 0px; 280 | } 281 | 282 | 283 | .progressjs-inner { 284 | width: 0; 285 | } 286 | .progressjs-progress { 287 | z-index: 9999999; 288 | } 289 | 290 | /* blue theme, like iOS 7 progress bar */ 291 | .progressjs-theme-blue .progressjs-inner { 292 | height: 2px; 293 | -webkit-transition: all 0.3s ease-out; 294 | -moz-transition: all 0.3s ease-out; 295 | -o-transition: all 0.3s ease-out; 296 | transition: all 0.3s ease-out; 297 | background-color: #3498db; 298 | } 299 | .progressjs-theme-blue.progressjs-end { 300 | -webkit-transition: opacity 0.2s ease-out; 301 | -moz-transition: opacity 0.2s ease-out; 302 | -o-transition: opacity 0.2s ease-out; 303 | transition: opacity 0.2s ease-out; 304 | opacity: 0; 305 | } 306 | .progressjs-theme-blue .progressjs-percent { 307 | display: none; 308 | } 309 | 310 | /* blue theme with overlay layer, no percent bar */ 311 | .progressjs-theme-blueOverlay { 312 | background-color: white; 313 | -webkit-transition: all 0.2s ease-out; 314 | -moz-transition: all 0.2s ease-out; 315 | -o-transition: all 0.2s ease-out; 316 | transition: all 0.2s ease-out; 317 | } 318 | .progressjs-theme-blueOverlay .progressjs-inner { 319 | height: 100%; 320 | -webkit-transition: all 0.3s ease-out; 321 | -moz-transition: all 0.3s ease-out; 322 | -o-transition: all 0.3s ease-out; 323 | transition: all 0.3s ease-out; 324 | background-color: #3498db; 325 | } 326 | .progressjs-theme-blueOverlay.progressjs-end { 327 | opacity: 0 !important; 328 | } 329 | .progressjs-theme-blueOverlay .progressjs-percent { 330 | display: none; 331 | } 332 | 333 | /* blue theme with overlay layer, no percent bar */ 334 | .progressjs-theme-blueOverlay { 335 | background-color: white; 336 | -webkit-transition: all 0.2s ease-out; 337 | -moz-transition: all 0.2s ease-out; 338 | -o-transition: all 0.2s ease-out; 339 | transition: all 0.2s ease-out; 340 | } 341 | .progressjs-theme-blueOverlay .progressjs-inner { 342 | height: 100%; 343 | -webkit-transition: all 0.3s ease-out; 344 | -moz-transition: all 0.3s ease-out; 345 | -o-transition: all 0.3s ease-out; 346 | transition: all 0.3s ease-out; 347 | background-color: #3498db; 348 | } 349 | .progressjs-theme-blueOverlay.progressjs-end { 350 | opacity: 0 !important; 351 | } 352 | .progressjs-theme-blueOverlay .progressjs-percent { 353 | display: none; 354 | } 355 | 356 | /* Blue theme with border radius and overlay layer */ 357 | .progressjs-theme-blueOverlayRadius { 358 | background-color: white; 359 | -webkit-transition: all 0.2s ease-out; 360 | -moz-transition: all 0.2s ease-out; 361 | -o-transition: all 0.2s ease-out; 362 | transition: all 0.2s ease-out; 363 | border-radius: 5px; 364 | } 365 | .progressjs-theme-blueOverlayRadius .progressjs-inner { 366 | height: 100%; 367 | -webkit-transition: all 0.3s ease-out; 368 | -moz-transition: all 0.3s ease-out; 369 | -o-transition: all 0.3s ease-out; 370 | transition: all 0.3s ease-out; 371 | background-color: #3498db; 372 | border-radius: 5px; 373 | } 374 | .progressjs-theme-blueOverlayRadius.progressjs-end { 375 | opacity: 0 !important; 376 | } 377 | .progressjs-theme-blueOverlayRadius .progressjs-percent { 378 | display: none; 379 | } 380 | 381 | /* Blue theme with border radius and overlay layer */ 382 | .progressjs-theme-blueOverlayRadiusHalfOpacity { 383 | background-color: white; 384 | opacity: 0.5; 385 | -webkit-transition: all 0.2s ease-out; 386 | -moz-transition: all 0.2s ease-out; 387 | -o-transition: all 0.2s ease-out; 388 | transition: all 0.2s ease-out; 389 | border-radius: 5px; 390 | } 391 | .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-inner { 392 | height: 100%; 393 | -webkit-transition: all 0.3s ease-out; 394 | -moz-transition: all 0.3s ease-out; 395 | -o-transition: all 0.3s ease-out; 396 | transition: all 0.3s ease-out; 397 | background-color: #3498db; 398 | border-radius: 5px; 399 | } 400 | .progressjs-theme-blueOverlayRadiusHalfOpacity.progressjs-end { 401 | opacity: 0 !important; 402 | } 403 | .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-percent { 404 | display: none; 405 | } 406 | 407 | /* Blue theme with border radius, overlay layer and percent bar */ 408 | .progressjs-theme-blueOverlayRadiusWithPercentBar { 409 | background-color: white; 410 | -webkit-transition: all 0.2s ease-out; 411 | -moz-transition: all 0.2s ease-out; 412 | -o-transition: all 0.2s ease-out; 413 | transition: all 0.2s ease-out; 414 | border-radius: 5px; 415 | } 416 | .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-inner { 417 | height: 100%; 418 | -webkit-transition: all 0.3s ease-out; 419 | -moz-transition: all 0.3s ease-out; 420 | -o-transition: all 0.3s ease-out; 421 | transition: all 0.3s ease-out; 422 | background-color: #3498db; 423 | border-radius: 5px; 424 | } 425 | .progressjs-theme-blueOverlayRadiusWithPercentBar.progressjs-end { 426 | opacity: 0 !important; 427 | } 428 | .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-percent { 429 | width: 70px; 430 | text-align: center; 431 | height: 40px; 432 | position: absolute; 433 | right: 50%; 434 | margin-right: -35px; 435 | top: 50%; 436 | margin-top: -20px; 437 | font-size: 30px; 438 | opacity: .5; 439 | } 440 | 441 | .progressjs-theme-blackRadiusInputs { 442 | height: 10px; 443 | border-radius: 10px; 444 | overflow: hidden; 445 | } 446 | .progressjs-theme-blackRadiusInputs .progressjs-inner { 447 | height: 2px; 448 | -webkit-transition: all 1s ease-out; 449 | -moz-transition: all 1s ease-out; 450 | -o-transition: all 1s ease-out; 451 | transition: all 1s ease-out; 452 | background-color: #34495e; 453 | } 454 | .progressjs-theme-blackRadiusInputs.progressjs-end { 455 | -webkit-transition: opacity 0.2s ease-out; 456 | -moz-transition: opacity 0.2s ease-out; 457 | -o-transition: opacity 0.2s ease-out; 458 | transition: opacity 0.2s ease-out; 459 | opacity: 0; 460 | } 461 | .progressjs-theme-blackRadiusInputs .progressjs-percent { 462 | display: none; 463 | } 464 | -------------------------------------------------------------------------------- /static/weight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/weight.png -------------------------------------------------------------------------------- /static/weight_ko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carpedm20/voxoffice/4aa1a08351dba574455148ab3218a617d6bb2734/static/weight_ko.png -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block meta %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | VoxOffice 12 | {% endblock %} 13 | 14 | {% block css %} 15 | .x.axis g text { 16 | fill: white; 17 | } 18 | {% endblock %} 19 | 20 | {% block navbar %} 21 |
  • VoxMusic
  • 22 | {% endblock %} 23 | 24 | {% block script %} 25 | 26 | {% endblock %} 27 | 28 | {% block intro %} 29 |
    30 |
    31 |

    VoxOffice

    32 |
    33 |

    A Data Visualization of Box Office History.
    "What was the greatest movies in 2007?"

    34 |
    35 |
    36 |

    Streamgraph algorithm, colors, and data generation inspired by this paper

    37 |

    * View on Desktop, not on Mobile *

    38 | Github 39 |

    Scroll down to enjoy yourself :)

    40 |
    41 |
    42 |
    43 | 44 |
    45 |
    46 |

    How to read?

    47 |
    48 |
    49 |
    50 |
    51 |
    52 | The bigger, the better (Daily) 53 |
    54 |
    55 | 56 |
    57 |
    58 |
    59 |
    60 |
    61 |
    62 |
    63 | The darker, the better (Yearly) 64 |
    65 |
    66 | 67 |
    68 |
    69 |
    70 |
    71 |
    72 |
    73 |
    74 | 75 |
    76 |
    77 |
    78 |
    79 |
    80 |
    81 | 82 |
    83 |
    84 |

    그래프 읽는법

    85 |
    86 |
    87 |
    88 |
    89 |
    90 | 두꺼울수록 인기가 있었던 영화 (하루 기준) 91 |
    92 |
    93 | 94 |
    95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 | 진할수록 인기가 있었던 영화 (1년 기준) 102 |
    103 |
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 |
    111 |
    112 | 113 |
    114 |
    115 |
    116 |
    117 |
    118 |
    119 | {% endblock %} 120 | 121 | {% block sticker%} 122 |
    123 | 124 | 125 |
    126 | 138 |
    139 |
    140 | 141 |
    142 |
    143 | All 144 | Romance 145 | Action 146 | Thriller 147 | Comedy 148 |
    149 |
    150 |
    151 | 152 |

    153 | 154 |    155 | 156 | 157 |

    158 |
    159 |
    160 | 185 |
    186 |
    187 |
    188 |
    189 | {% endblock %} 190 | 191 | {% block content %} 192 |
    193 | {% for year in years %} 194 |
    195 |

    Movie of {{year}}

    196 |
    197 |
    198 |
    199 |
    200 |
    201 |
    202 |
    203 |
    204 |
    205 |
    206 |
    207 |
    208 |
    209 |
    210 |
    211 |
    212 |
    213 |
    214 |
    215 |

    People of {{year}}

    216 |
    217 |
    218 |
    219 |
    220 |
    221 |
    222 |
    223 |
    224 |
    225 |
    226 |
    227 |
    228 |
    229 |
    230 |
    231 |
    232 |
    233 |
    234 | {% endfor %} 235 |
    236 | {% endblock %} 237 | -------------------------------------------------------------------------------- /templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {% block meta %} {% endblock %} 7 | 8 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 36 | 37 | 38 | 39 |
    40 | 47 | 57 | 58 | {% block intro %}{% endblock %} 59 | 60 |
    61 |
    62 |
    63 | {% block sticker %}{% endblock %} 64 |
    65 |
    66 |
    67 | {% block content %}{% endblock %} 68 |
    69 |
    70 | 71 |
    72 |
    73 |
    74 |
    75 |
    About me
    76 |
    77 | 78 |
    79 | 80 | 81 |
    82 |
    83 |
    84 |
    85 |

    드라마 '유령'을 보고 해킹에 빠진 컴덕. 단순히 해커의 언어라는 이유로 파이썬을 시작했고 그렇게 파이썬이라는 나라가 허락한 유일한 마약에 빠져 파이뽕에 취해있다. 취미는 역시 해킹. 가끔 파이썬 라이브러리를 만들거나 영화를 감상한다.

    86 | Portfolio 87 | Github 88 |
    89 |
    90 |
    91 |
    92 |
    93 |
    Contact
    94 | 95 | 99 |
    100 |
    101 |
    102 | 107 |
    108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | {% block script %} {% endblock %} 116 | 117 | 118 | -------------------------------------------------------------------------------- /templates/music.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | 3 | {% block meta %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | VoxMusic 12 | {% endblock %} 13 | 14 | {% block css %} 15 | .x.axis g text { 16 | fill: #34495e; 17 | font-weight: 600; 18 | } 19 | {% endblock %} 20 | 21 | {% block navbar %} 22 |
  • VoxOffice
  • 23 | {% endblock %} 24 | 25 | {% block script %} 26 | 27 | {% endblock %} 28 | 29 | {% block intro %} 30 |
    31 |
    32 |

    VoxMusic

    33 |
    34 |

    A Data Visualization of Music Chart History.
    "What was the famous music in 2007?"

    35 |
    36 |
    37 |

    Streamgraph algorithm, colors, and data generation inspired by this paper

    38 |

    * View on Desktop, not on Mobile *

    39 | Github 40 |

    Scroll down to enjoy yourself :)

    41 |
    42 |
    43 |
    44 | 45 |
    46 |
    47 |

    How to read?

    48 |
    49 |
    50 |
    51 |
    52 |
    53 |
    54 | The bigger, the better (Daily) 55 |
    56 |
    57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 |
    64 |
    65 | The darker, the better (Yearly) 66 |
    67 |
    68 | 69 |
    70 |
    71 |
    72 |
    73 |
    74 |
    75 |
    76 |
    77 |
    78 | 두꺼울수록 인기가 있었던 음악 (하루 기준) 79 |
    80 |
    81 | 82 |
    83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    89 | 진할수록 인기가 있었던 음악 (1년 기준) 90 |
    91 |
    92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 | 102 | {% endblock %} 103 | 104 | {% block sticker%} 105 |
    106 | 107 | 108 | 125 |
    126 | 127 |
    128 |
    129 | All 130 | Domestic 131 | Oversea 132 |
    133 |
    134 |
    135 | 136 |

    137 | 138 |    139 | 140 | 141 |

    142 |
    143 |
    144 | 169 |
    170 |
    171 |
    172 |
    173 | {% endblock %} 174 | 175 | {% block content %} 176 |
    177 | {% for year in years %} 178 |
    179 |

    {{year}}

    180 |
    181 |
    182 |
    183 |
    184 |
    185 |
    186 |
    187 |
    188 |
    189 |
    190 |
    191 |
    192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 | 218 | {% endfor %} 219 |
    220 | {% endblock %} 221 | --------------------------------------------------------------------------------