diff --git a/src/article.py b/src/article.py
index be39ab9..9a0b09f 100644
--- a/src/article.py
+++ b/src/article.py
@@ -153,7 +153,7 @@ class LexiconArticle:
article_by_title[target].citedby.add(article.title)
return list(article_by_title.values())
- def build_default_content(self):
+ def build_default_contentblock(self):
"""
Formats citations into the article content as normal HTML links
and returns the result.
@@ -164,23 +164,29 @@ class LexiconArticle:
"" if cite_tuple[1] in self.wcites else " class=\"phantom\"")
for format_id, cite_tuple in self.citations.items()
}
- return self.content.format(**format_map)
+ article_content = self.content.format(**format_map)
+ return "
\n
{}
\n{}
\n".format(
+ self.title,
+ article_content)
def build_default_citeblock(self, prev_article, next_article):
"""
Builds the citeblock content HTML for use in regular article pages.
For each defined target, links the target page as Previous or Next.
"""
- citeblock = "
\n"
- # Prev/next links
- if next_article is not None:
- citeblock += "
\n".format(
- next_article.title_filesafe, " class=\"phantom\"" if next_article.player is None else "")
- if prev_article is not None:
- citeblock += "
\n".format(
- prev_article.title_filesafe, " class=\"phantom\"" if prev_article.player is None else "")
- if next_article is None and prev_article is None:
- citeblock += "
\n"
+ citeblock = "
\n"
+ # Prev/next links:
+ if next_article is not None or prev_article is not None:
+ prev_link = ("← Previous".format(
+ prev_article.title_filesafe,
+ " class=\"phantom\"" if prev_article.player is None else "")
+ if prev_article is not None else "")
+ next_link = ("Next →".format(
+ next_article.title_filesafe,
+ " class=\"phantom\"" if next_article.player is None else "")
+ if next_article is not None else "")
+ citeblock += "
\n
{}
\n
{}
\n
\n".format(
+ prev_link, next_link)
# Citations
cites_links = [
"{0}".format(
@@ -189,7 +195,7 @@ class LexiconArticle:
for title in sorted(
self.wcites | self.pcites,
key=lambda t: utils.titlesort(t))]
- cites_str = " | ".join(cites_links)
+ cites_str = " / ".join(cites_links)
if len(cites_str) < 1: cites_str = "—"
citeblock += "
Citations: {}
\n".format(cites_str)
# Citedby
@@ -199,7 +205,7 @@ class LexiconArticle:
for title in sorted(
self.citedby,
key=lambda t: utils.titlesort(t))]
- citedby_str = " | ".join(citedby_links)
+ citedby_str = " / ".join(citedby_links)
if len(citedby_str) < 1: citedby_str = "—"
citeblock += "
Cited by: {}
\n
\n".format(citedby_str)
return citeblock
diff --git a/src/build.py b/src/build.py
index 456f4bf..bdab5ca 100644
--- a/src/build.py
+++ b/src/build.py
@@ -8,17 +8,37 @@ from collections import defaultdict # For rank inversion in statistics
from src import utils
from src.article import LexiconArticle
-def build_contents_page(articles, config):
+class LexiconPage:
+ """
+ An abstraction layer around formatting a Lexicon page skeleton with kwargs
+ so that kwargs that are constant across pages aren't repeated.
+ """
+
+ def __init__(self, skeleton=None, page=None):
+ self.kwargs = {}
+ self.skeleton = skeleton
+ if page is not None:
+ self.skeleton = page.skeleton
+ self.kwargs = dict(page.kwargs)
+
+ def add_kwargs(self, **kwargs):
+ self.kwargs.update(kwargs)
+
+ def format(self, **kwargs):
+ total_kwargs = {**self.kwargs, **kwargs}
+ return self.skeleton.format(**total_kwargs)
+
+def build_contents_page(page, articles, index_list):
"""
Builds the full HTML of the contents page.
"""
- content = ""
+ content = "
"
# Head the contents page with counts of written and phantom articles
phantom_count = len([article for article in articles if article.player is None])
if phantom_count == 0:
- content = "
There are {0} entries in this lexicon.
\n".format(len(articles))
+ content += "
There are {0} entries in this lexicon.
\n".format(len(articles))
else:
- content = "
There are {0} entries, {1} written and {2} phantom.
\n".format(
+ content += "
There are {0} entries, {1} written and {2} phantom.
\n"
# Top number of citations made
citations_made = { title : len(cites) for title, cites in cite_map.items() }
top_citations = reverse_statistics_dict(citations_made)[:3]
top_citations_items = itemize(top_citations)
- content += "
\n"
# Top number of times cited
# Build a map of what cites each article
@@ -201,10 +182,10 @@ def build_statistics_page(articles, config):
citations_to = { title : len(cites) for title, cites in cited_by_map.items() }
top_cited = reverse_statistics_dict(citations_to)[:3]
top_cited_items = itemize(top_cited)
- content += "
\n"
- content += "
Most citations made to: \n"
+ content += "
\n"
+ content += "Most citations made to: \n"
content += " \n".join(top_cited_items)
- content += "\n
\n"
# Player pageranks
players = sorted(set([article.player for article in articles if article.player is not None]))
@@ -247,10 +228,10 @@ def build_statistics_page(articles, config):
in articles_by_player.items()}
player_rank = reverse_statistics_dict(pagerank_by_player)
player_rank_items = itemize(player_rank)
- content += "
\n"
# Player citations made
player_cite_count = {
@@ -258,10 +239,10 @@ def build_statistics_page(articles, config):
for player, articles in articles_by_player.items()}
player_cites_made_ranks = reverse_statistics_dict(player_cite_count)
player_cites_made_items = itemize(player_cites_made_ranks)
- content += "
\n"
- content += "
Citations made by player \n"
+ content += "
\n"
+ content += "Citations made by player \n"
content += " \n".join(player_cites_made_items)
- content += "\n
\n"
+ content += "
\n"
# Player cited count
cited_times = {player : 0 for player in players}
@@ -270,23 +251,13 @@ def build_statistics_page(articles, config):
cited_times[article.player] += len(article.citedby)
cited_times_ranked = reverse_statistics_dict(cited_times)
cited_times_items = itemize(cited_times_ranked)
- content += "
\n"
- content += "
Citations made to player \n"
+ content += "
\n"
+ content += "Citations made to player \n"
content += " \n".join(cited_times_items)
- content += "\n
\n"
+ content += "
\n"
# Fill in the entry skeleton
- entry_skeleton = utils.load_resource("entry-page.html")
- css = utils.load_resource("lexicon.css")
- return entry_skeleton.format(
- title="Statistics",
- lexicon=config["LEXICON_TITLE"],
- css=css,
- logo=config["LOGO_FILENAME"],
- prompt=config["PROMPT"],
- sort=config["DEFAULT_SORT"],
- content=content,
- citeblock="")
+ return page.format(title="Statistics", content=content)
def build_graphviz_file(cite_map):
"""
@@ -367,26 +338,29 @@ def build_all(path_prefix, lexicon_name):
lex_path = os.path.join(path_prefix, lexicon_name)
# Load the Lexicon's peripherals
config = utils.load_config(lexicon_name)
- entry_skeleton = utils.load_resource("entry-page.html")
- css = utils.load_resource("lexicon.css")
+ page_skeleton = utils.load_resource("page-skeleton.html")
+ page = LexiconPage(skeleton=page_skeleton)
+ page.add_kwargs(
+ lexicon=config["LEXICON_TITLE"],
+ logo=config["LOGO_FILENAME"],
+ prompt=config["PROMPT"],
+ sort=config["DEFAULT_SORT"])
# Parse the written articles
articles = LexiconArticle.parse_from_directory(os.path.join(lex_path, "src"))
- # At this point, the articles haven't been cross-populated,
- # so we can derive the written titles from this list
- #written_titles = [article.title for article in articles]
# Once they've been populated, the articles list has the titles of all articles
# Sort this by turn before title so prev/next links run in turn order
articles = sorted(
LexiconArticle.populate(articles),
key=lambda a: (a.turn, utils.titlesort(a.title)))
- #phantom_titles = [article.title for article in articles if article.title not in written_titles]
+
def pathto(*els):
return os.path.join(lex_path, *els)
# Write the redirect page
print("Writing redirect page...")
with open(pathto("index.html"), "w", encoding="utf8") as f:
- f.write(utils.load_resource("redirect.html").format(lexicon=config["LEXICON_TITLE"], sort=config["DEFAULT_SORT"]))
+ f.write(utils.load_resource("redirect.html").format(
+ lexicon=config["LEXICON_TITLE"], sort=config["DEFAULT_SORT"]))
# Write the article pages
print("Deleting old article pages...")
@@ -398,38 +372,32 @@ def build_all(path_prefix, lexicon_name):
for idx in range(l):
article = articles[idx]
with open(pathto("article", article.title_filesafe + ".html"), "w", encoding="utf-8") as f:
- content = article.build_default_content()
+ contentblock = article.build_default_contentblock()
citeblock = article.build_default_citeblock(
None if idx == 0 else articles[idx - 1],
None if idx == l-1 else articles[idx + 1])
- article_html = entry_skeleton.format(
+ article_html = page.format(
title = article.title,
- lexicon = config["LEXICON_TITLE"],
- css = css,
- logo = config["LOGO_FILENAME"],
- prompt = config["PROMPT"],
- sort = config["DEFAULT_SORT"],
- content = content,
- citeblock = citeblock)
+ content = contentblock + citeblock)
f.write(article_html)
print(" Wrote " + article.title)
# Write default pages
print("Writing default pages...")
with open(pathto("contents", "index.html"), "w", encoding="utf-8") as f:
- f.write(build_contents_page(articles, config))
+ f.write(build_contents_page(page, articles, config["INDEX_LIST"]))
print(" Wrote Contents")
with open(pathto("rules", "index.html"), "w", encoding="utf-8") as f:
- f.write(build_rules_page(config))
+ f.write(build_rules_page(page))
print(" Wrote Rules")
with open(pathto("formatting", "index.html"), "w", encoding="utf-8") as f:
- f.write(build_formatting_page(config))
+ f.write(build_formatting_page(page))
print(" Wrote Formatting")
with open(pathto("session", "index.html"), "w", encoding="utf-8") as f:
- f.write(build_session_page(config))
+ f.write(build_session_page(page, config["SESSION_PAGE"]))
print(" Wrote Session")
with open(pathto("statistics", "index.html"), "w", encoding="utf-8") as f:
- f.write(build_statistics_page(articles, config))
+ f.write(build_statistics_page(page, articles))
print(" Wrote Statistics")
# Write auxiliary pages
diff --git a/src/resources/formatting.html b/src/resources/formatting.html
index b2b3341..0976810 100644
--- a/src/resources/formatting.html
+++ b/src/resources/formatting.html
@@ -1,3 +1,4 @@
+
Lexipython provides support for a limited amount of Markdown-esque formatting.
# Player: PN
@@ -7,17 +8,50 @@
This is an example page.
Some words are //italicized//,
and some words are **bolded**.
-All of these sentences are part of the same paragraph.
+All of these sentences are part of the same
+paragraph.
This is a new paragraph.\\
-Unlike the last paragraph, this line will be after a line break within the paragraph.
+Unlike the last paragraph, this line will be after a
+line break within the paragraph.
-This is an [[example citation|Phantom page]]. You can also cite a [[phantom page]] with just the title.
+This is an [[example citation|Phantom page]]. You can
+also cite a [[phantom page]] with just the title.
~Dr. X. Amplepage
-
Each turn, fill out the header with your player information, the current turn, and the title of your entry. The Player field can be anything as long as it's the same for all articles you write (even when they're by different characters). Using your initials is recommended.
-
Two line breaks begins a new paragraph. A single line break does nothing, unless the line is ended by a double backslash (\\).
-
Text bounded by ** will be bolded: **bold** produces bold. Text bounded by // will be italicized: //italics// produces italics.
-
To cite another Lexicon entry, use double brackets. Text in double brackets will cite and link to the entry of the same name: [[Example page]] produces Example page. Text in double brackets split with a | will alias the link as the left text and link to the entry with the name of the right text: [[this text|Example page]] produces this text. You must be precise in the entry title you cite to. Citations to "Example" vs. "The Example" will point to different entries and create different phantoms, and your GM will probably have to clean up after you.
-
Beginning a paragraph with ~ will right-align it and place a horizontal line above it. Use this for signing your entry with your scholar's name.
+
Each turn, fill out the header with your player information, the current
+ turn, and the title of your entry. The Player field can be anything
+ as long as it's the same for all articles you write (even when they're by
+ different characters). Using your initials is recommended.
+
Two line breaks begins a new paragraph. A single line break does nothing,
+ unless the line is ended by a double backslash (\\).
+
Text bounded by ** will be bolded: **bold** produces bold. Text
+ bounded by // will be italicized: //italics// produces italics.
+
To cite another Lexicon entry, use double brackets. Text in double brackets
+ will cite and link to the entry of the same name: [[Example page]] produces
+ Example page. Text in
+ double brackets split with a | will alias the link as the left text and
+ link to the entry with the name of the right text: [[this text|Example page]]
+ produces this text. You
+ must be precise in the entry title you cite to. Citations to
+ "Example" vs. "The Example" will point to different entries and create
+ different phantoms, and your GM will probably have to clean up after
+ you.
+
Beginning a paragraph with ~ will right-align it and place a horizontal line
+ above it. Use this for signing your entry with your scholar's name.
+
+
+
Example page
+
This is an example page.
+Some words are italicized,
+and some words are bolded.
+All of these sentences are part of the same
+paragraph.
+
This is a new paragraph.
+Unlike the last paragraph, this line will be after a
+line break within the paragraph.
At the beginning of the game, you will be provided with a topic statement that sets the tone for the game. Use it for inspiration and a stepping-stone into shaping the world of the Lexicon.
-
Each round, you will be assigned an index, a grouping of letters. Your entry must alphabetize under that index.
-
Each index has a number of open slots equal to the number of players, which are taken up by article titles when an article is written in that index or a citation is made to an unwritten article, or phantom. If there are no open slots in your index, you must write the article for a phantom in that index.
-
"The" and "A" aren't counted in indexing.
-
Once you've picked an article title, write your article on that subject.
-
There are no hard and fast rules about style. Try to sound like an encyclopedia entry or the overview section at the top of a wiki article.
-
You must respect and not contradict any factual content of any posted articles. You may introduce new facts that place things in a new light, provide alternative interpretations, or flesh out unexplained details in unexpected ways; but you must not contradict what has been previously established as fact.
-
Aim for around 200-300 words. Going over is okay, but be reasonable.
-
Your article must cite other articles in the Lexicon. Sometimes these citations will be to phantoms, articles that have not been written yet.
-
On the first turn, your article must cite exactly two phantom articles.
-
On subsequent turns, your article must cite exactly two phantom articles, either already-cited phantoms or new ones. Your article must also cite at least one written article.
-
On the penultimate turn, you must cite exactly one phantom article and at least two written articles.
-
On the final turn, you must cite at least three written articles.
-
You may not cite an entry you wrote. You may cite phantoms you have cited before.
-
Once you cite a phantom, you cannot choose to write it if you write an article for that index later.
-
-
Ersatz Scrivener. In the course of the game, it may come to pass that a scholar is assigned an index in which no slots are available to them, because they have cited all the available phantoms in their previous articles. When this happens, the player instead writes their article as Ersatz Scrivener, radical skeptic. Ersatz does not believe in the existence of whatever he is writing about, no matter how obvious it seems to others or how central it is in the developing history of the world. All references, testimony, etc. with regard to its existence are tragic delusion at best or malicious lies at worst. Unlike the other scholars, Ersatz does not treat the research of his peers as fact, because he does not believe he has peers. Players writing articles as Ersatz are encouraged to name and shame the work of the misguided amateurs collaborating with him.
+
Each Lexicon has a topic statement that sets the tone for the game.
+ It provides a starting point for shaping the developing world of the
+ Lexicon. As it is a starting point, don't feel contrained to write only
+ about the topics mentioned directly in it.
+
Articles are sorted under an index, a grouping of letters. An article is
+ in an index if its first letter is in that group of letters. "The", "A",
+ and "An" aren't counted in indexing. Example: One of the indices is JKL.
+ An article titled 'The Jabberwock' would index under JKL, not T's index.
+
Until the game is over, some of the articles will have been cited, but not
+ yet written. These are called phantom articles. A phantom article has a
+ title, which is defined by the first citation to it, but no content.
+
Generally, an index has a number of "slots" equal to the number of players.
+ When an article is first written or cited, it takes up one slot in its
+ corresponding index.
+
Each turn, you will be assigned to write in an index.
+
Your articles should be written from the perspective of your character.
+ Your character should be a scholar collaborating with the other
+ scholars on the production of the Lexicon. You should play the same
+ character for the duration of the game.
+
If the index has open slots, you may come up with a new article title
+ and write an article under that title. If all unwritten slots in your
+ index are filled by phantom articles, you must choose one of them and
+ write it.
+
There are no hard and fast rules about style, but it is recommended
+ that players imitate an encyclopedic style to stay true to the game's
+ conceit.
+
There are no hard and fast rules about length, but it is recommended
+ that the Editor enforce a maximum word limit. In general, aiming for
+ 200-300 words is ideal.
+
You must respect and not contradict the factual content of all written
+ articles. You may introduce new facts that put things in a new light,
+ provide alternative interpretations, or flesh out unexplained details
+ in unexpected ways; but you must not contradict what has been
+ previously established as fact. Use the "yes, and" rule from improv
+ acting: accept what your fellow scholars have written and add to it in
+ new ways, rather than trying to undo their work. This rule includes
+ facts that have been established in written articles about the topics
+ of phantom articles.
+
Each article will cite other articles in the Lexicon.
+
You may not cite an entry that you have written. When you write an
+ article, you may not cite it in later articles.
+
As a corollary, you may not write phantom articles that you have cited.
+ If you cite an article and then write it later, your former article
+ now cites you, which is forbidden per the above.
+
On the first turn, there are no written articles. Your first article
+ must cite exactly two phantom articles.
+
On subsequent turns, your article must cite exactly two phantoms,
+ but you can cite phantoms that already exist. Your article must also
+ cite at least one written article. You can cite more than one.
+
On the penultimate turn, you must cite exactly one phantom
+ article and at least two written articles.
+
On the final turn, you must cite at least three written articles.
+
As the game goes on, it may come to pass that a player must write an
+ article in an index, but that index is full, and that player has already
+ cited all the phantoms in it. When this happens, the player instead writes
+ their article as **Ersatz Scrivener**, radical skeptic. Ersatz does not
+ believe in the existence of whatever he is writing about, no matter how
+ obvious it seems to others or how central it is in the developing history
+ of the world. For Ersatz, all references, testimony, etc. with regard to
+ its existence are tragic delusion at best or malicious lies at worst.
+ Unlike the other scholars, Ersatz does not treat the research of his peers
+ as fact, because he does not believe he has peers. Players writing articles
+ as Ersatz are encouraged to lambast the amateur work of his misguided
+ "collaborators".