From b588c0eb1121e2264c149e8fa8934e6d0b6eb64a Mon Sep 17 00:00:00 2001 From: simon987 Date: Wed, 8 May 2019 20:36:42 -0400 Subject: [PATCH] minimum viable UI. CAA-related scripts --- README.md | 43 ++ ...musicbrains_dump.sh => download_mb_dump.sh | 5 +- extract_covers.py | 27 + generate_caa_tasks.py | 56 ++ generate_lastfm_tasks.py | 48 ++ generate_scrape_tasks.py | 39 -- jenkins/build.sh | 5 +- last.fm | 2 +- import.sh => make_neoj4_db.sh | 4 + make_release_to_rg_map.py | 31 + patch_artists_with_lastfm.py | 74 --- process_lastfm_data.py | 100 +++ convert_mb.py => process_mb_dump.py | 64 +- seed.cypher | 2 + seed_neo4j_db.sh | 5 + task_tracker_drone | 2 +- ui/.eslintrc.js | 6 +- ui/package-lock.json | 363 +++++++++-- ui/package.json | 4 + ui/src/App.vue | 17 +- ui/src/components/ArtistInfo.vue | 54 ++ ui/src/components/HelloWorld.vue | 583 +++++++++++++----- ui/src/components/ImageCarousel.vue | 51 ++ ui/src/main.js | 9 +- 24 files changed, 1256 insertions(+), 338 deletions(-) rename get_musicbrains_dump.sh => download_mb_dump.sh (82%) create mode 100644 extract_covers.py create mode 100644 generate_caa_tasks.py create mode 100644 generate_lastfm_tasks.py delete mode 100644 generate_scrape_tasks.py rename import.sh => make_neoj4_db.sh (91%) create mode 100644 make_release_to_rg_map.py delete mode 100644 patch_artists_with_lastfm.py create mode 100644 process_lastfm_data.py rename convert_mb.py => process_mb_dump.py (85%) create mode 100644 seed.cypher create mode 100755 seed_neo4j_db.sh create mode 100644 ui/src/components/ArtistInfo.vue create mode 100644 ui/src/components/ImageCarousel.vue diff --git a/README.md b/README.md index 3c160f2..22c6eaf 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,46 @@ wip + +### Data import from MusicBrainz & Last.fm + +```bash +# Download latest database dump +./get_musicbrainz_dump.sh + +# Convert to .csv +python convert_mb.py + +# Generate scraping tasks for task_tracker_drone (See notes) +python generate_scrape_tasks.py + +# Apply last.fm data to artist.csv +python patch_artists_with_lastfm.py "/path/to/lasfm_data.db" + +# Expose generated .csv data to the network +cd repo/ +python -m http.server 9999 + +# On the machine where neo4j is installed: +./import.sh +``` + +### task_tracker setup: + +Last.fm api calls are queued to [task_tracker](https://github.com/simon987/task_tracker/), + and results are gathered by a [task_tracker_drone](https://github.com/simon987/task_tracker_drone/) + ([script](https://git.simon987.net/drone/last.fm/src/master/run)). + + +Project secret: +```json +{ + "apikey": "", + "user": "" +} +``` + +### Api setup + +See [music-graph-api](https://github.com/simon987/music-graph-api) for setup instructions. + diff --git a/get_musicbrains_dump.sh b/download_mb_dump.sh similarity index 82% rename from get_musicbrains_dump.sh rename to download_mb_dump.sh index 2c11bd2..ff3bf47 100755 --- a/get_musicbrains_dump.sh +++ b/download_mb_dump.sh @@ -11,8 +11,9 @@ wget -nc "http://ftp.musicbrainz.org/pub/musicbrainz/data/fullexport/${latest}/m tar -xjvf mbdump.tar.bz2 mbdump/area mbdump/artist mbdump/l_area_area mbdump/l_artist_artist \ mbdump/l_artist_release mbdump/l_artist_release_group mbdump/l_label_label mbdump/l_release_group_release_group \ mbdump/label mbdump/label_type mbdump/link mbdump/link_type mbdump/release mbdump/release_group \ -mbdump/release_group_primary_type -tar -xjvf mbdump-derived.tar.bz2 mbdump/artist_tag mbdump/release_group_tag mbdump/tag mbdump/tag_relation +mbdump/release_group_primary_type mbdump/artist_credit_name mbdump/release_status +tar -xjvf mbdump-derived.tar.bz2 mbdump/artist_tag mbdump/release_group_tag mbdump/tag mbdump/tag_relation \ +mbdump/release_group_meta mv mbdump/* . rm -r mbdump diff --git a/extract_covers.py b/extract_covers.py new file mode 100644 index 0000000..e50ea6c --- /dev/null +++ b/extract_covers.py @@ -0,0 +1,27 @@ +import sqlite3 + +import sys + +with sqlite3.connect(sys.argv[1]) as conn: + + cursor = conn.cursor() + cursor.execute("SELECT id from covers") + + cursor = conn.cursor() + cursor.execute("SELECT id from covers") + + def rows(): + buf = list() + for row in cursor.fetchall(): + buf.append(row[0]) + if len(buf) > 30: + yield buf + buf.clear() + + for batch in rows(): + cursor.execute("SELECT cover from covers where id in (%s)" % (",".join(("'" + b + "'") for b in batch))) + covers = cursor.fetchall() + for i, cover in enumerate(covers): + with open("./tmpcovers/" + batch[i] + ".jpg", "wb") as out: + out.write(cover[0]) + print(batch[i]) diff --git a/generate_caa_tasks.py b/generate_caa_tasks.py new file mode 100644 index 0000000..cb20260 --- /dev/null +++ b/generate_caa_tasks.py @@ -0,0 +1,56 @@ +import json +from multiprocessing.pool import ThreadPool + +from task_tracker_drone.src.tt_drone.api import TaskTrackerApi, Worker + +TT_API_URL = "https://tt.simon987.net/api" +TT_PROJECT = 5 + + +done = set() +# with sqlite3.connect(sys.argv[1]) as conn: +# cur = conn.cursor() +# cur.execute("SELECT id FROM covers") +# for mbid in cur.fetchall(): +# done.add(mbid[0]) + +api = TaskTrackerApi(TT_API_URL) + +worker = Worker.from_file(api) +if not worker: + worker = api.make_worker("caa scraper") + worker.dump_to_file() +worker.request_access(TT_PROJECT, True, True) +input("Give permission to " + worker.alias) + + +def mktask(mbids): + res = worker.submit_task( + project=TT_PROJECT, + recipe=json.dumps(mbids), + hash64=hash(mbids[0]), + max_assign_time=60 * 30, + priority=1, + unique_str=None, + verification_count=None, + max_retries=5, + ) + print(res.text) + + +def lines(): + with open("in/release") as f: + buf = list() + + for line in f: + cols = line.split("\t") + + buf.append(cols[1]) + if len(buf) == 75: + a = list(buf) + buf.clear() + yield a + + +pool = ThreadPool(processes=20) +pool.map(func=mktask, iterable=lines()) diff --git a/generate_lastfm_tasks.py b/generate_lastfm_tasks.py new file mode 100644 index 0000000..1d8051c --- /dev/null +++ b/generate_lastfm_tasks.py @@ -0,0 +1,48 @@ +import csv +import json +from multiprocessing.pool import ThreadPool + +from task_tracker_drone.src.tt_drone.api import TaskTrackerApi, Worker + +TT_API_URL = "https://tt.simon987.net/api" +TT_PROJECT = 1 + +api = TaskTrackerApi(TT_API_URL) + +worker = Worker.from_file(api) +if not worker: + worker = api.make_worker("last.fm scraper") + worker.dump_to_file() +worker.request_access(TT_PROJECT, True, True) +input("Give permission to " + worker.alias) + +with open("repo/artist.csv") as f: + reader = csv.reader(f) + + def mktask(lines): + res = worker.submit_task( + project=TT_PROJECT, + recipe=json.dumps( + [{"mbid": line[0], "name": line[1]} for line in lines] + ), + unique_str=lines[0][0], + max_assign_time=60 * 5, + ) + print(res.text) + + def lines(): + line_batch = list() + + for line in reader: + if "Group" in line[3]: + line_batch.append(line) + if len(line_batch) >= 30: + res = list(line_batch) + line_batch.clear() + yield res + + tasks = list(lines()) + + pool = ThreadPool(processes=25) + pool.map(func=mktask, iterable=tasks) + diff --git a/generate_scrape_tasks.py b/generate_scrape_tasks.py deleted file mode 100644 index e310184..0000000 --- a/generate_scrape_tasks.py +++ /dev/null @@ -1,39 +0,0 @@ -import csv -import json -from multiprocessing.pool import ThreadPool - -from task_tracker_drone.src.tt_drone.api import TaskTrackerApi, Worker - -TT_API_URL = "https://tt.simon987.net/api" -TT_PROJECT = 1 - -api = TaskTrackerApi(TT_API_URL) - -worker = Worker.from_file(api) -if not worker: - worker = api.make_worker("last.fm scraper") - worker.dump_to_file() - worker.request_access(TT_PROJECT, True, True) - - input("Give permission to " + worker.alias) - -with open("repo/artist.csv") as f: - reader = csv.reader(f) - - def mktask(line): - res = worker.submit_task( - project=TT_PROJECT, - recipe=json.dumps({"mbid": line[0], "name": line[1]}), - unique_str=line[0], - max_assign_time=60 * 5, - ) - print(res.text) - - def lines(): - for line in reader: - if "Group" in line[2]: - yield line - - pool = ThreadPool(processes=60) - pool.map(func=mktask, iterable=lines()) - diff --git a/jenkins/build.sh b/jenkins/build.sh index 212c4ba..59e324b 100755 --- a/jenkins/build.sh +++ b/jenkins/build.sh @@ -1 +1,4 @@ -#!/usr/bin/env bash \ No newline at end of file +#!/usr/bin/env bash + +git submodule init +git submodule update --remote \ No newline at end of file diff --git a/last.fm b/last.fm index da08742..855df64 160000 --- a/last.fm +++ b/last.fm @@ -1 +1 @@ -Subproject commit da0874207e2e214ee72be818233e2bf2d30ded19 +Subproject commit 855df64c316930062ff4f7740492d0f039788498 diff --git a/import.sh b/make_neoj4_db.sh similarity index 91% rename from import.sh rename to make_neoj4_db.sh index 171b7ad..85930e7 100755 --- a/import.sh +++ b/make_neoj4_db.sh @@ -6,8 +6,12 @@ export DATABASE="graph.db" rm -rf "${NEO4J_HOME}/data/databases/${DATABASE}" +cp ${NEO4J_HOME}/conf/neo4j.conf ${NEO4J_HOME}/conf/neo4j.conf.bak +echo "dbms.security.auth_enabled=false" >> ${NEO4J_HOME}/conf/neo4j.conf + mkdir workspace 2> /dev/null cd workspace +rm *.csv wget ${REPOSITORY}/area.csv wget ${REPOSITORY}/area_area.csv diff --git a/make_release_to_rg_map.py b/make_release_to_rg_map.py new file mode 100644 index 0000000..ba21978 --- /dev/null +++ b/make_release_to_rg_map.py @@ -0,0 +1,31 @@ +import sqlite3 + +release_to_release_group_map = dict() +release_groups = dict() + +with open("in/release_group") as f: + for line in f: + cols = line.split("\t") + release_groups[cols[0]] = cols[1] + +with open("in/release") as f: + for line in f: + cols = line.split("\t") + release_to_release_group_map[cols[1]] = release_groups[cols[4]] + +with sqlite3.connect("mapdb.db") as conn: + + cursor = conn.cursor() + cursor.execute("CREATE TABLE map (release TEXT PRIMARY KEY , release_group TEXT)") + + for k, v in release_to_release_group_map.items(): + cursor.execute("INSERT INTO map (release, release_group) VALUES (?,?)", (k, v)) + conn.commit() + +""" +CREATE TABLE covers (id TEXT primary key, cover BLOB); +ATTACH 'mapdb.db' AS map; +ATTACH '/mnt/Data8/caa_tn_only.db' AS source; +INSERT OR IGNORE INTO covers SELECT release_group, cover FROM source.covers INNER JOIN map.map ON id = map.release; +""" + diff --git a/patch_artists_with_lastfm.py b/patch_artists_with_lastfm.py deleted file mode 100644 index 580a2c3..0000000 --- a/patch_artists_with_lastfm.py +++ /dev/null @@ -1,74 +0,0 @@ -import csv -import json -import sqlite3 -import sys - - -def patch(lastfm_data): - with sqlite3.connect(lastfm_data) as conn: - cur = conn.cursor() - - cur.execute("SELECT data FROM lastfmdata", ) - data = cur.fetchall() - - if data: - - buffer = [] - dup_buf = set() - artist_listeners = dict() - artists = set() - - for row in data: - lastfm_data = json.loads(row[0]) - - for similar in [s for s in lastfm_data["similar"] if s["mbid"] is not None]: - if (similar["mbid"], lastfm_data["artist"]) not in dup_buf: - buffer.append(( - similar["mbid"], - lastfm_data["artist"], - similar["match"] - )) - dup_buf.add((similar["mbid"], lastfm_data["artist"])) - dup_buf.add((lastfm_data["artist"], similar["mbid"])) - - artist_listeners[lastfm_data["artist"]] = (lastfm_data["listeners"], lastfm_data["playcount"]) - - del dup_buf - - with open("repo/lastfm_artist.csv", "w") as out: - - writer = csv.writer(out) - writer.writerow([ - "id:ID(Artist)", "name", ":LABEL", "listeners:int", "playcount:int" - ]) - - with open("repo/artist.csv") as f: - reader = csv.reader(f) - - reader.__next__() # Skip header - for row in reader: - writer.writerow([ - row[0], - row[1], - row[2], - row[3], - artist_listeners.get(row[0], (0, 0))[0], - artist_listeners.get(row[0], (0, 0))[1], - ]) - artists.add(row[0]) - - with open("repo/lastfm_artist_artist.csv", "w") as out: - out.write(",".join(( - ":START_ID(Artist)", ":END_ID(Artist)", "weight" - )) + "\n") - - for x in buffer: - if x[0] not in artists: - continue - if x[1] not in artists: - continue - - out.write(",".join(x) + "\n") - - -patch(sys.argv[1]) diff --git a/process_lastfm_data.py b/process_lastfm_data.py new file mode 100644 index 0000000..af6b1c6 --- /dev/null +++ b/process_lastfm_data.py @@ -0,0 +1,100 @@ +import csv +import json +import sqlite3 +from collections import defaultdict +import sys + +artists = set() + + +def disambiguate(lfm_artist, artist_release_count, name, mbid): + existing_mbid = lfm_artist.get(name, None) + + if existing_mbid and mbid != existing_mbid: + if artist_release_count[existing_mbid] < artist_release_count[mbid]: + + lfm_artist[name] = mbid + + print("Replacing %s (%s) with %s (%d) for %s" % + (existing_mbid, artist_release_count[existing_mbid], + mbid, artist_release_count[mbid], + name)) + else: + lfm_artist[name] = mbid + + +def patch(lastfm_data): + + artist_listeners = dict() + lastfm_artist_to_mbid = dict() + artist_release_count = defaultdict(int) + related = list() + + with open("repo/artist_release.csv") as f: + for line in f: + cols = line.split(',') + artist_release_count[cols[0]] += 1 + + with sqlite3.connect(lastfm_data) as conn: + cur = conn.cursor() + cur.execute("SELECT data FROM lastfmdata", ) + data = list(cur.fetchall()) + + # A lastfm artist name can refer to multiple MBIDs + # For RELATED_TO purposes, we assume that the MBID referring + # to the artist with the most official releases is the one + + for row in data: + meta = json.loads(row[0]) + + disambiguate(lastfm_artist_to_mbid, artist_release_count, meta["name"], meta["artist"]) + + for similar in [s for s in meta["similar"] if s["mbid"] is not None]: + disambiguate(lastfm_artist_to_mbid, artist_release_count, similar["name"], similar["mbid"]) + + # Get related links & listener counts + for row in data: + meta = json.loads(row[0]) + + artist_listeners[lastfm_artist_to_mbid[meta["name"]]] = \ + (meta["listeners"], meta["playcount"]) + + for similar in [s for s in meta["similar"] if s["mbid"] is not None]: + related.append(( + lastfm_artist_to_mbid[similar["name"]], + lastfm_artist_to_mbid[meta["name"]], + similar["match"] + )) + + with open("repo/lastfm_artist.csv", "w") as out: + writer = csv.writer(out) + writer.writerow([ + "id:ID(Artist)", "name", "year:short", ":LABEL", "listeners:int", "playcount:int" + ]) + + with open("repo/artist.csv") as f: + reader = csv.reader(f) + + reader.__next__() # Skip header + for row in reader: + writer.writerow([ + row[0], + row[1], + row[2], + row[3], + artist_listeners.get(row[0], (0, 0))[0], + artist_listeners.get(row[0], (0, 0))[1], + ]) + artists.add(row[0]) + + with open("repo/lastfm_artist_artist.csv", "w") as out: + out.write(",".join(( + ":START_ID(Artist)", ":END_ID(Artist)", "weight:float" + )) + "\n") + + for x in related: + if x[0] in artists and x[1] in artists: + out.write(",".join(x) + "\n") + + +patch(sys.argv[1]) diff --git a/convert_mb.py b/process_mb_dump.py similarity index 85% rename from convert_mb.py rename to process_mb_dump.py index a532039..b4d369f 100644 --- a/convert_mb.py +++ b/process_mb_dump.py @@ -1,4 +1,5 @@ import os +from collections import defaultdict links = dict() link_types = dict() @@ -8,6 +9,7 @@ label_types = { "\\N": "" } release_groups = dict() +release_statuses = dict() release_to_release_group_map = dict() release_types = { "\\N": "", @@ -107,6 +109,7 @@ artist_artist_rel_map = { "subgroup": "IS_SUBGROUP_OF", "founder": "IS_FOUNDER_OF", "involved with": "IS_INVOLVED_WITH", + "named after": "IS_NAMED_AFTER", } label_label_rel_map = { @@ -132,6 +135,11 @@ with open("in/link", "r") as f: cols = line.split("\t") links[cols[0]] = cols +with open("in/release_status", "r") as f: + for line in f: + cols = line.split("\t") + release_statuses[cols[0]] = cols + with open("in/link_type", "r") as f: for line in f: cols = line.split("\t") @@ -187,7 +195,7 @@ for _, artist in artists.items(): out_artist.write(",".join(( artist[1], '"' + artist[2].replace("\"", "\"\"") + '"', - artist[4] if artist[4] != "\\N" else "", + artist[4] if artist[4] != "\\N" else "0", "Artist" + (";Group\n" if artist[10] == "2" else "\n") ))) @@ -216,8 +224,14 @@ with open("in/release_group_primary_type") as f: cols = line.split("\t") release_types[cols[0]] = ";" + cols[1] +release_group_year = dict() +with open("in/release_group_meta") as f: + for line in f: + cols = line.split("\t") + release_group_year[cols[0]] = cols[2] if cols[2] != "\\N" else "0" + with open("repo/release.csv", "w") as out: - out.write("id:ID(Release),name,:LABEL\n") + out.write("id:ID(Release),name,year:int,:LABEL\n") with open("in/release_group") as f: for line in f: @@ -225,6 +239,7 @@ with open("repo/release.csv", "w") as out: out.write(",".join(( cols[1], '"' + cols[2].replace("\"", "\"\"") + '"', + release_group_year[cols[0]], "Release" + release_types[cols[4]], )) + "\n") @@ -233,19 +248,46 @@ with open("repo/release.csv", "w") as out: with open("in/release") as f: for line in f: cols = line.split("\t") - release_to_release_group_map[cols[0]] = cols[4] + if cols[5] != '\\N' and release_statuses[cols[5]][1] == "Official": + release_to_release_group_map[cols[0]] = cols[4] + +credit_names = defaultdict(list) + +with open("in/artist_credit_name") as f: + for line in f: + cols = line.split("\t") + credit_names[cols[0]].append(artists[cols[2]][1]) with open("tmp/tmp_artist_release.csv", "w") as out: out.write(":START_ID(Artist),:END_ID(Release),:TYPE\n") + # Is this part really necessary? with open("in/l_artist_release") as f: for line in f: cols = line.split("\t") - out.write(",".join(( - artists[cols[2]][1], - release_groups[release_to_release_group_map[cols[3]]][1], - artist_release_rel_map[link_types[links[cols[1]][1]][6]] - )) + "\n") + if cols[3] in release_to_release_group_map: + out.write(",".join(( + artists[cols[2]][1], + release_groups[release_to_release_group_map[cols[3]]][1], + artist_release_rel_map[link_types[links[cols[1]][1]][6]] + )) + "\n") + + # Artist credits + with open("in/release") as f: + for line in f: + cols = line.split("\t") + if cols[0] in release_to_release_group_map: + for credit in credit_names[cols[3]]: + out.write(",".join(( + credit, + release_groups[release_to_release_group_map[cols[0]]][1], + "CREDITED_FOR" + )) + "\n") + +# Remove dupes +os.system("(head -n 1 tmp/tmp_artist_release.csv && tail -n +2 tmp/tmp_artist_release.csv" + " | sort) | uniq > repo/artist_release.csv && rm tmp/tmp_artist_release.csv") + with open("repo/release_release.csv", "w") as out: out.write(":START_ID(Release),:END_ID(Release),:TYPE\n") @@ -259,12 +301,8 @@ with open("repo/release_release.csv", "w") as out: release_release_rel_map[link_types[links[cols[1]][1]][6]] )) + "\n") -os.system("(head -n 1 tmp/tmp_artist_release.csv && tail -n +2 tmp/tmp_artist_release.csv" - " | sort) | uniq > repo/artist_release.csv && rm tmp/tmp_artist_release.csv") - # --- - with open("in/tag") as f: with open("repo/tag.csv", "w") as out: out.write("id:ID(Tag),name\n") @@ -307,7 +345,7 @@ with open("repo/artist_tag.csv", "w") as out: )) + "\n") with open("repo/tag_tag.csv", "w") as out: - out.write(":START_ID(Tag),:END_ID(Tag),weight\n") + out.write(":START_ID(Tag),:END_ID(Tag),weight:int\n") with open("in/tag_relation") as f: for line in f: diff --git a/seed.cypher b/seed.cypher new file mode 100644 index 0000000..7a2fc34 --- /dev/null +++ b/seed.cypher @@ -0,0 +1,2 @@ +CREATE INDEX ON :Artist(id); +CREATE INDEX ON :Release(id); diff --git a/seed_neo4j_db.sh b/seed_neo4j_db.sh new file mode 100755 index 0000000..846b8ef --- /dev/null +++ b/seed_neo4j_db.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +export NEO4J_HOME="/home/drone/Downloads/neo4j-community-3.5.3" + +cat seed.cypher | ${NEO4J_HOME}/bin/cypher-shell diff --git a/task_tracker_drone b/task_tracker_drone index 69af3ba..e025596 160000 --- a/task_tracker_drone +++ b/task_tracker_drone @@ -1 +1 @@ -Subproject commit 69af3ba908c3c3e1c838da3e95ff15f967086af0 +Subproject commit e025596cf2ccfbe803f05cb848927ae36fe270a3 diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index c856330..d94a692 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -25,10 +25,10 @@ module.exports = { 'generator-star-spacing': 'off', // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - - "indent": "off", - "indent-legacy": ["error", 4], + "indent": ["error", 4, {SwitchCase: 1}], "no-param-reassign": 0, "no-underscore-dangle": 0, + "no-trailing-spaces": 0, + "space-before-function-paren": 0, } }; diff --git a/ui/package-lock.json b/ui/package-lock.json index 2dd726a..32b0508 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -60,6 +60,35 @@ "@babel/types": "7.0.0-beta.44" } }, + "@babel/helper-module-imports": { + "version": "7.0.0-beta.35", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz", + "integrity": "sha512-vaC1KyIZSuyWb3Lj277fX0pxivyHwuDU4xZsofqgYAbkDxNieMg2vuhzP5AgMweMY7fCQUMTi+BgPqTLjkxXFg==", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.35", + "lodash": "^4.2.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.0.0-beta.35", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.35.tgz", + "integrity": "sha512-y9XT11CozHDgjWcTdxmhSj13rJVXpa5ZXwjjOiTedjaM0ba5ItqdS02t31EhPl7HtOWxsZkYCCUNrSfrOisA6w==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.2.0", + "to-fast-properties": "^2.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@babel/helper-split-export-declaration": { "version": "7.0.0-beta.44", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", @@ -166,6 +195,11 @@ } } }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -439,6 +473,14 @@ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "dev": true }, + "async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "requires": { + "babel-runtime": "6.x" + } + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -731,8 +773,7 @@ "babel-helper-vue-jsx-merge-props": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", - "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==", - "dev": true + "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==" }, "babel-helpers": { "version": "6.24.1", @@ -773,6 +814,15 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-component": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-component/-/babel-plugin-component-1.1.1.tgz", + "integrity": "sha512-WUw887kJf2GH80Ng/ZMctKZ511iamHNqPhd9uKo14yzisvV7Wt1EckIrb8oq/uCz3B3PpAW7Xfl7AkTLDYT6ag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "7.0.0-beta.35" + } + }, "babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", @@ -1260,7 +1310,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -1690,6 +1739,60 @@ "unset-value": "^1.0.0" } }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" + }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "requires": { + "is-plain-obj": "^1.0.0" + } + } + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -1964,6 +2067,14 @@ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2220,14 +2331,12 @@ "core-js": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", - "dev": true + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { "version": "4.0.0", @@ -2954,8 +3063,15 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } }, "deep-equal": { "version": "1.0.1", @@ -2969,6 +3085,11 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -3215,6 +3336,11 @@ "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -3245,6 +3371,19 @@ "integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w==", "dev": true }, + "element-ui": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.7.2.tgz", + "integrity": "sha512-Exh9QTkm9gwMMPzg1TyaTlBKyr3k4K9XcC5vl0A/mneDvJX//RsURGuOWsCNDVQMdhh5h9e+W5icosh+pKfbCg==", + "requires": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + } + }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", @@ -4311,7 +4450,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" @@ -4910,8 +5048,7 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { "version": "2.0.6", @@ -4974,6 +5111,30 @@ "slash": "^1.0.0" } }, + "got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -5026,12 +5187,25 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -5223,6 +5397,11 @@ } } }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -5393,8 +5572,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "inquirer": { "version": "3.3.0", @@ -5450,6 +5628,15 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -5604,6 +5791,11 @@ "kind-of": "^3.0.2" } }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -5631,8 +5823,7 @@ "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" }, "is-plain-object": { "version": "2.0.4", @@ -5664,6 +5855,11 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -5709,8 +5905,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -5724,6 +5919,15 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, "js-base64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", @@ -5752,6 +5956,11 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", @@ -5788,6 +5997,14 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "requires": { + "json-buffer": "3.0.0" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -6023,6 +6240,11 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -6288,6 +6510,11 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -6566,6 +6793,11 @@ "sort-keys": "^1.0.0" } }, + "normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=" + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -6599,8 +6831,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -6782,11 +7013,20 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" }, "p-limit": { "version": "1.3.0", @@ -6812,6 +7052,14 @@ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -6952,8 +7200,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, "pinkie": { "version": "2.0.4", @@ -9113,8 +9360,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "progress": { "version": "2.0.3", @@ -9338,7 +9584,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -9424,8 +9669,7 @@ "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { "version": "0.10.1", @@ -9568,6 +9812,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "resolve": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", @@ -9606,6 +9855,14 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -9691,8 +9948,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -10313,8 +10569,7 @@ "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, "string-width": { "version": "2.1.1", @@ -10347,7 +10602,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -10458,6 +10712,11 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -10486,6 +10745,11 @@ "integrity": "sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==", "dev": true }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", @@ -10885,6 +11149,26 @@ "requires-port": "^1.0.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + }, + "dependencies": { + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + } + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -10903,8 +11187,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utila": { "version": "0.4.0", @@ -11059,6 +11342,14 @@ } } }, + "vue-resource": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/vue-resource/-/vue-resource-1.5.1.tgz", + "integrity": "sha512-o6V4wNgeqP+9v9b2bPXrr20CGNQPEXjpbUWdZWq9GJhqVeAGcYoeTtn/D4q059ZiyN0DIrDv/ADrQUmlUQcsmg==", + "requires": { + "got": "^8.0.3" + } + }, "vue-router": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz", diff --git a/ui/package.json b/ui/package.json index 5445ae7..034e0f2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,7 +13,10 @@ "dependencies": { "d3": "^5.9.2", "d3-force": "^2.0.1", + "d3-path": "^1.0.7", + "element-ui": "^2.7.2", "vue": "^2.6.10", + "vue-resource": "^1.5.1", "vue-router": "^3.0.2" }, "devDependencies": { @@ -22,6 +25,7 @@ "babel-eslint": "^8.2.1", "babel-helper-vue-jsx-merge-props": "^2.0.3", "babel-loader": "^7.1.1", + "babel-plugin-component": "^1.1.1", "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-transform-runtime": "^6.22.0", "babel-plugin-transform-vue-jsx": "^3.5.0", diff --git a/ui/src/App.vue b/ui/src/App.vue index ae39de1..b5dce4b 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -5,18 +5,17 @@ diff --git a/ui/src/components/ArtistInfo.vue b/ui/src/components/ArtistInfo.vue new file mode 100644 index 0000000..58e1c3f --- /dev/null +++ b/ui/src/components/ArtistInfo.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/ui/src/components/HelloWorld.vue b/ui/src/components/HelloWorld.vue index 36a8e07..a279834 100644 --- a/ui/src/components/HelloWorld.vue +++ b/ui/src/components/HelloWorld.vue @@ -1,12 +1,19 @@ - - diff --git a/ui/src/components/ImageCarousel.vue b/ui/src/components/ImageCarousel.vue new file mode 100644 index 0000000..131d8aa --- /dev/null +++ b/ui/src/components/ImageCarousel.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/ui/src/main.js b/ui/src/main.js index 9658b83..50fb7c5 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -2,7 +2,12 @@ import Vue from 'vue' import App from './App' import router from './router' -Vue.config.productionTip = false +import ElementUI from 'element-ui' +import VueResource from 'vue-resource' +import 'element-ui/lib/theme-chalk/index.css' + +Vue.use(ElementUI) +Vue.use(VueResource) /* eslint-disable no-new */ new Vue({ @@ -10,3 +15,5 @@ new Vue({ router, render: h => h(App) }) + +Vue.config.productionTip = false