2017-08-27 05:14:16 +00:00
|
|
|
import sys # For argv and stderr
|
|
|
|
import os # For reading directories
|
|
|
|
import re # For parsing lex content
|
2017-11-06 07:53:25 +00:00
|
|
|
import io # For writing pages out as UTF-8
|
2017-08-27 05:14:16 +00:00
|
|
|
import networkx # For pagerank analytics
|
|
|
|
from collections import defaultdict # For rank inversion in statistics
|
|
|
|
|
2018-07-01 20:12:57 +00:00
|
|
|
import src.utils as utils
|
2017-08-27 05:14:16 +00:00
|
|
|
|
2017-12-11 05:21:46 +00:00
|
|
|
def build_contents_page(articles, config):
|
2017-08-27 05:14:16 +00:00
|
|
|
"""
|
2017-12-11 05:21:46 +00:00
|
|
|
Builds the full HTML of the contents page.
|
2017-08-27 05:14:16 +00:00
|
|
|
"""
|
|
|
|
content = ""
|
2017-12-11 05:21:46 +00:00
|
|
|
# Article counts
|
|
|
|
phantom_count = len([article for article in articles if article.author is None])
|
|
|
|
if phantom_count == 0:
|
2017-12-11 06:34:41 +00:00
|
|
|
content = "<p>There are <b>{0}</b> entries in this lexicon.</p>\n".format(len(articles))
|
2017-08-27 05:14:16 +00:00
|
|
|
else:
|
2017-12-11 05:21:46 +00:00
|
|
|
content = "<p>There are <b>{0}</b> entries, <b>{1}</b> written and <b>{2}</b> phantom.</p>\n".format(
|
|
|
|
len(articles), len(articles) - phantom_count, phantom_count)
|
|
|
|
# Prepare article links
|
|
|
|
link_by_title = {article.title : "<a href=\"../article/{1}.html\"{2}>{0}</a>".format(
|
|
|
|
article.title, article.title_filesafe,
|
|
|
|
"" if article.author is not None else " class=\"phantom\"")
|
|
|
|
for article in articles}
|
|
|
|
# Write the articles in alphabetical order
|
2018-07-01 20:12:57 +00:00
|
|
|
content += utils.load_resource("contents.html")
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "<div id=\"index-order\" style=\"display:block\">\n<ul>\n"
|
2017-08-27 05:14:16 +00:00
|
|
|
indices = config["INDEX_LIST"].split("\n")
|
2018-07-01 20:12:57 +00:00
|
|
|
alphabetical_order = sorted(articles, key=lambda a: utils.titlecase(a.title))
|
2017-12-11 05:21:46 +00:00
|
|
|
check_off = list(alphabetical_order)
|
2017-08-27 05:14:16 +00:00
|
|
|
for index_str in indices:
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "<h3>{0}</h3>\n".format(index_str)
|
|
|
|
for article in alphabetical_order:
|
2018-07-01 20:12:57 +00:00
|
|
|
if (utils.titlestrip(article.title)[0].upper() in index_str):
|
2017-12-11 05:21:46 +00:00
|
|
|
check_off.remove(article)
|
|
|
|
content += "<li>"
|
|
|
|
content += link_by_title[article.title]
|
|
|
|
content += "</li>\n"
|
|
|
|
if len(check_off) > 0:
|
|
|
|
content += "<h3>&c.</h3>\n".format(index_str)
|
|
|
|
for article in check_off:
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "<li>"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += link_by_title[article.title]
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "</li>\n"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "</ul>\n</div>\n"
|
|
|
|
# Write the articles in turn order
|
|
|
|
content += "<div id=\"turn-order\" style=\"display:none\">\n<ul>\n"
|
|
|
|
latest_turn = max([article.turn for article in articles if article.author is not None])
|
2018-07-01 20:12:57 +00:00
|
|
|
turn_order = sorted(articles, key=lambda a: (a.turn, utils.titlecase(a.title)))
|
2017-12-11 05:21:46 +00:00
|
|
|
check_off = list(turn_order)
|
|
|
|
for turn_num in range(1, latest_turn + 1):
|
|
|
|
content += "<h3>Turn {0}</h3>\n".format(turn_num)
|
|
|
|
for article in turn_order:
|
|
|
|
if article.turn == turn_num:
|
|
|
|
check_off.remove(article)
|
|
|
|
content += "<li>"
|
|
|
|
content += link_by_title[article.title]
|
|
|
|
content += "</li>\n"
|
|
|
|
if len(check_off) > 0:
|
|
|
|
content += "<h3>Unwritten</h3>\n"
|
|
|
|
for article in check_off:
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "<li>"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += link_by_title[article.title]
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "</li>\n"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "</ul>\n</div>\n"
|
|
|
|
# Fill in the page skeleton
|
2018-07-01 20:12:57 +00:00
|
|
|
entry_skeleton = utils.load_resource("entry-page.html")
|
|
|
|
css = utils.load_resource("lexicon.css")
|
2017-08-27 05:14:16 +00:00
|
|
|
return entry_skeleton.format(
|
|
|
|
title="Index of " + config["LEXICON_TITLE"],
|
|
|
|
lexicon=config["LEXICON_TITLE"],
|
|
|
|
css=css,
|
|
|
|
logo=config["LOGO_FILENAME"],
|
2017-12-11 05:21:46 +00:00
|
|
|
prompt=config["PROMPT"],
|
2017-08-27 05:14:16 +00:00
|
|
|
content=content,
|
|
|
|
citeblock="")
|
|
|
|
|
|
|
|
def build_rules_page(config):
|
|
|
|
"""
|
|
|
|
Builds the full HTML of the rules page.
|
|
|
|
"""
|
2018-07-01 20:12:57 +00:00
|
|
|
content = utils.load_resource("rules.html")
|
2017-08-27 05:14:16 +00:00
|
|
|
# Fill in the entry skeleton
|
2018-07-01 20:12:57 +00:00
|
|
|
entry_skeleton = utils.load_resource("entry-page.html")
|
|
|
|
css = utils.load_resource("lexicon.css")
|
2017-08-27 05:14:16 +00:00
|
|
|
return entry_skeleton.format(
|
|
|
|
title="Rules",
|
|
|
|
lexicon=config["LEXICON_TITLE"],
|
|
|
|
css=css,
|
|
|
|
logo=config["LOGO_FILENAME"],
|
2017-12-11 05:21:46 +00:00
|
|
|
prompt=config["PROMPT"],
|
2017-08-27 05:14:16 +00:00
|
|
|
content=content,
|
|
|
|
citeblock="")
|
|
|
|
|
|
|
|
def build_formatting_page(config):
|
|
|
|
"""
|
|
|
|
Builds the full HTML of the formatting page.
|
|
|
|
"""
|
2018-07-01 20:12:57 +00:00
|
|
|
content = utils.load_resource("formatting.html")
|
2017-08-27 05:14:16 +00:00
|
|
|
# Fill in the entry skeleton
|
2018-07-01 20:12:57 +00:00
|
|
|
entry_skeleton = utils.load_resource("entry-page.html")
|
|
|
|
css = utils.load_resource("lexicon.css")
|
2017-08-27 05:14:16 +00:00
|
|
|
return entry_skeleton.format(
|
|
|
|
title="Formatting",
|
|
|
|
lexicon=config["LEXICON_TITLE"],
|
|
|
|
css=css,
|
|
|
|
logo=config["LOGO_FILENAME"],
|
2017-12-11 05:21:46 +00:00
|
|
|
prompt=config["PROMPT"],
|
2017-08-27 05:14:16 +00:00
|
|
|
content=content,
|
|
|
|
citeblock="")
|
|
|
|
|
|
|
|
def build_session_page(config):
|
|
|
|
"""
|
|
|
|
Builds the full HTML of the session page.
|
|
|
|
"""
|
|
|
|
# Fill in the entry skeleton
|
2018-07-01 20:12:57 +00:00
|
|
|
entry_skeleton = utils.load_resource("entry-page.html")
|
|
|
|
css = utils.load_resource("lexicon.css")
|
2017-08-27 05:14:16 +00:00
|
|
|
return entry_skeleton.format(
|
2017-12-11 05:21:46 +00:00
|
|
|
title=config["LEXICON_TITLE"],
|
2017-08-27 05:14:16 +00:00
|
|
|
lexicon=config["LEXICON_TITLE"],
|
|
|
|
css=css,
|
|
|
|
logo=config["LOGO_FILENAME"],
|
2017-12-11 05:21:46 +00:00
|
|
|
prompt=config["PROMPT"],
|
|
|
|
content=config["SESSION_PAGE"],
|
2017-08-27 05:14:16 +00:00
|
|
|
citeblock="")
|
|
|
|
|
2017-12-11 05:21:46 +00:00
|
|
|
def build_statistics_page(articles, config):
|
2017-08-27 05:14:16 +00:00
|
|
|
"""
|
|
|
|
Builds the full HTML of the statistics page.
|
|
|
|
"""
|
|
|
|
content = ""
|
2017-12-11 05:21:46 +00:00
|
|
|
cite_map = {article.title : [cite_tuple[1] for cite_tuple in article.citations.values()] for article in articles}
|
|
|
|
# Pages by pagerank
|
|
|
|
content += "<div class=\"moveable\">\n"
|
|
|
|
content += "<p><u>Top 10 pages by page rank:</u><br>\n"
|
2017-08-27 05:14:16 +00:00
|
|
|
G = networkx.Graph()
|
|
|
|
for citer, citeds in cite_map.items():
|
|
|
|
for cited in citeds:
|
|
|
|
G.add_edge(citer, cited)
|
|
|
|
ranks = networkx.pagerank(G)
|
|
|
|
sranks = sorted(ranks.items(), key=lambda x: x[1], reverse=True)
|
|
|
|
ranking = list(enumerate(map(lambda x: x[0], sranks)))
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "<br>\n".join(map(lambda x: "{0} – {1}".format(x[0]+1, x[1]), ranking[:10]))
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "</p>\n"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "</div>\n"
|
|
|
|
# Top numebr of citations made
|
|
|
|
content += "<div class=\"moveable\">\n"
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "<p><u>Most citations made from:</u><br>\n"
|
|
|
|
citation_tally = [(kv[0], len(kv[1])) for kv in cite_map.items()]
|
|
|
|
citation_count = defaultdict(list)
|
|
|
|
for title, count in citation_tally: citation_count[count].append(title)
|
|
|
|
content += "<br>\n".join(map(
|
2017-12-11 05:21:46 +00:00
|
|
|
lambda kv: "{0} – {1}".format(kv[0], "; ".join(kv[1])),
|
2017-08-27 05:14:16 +00:00
|
|
|
sorted(citation_count.items(), reverse=True)[:3]))
|
|
|
|
content += "</p>\n"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "</div>\n"
|
|
|
|
# Top number of times cited
|
|
|
|
content += "<div class=\"moveable\">\n"
|
2017-08-27 05:14:16 +00:00
|
|
|
content += "<p><u>Most citations made to:</u><br>\n"
|
|
|
|
all_cited = set([title for cites in cite_map.values() for title in cites])
|
|
|
|
cited_by_map = { cited: [citer for citer in cite_map.keys() if cited in cite_map[citer]] for cited in all_cited }
|
|
|
|
cited_tally = [(kv[0], len(kv[1])) for kv in cited_by_map.items()]
|
|
|
|
cited_count = defaultdict(list)
|
|
|
|
for title, count in cited_tally: cited_count[count].append(title)
|
|
|
|
content += "<br>\n".join(map(
|
2017-12-11 05:21:46 +00:00
|
|
|
lambda kv: "{0} – {1}".format(kv[0], "; ".join(kv[1])),
|
2017-08-27 05:14:16 +00:00
|
|
|
sorted(cited_count.items(), reverse=True)[:3]))
|
|
|
|
content += "</p>\n"
|
2017-12-11 05:21:46 +00:00
|
|
|
content += "</div>\n"
|
|
|
|
# Author pageranks
|
|
|
|
content += "<div class=\"moveable\">\n"
|
|
|
|
content += "<p><u>Author total page rank:</u><br>\n"
|
|
|
|
authors = sorted(set([article.author for article in articles if article.author is not None]))
|
|
|
|
articles_by = {author : [a for a in articles if a.author == author] for author in authors}
|
|
|
|
author_rank = {author : sum(map(lambda a: ranks[a.title], articles)) for author, articles in articles_by.items()}
|
|
|
|
content += "<br>\n".join(map(
|
|
|
|
lambda kv: "{0} – {1}".format(kv[0], round(kv[1], 3)),
|
|
|
|
sorted(author_rank.items(), key=lambda t:-t[1])))
|
|
|
|
content += "</p>\n"
|
|
|
|
content += "</div>\n"
|
|
|
|
# Author citations made
|
|
|
|
content += "<div class=\"moveable\">\n"
|
|
|
|
content += "<p><u>Citations made by author</u><br>\n"
|
|
|
|
author_cite_count = {author : sum(map(lambda a:len(a.wcites | a.pcites), articles)) for author, articles in articles_by.items()}
|
|
|
|
content += "<br>\n".join(map(
|
|
|
|
lambda kv: "{0} – {1}".format(kv[0], kv[1]),
|
|
|
|
sorted(author_cite_count.items(), key=lambda t:-t[1])))
|
|
|
|
content += "</p>\n"
|
|
|
|
content += "</div>\n"
|
|
|
|
# Author cited count
|
|
|
|
content += "<div class=\"moveable\">\n"
|
|
|
|
content += "<p><u>Citations made to author</u><br>\n"
|
|
|
|
cited_times = {author : 0 for author in authors}
|
|
|
|
for article in articles:
|
|
|
|
if article.author is not None:
|
|
|
|
cited_times[article.author] += len(article.citedby)
|
|
|
|
content += "<br>\n".join(map(
|
|
|
|
lambda kv: "{0} – {1}".format(kv[0], kv[1]),
|
|
|
|
sorted(cited_times.items(), key=lambda t:-t[1])))
|
|
|
|
content += "</p>\n"
|
|
|
|
content += "</div>\n"
|
|
|
|
|
2017-08-27 05:14:16 +00:00
|
|
|
# Fill in the entry skeleton
|
2018-07-01 20:12:57 +00:00
|
|
|
entry_skeleton = utils.load_resource("entry-page.html")
|
|
|
|
css = utils.load_resource("lexicon.css")
|
2017-08-27 05:14:16 +00:00
|
|
|
return entry_skeleton.format(
|
|
|
|
title="Statistics",
|
|
|
|
lexicon=config["LEXICON_TITLE"],
|
|
|
|
css=css,
|
|
|
|
logo=config["LOGO_FILENAME"],
|
2017-12-11 05:21:46 +00:00
|
|
|
prompt=config["PROMPT"],
|
2017-08-27 05:14:16 +00:00
|
|
|
content=content,
|
|
|
|
citeblock="")
|
|
|
|
|
2017-12-11 05:21:46 +00:00
|
|
|
def build_graphviz_file(cite_map):
|
|
|
|
"""
|
|
|
|
Builds a citation graph in dot format for Graphviz.
|
|
|
|
"""
|
|
|
|
result = []
|
|
|
|
result.append("digraph G {\n")
|
|
|
|
# Node labeling
|
|
|
|
written_entries = list(cite_map.keys())
|
|
|
|
phantom_entries = set([title for cites in cite_map.values() for title in cites if title not in written_entries])
|
|
|
|
node_labels = [title[:20] for title in written_entries + list(phantom_entries)]
|
|
|
|
node_names = [hash(i) for i in node_labels]
|
|
|
|
for i in range(len(node_labels)):
|
|
|
|
result.append("{} [label=\"{}\"];\n".format(node_names[i], node_labels[i]))
|
|
|
|
# Edges
|
|
|
|
for citer in written_entries:
|
|
|
|
for cited in cite_map[citer]:
|
|
|
|
result.append("{}->{};\n".format(hash(citer[:20]), hash(cited[:20])))
|
|
|
|
# Return result
|
|
|
|
result.append("overlap=false;\n}\n")
|
|
|
|
return "".join(result)#"…"
|
|
|
|
|
2017-08-27 05:14:16 +00:00
|
|
|
# Summative functions
|
|
|
|
|
|
|
|
|
2017-12-11 05:21:46 +00:00
|
|
|
# Write auxiliary files
|
|
|
|
# TODO: write graphviz file
|
|
|
|
# TODO: write compiled lexicon page
|