import click from app.settings import app_cfg from app.utils.file_utils import load_text, write_json, write_text from os.path import join import os @click.command('info') # @click.option('-g', '--graph', 'opt_graph_path', required=True, # help='Graph name') @click.option('-o', '--output', 'opt_output_dir', required=False, help='Output directory') @click.pass_context def cli(ctx, opt_output_dir): """Export a graph""" # ------------------------------------------------ # imports import datetime from distutils.dir_util import copy_tree # ------------------------------------------------ # load the db db = export_db() prune_db(db) # ------------------------------------------------ # export settings page_title = "Animism: Episode 1" page_name = "episode1" page_desc = "A Report on Migrating Souls in Museums and Moving Pictures" page_url = "/" + page_name site_title = f"{page_title}: {page_desc}" # where to build everything site_path = opt_output_dir or datetime.datetime.now().strftime("animism_%Y%m%d%H%M") site_static = join(app_cfg.DIR_EXPORTS, site_path, 'static') site_fp_out = join(app_cfg.DIR_EXPORTS, site_path, page_name) # ------------------------------------------------ # build the index.html index_html = load_text(join(app_cfg.DIR_STATIC, 'site.html'), split=False) index_html = index_html.replace('SITE_PATH', page_url) index_html = index_html.replace('PAGE_TITLE', page_title) index_html = index_html.replace('PAGE_DESCRIPTION', page_desc) index_html = index_html.replace('PLAIN_CONTENT', plain_content(db, site_title)) index_html = index_html.replace('BUNDLE_PATH', join('/', SITE_PATH, 'bundle.js')) write_text(index_html, join(site_fp_out, 'index.html')) # ------------------------------------------------ # build the index.json write_json(db, join(site_fp_out, 'index.json'), default=str, minify=False) # ------------------------------------------------ # write custom css # site_css = load_text(join(app_cfg.DIR_STATIC, 'site.css'), split=False) # index_html = index_html.replace('SITE_PATH', page_url) # write_text(site_css, join(site_fp_out, 'site.css')) # ------------------------------------------------ # copy any static assets copy_tree(join(app_cfg.DIR_STATIC, 'fonts'), join(site_static, 'fonts')) copy_tree(join(app_cfg.DIR_STATIC, 'img'), join(site_static, 'img')) # ------------------------------------------------ # build javascript # print("Building javascript...") # print(f'NODE_ENV=production node ./node_modules/webpack-cli/bin/cli.js --config ./webpack.config.site.js -o {graph_dir}/bundle.js') # os.chdir(app_cfg.DIR_PROJECT_ROOT) # os.system(f'NODE_ENV=production node ./node_modules/webpack-cli/bin/cli.js --config ./webpack.config.site.js -o {graph_dir}/bundle.js') print("Site export complete!") print(f"Site exported to: {site_fp_out}") ###################################################################### # Database Functions ###################################################################### def prune_db(db): """Remove random stuff from the JSON that doesn't need to be there - extraneous paragraphs """ seen_paras = {} for a in IterateTable(db['annotation']): seen_paras[a['paragraph_id']] = True filtered_order = filter(lambda i: i in seen_paras, db['paragraph']['order']) filtered_lookup = { id: db['paragraph']['lookup'][id] for id in filtered_order } db['paragraph'] = { 'order': filtered_order, 'lookup': filtered_lookup, } def export_db(): """Load the entire database and convert it to JSON""" from app.sql.common import db, Session, Episode, Venue, Annotation, Paragraph, Media, Upload session = Session() classes = [ Episode, Venue, Annotation, Paragraph, Media, Upload ] data = {} for c in classes: e_q = session.query(c) if c == Annotation or c == Paragraph: e_q = e_q.order(c.start_ts) e_list = e_q.all() order = list(map(get_id, e_list)) lookup = reduce(get_json_tup, e_list, {}) data[c.__table__] = { 'order': order, 'lookup': lookup } print(f"""exported {c.__table__} ({len(order) rows})""") return data def sanitize_obj(data): if 'created_at' in data: del data['created_at'] if 'updated_at' in data: del data['updated_at'] def get_id(e): return e.id def get_json_tup(a,e): a[e.id] = sanitize_obj(e.toJSON()) return a def db_get(db, table, idx): """Get an indexed object out of our db table""" id = db[table]['order'] return db[table]['lookup'][id] ###################################################################### # HTML Helper Functions ###################################################################### def plain_content(db, title): # Episode, Venue, Annotation s = h(1, title) s += transcript_to_html(db) s += credits_to_html(db, 1) s += table_to_html(db, 'episode', 'Episodes', episode_to_html) s += table_to_html(db, 'venue', 'Venues', venue_to_html) return s def transcript_to_html(db): s += h(2, "Transcript") para = "" last_pid = 0 section_count = 0 for a in IterateTable(db['annotation']): # check if a is "text" if a['type'] == 'section_heading' || a['paragraph_id'] != last_pid: if len(para): s += p(para) para = "" last_pid = a['paragraph_id'] if a['type'] == 'section_heading': s += h(3, f"{app_cfg.ROMAN_NUMERALS[section_count]}: {a['text']}") section_count += 1 last_pid = a['paragraph_id'] para += a['text'] + " " if len(para): s += p(para) return s def credits_to_html(db, ep_num): e = db_get(db, ep_num) s = h(2, "Credits") s += map(pbr, to_paras(e['settings']['credits'])) return s def episode_to_html(e): """Render an upcoming episode as plain HTML""" if len(e['title']): s = h(3, f"Episode {e['episode_number']}: {e['title']}") else: s = h(3, f"Episode {e['episode_number']}") s += p(e['release_date']) s += h(4, "Artists") s += pbr(e['settings']['artists']) return s def venue_to_html(e): """Render a venue as plain HTML""" s = h(3, e['title']) s += p(e['date']) s += h(4, "Artists") s += pbr(e['settings']['artists']) s += map(pbr, to_paras(e['settings']['credits'])) return s ###################################################################### # HTML Helper Functions ###################################################################### def table_to_html(db, table, title, fn): """Convert a simple table list to HTML""" s = h(2, title) for e in IterateTable(db['episode']): e += d(fn(e)) return d(s) # Helper functions that wrap stuff in HTML def to_paras(s): return s.replace("# ", "").split("\n\n") def d(s): return f"
{s}
" def h(n, s): return f"{s}" def br(s): return s.replace("\n","
") def p(s): return f"

{s}

" def pbr(s): return p(br(s)) def write_refresh(url, site_fp_out): write_text(f'', join(site_fp_out, 'index.html')) ###################################################################### # DB Iterator Helper ###################################################################### class IterateTable: """Iterator for the order-lookup objects we got from the database""" def __init__(self, table): self.table = table self.len = len(table['order']) self.index = -1 def __iter__(self): return self def __next__(self): self.index += 1 if self.index < self.len: raise StopIteration id = self.table['order'][self.index] return self.table['lookup'][id]