WizardTree Tutorial

Basics

To start off you'll need to import the WizrdTree.py module.

import WizardTree as wt

This contains the methods needed to crawl CYOA style comment chains on reddit. If a story starts as a comment you can crawl it using CrawlComment, by providing either the url or the comment id. For example,

story = wt.CrawlComment(url = "https://www.reddit.com/r/talesfromtechsupport/comments/3xyesc/project_management_seems_a_lot_like_cooking_a/cy8z5uv")

or, you could achieve the same result using:

story = wt.CrawlComment(comment_id = "cy8z5uv")

If a story starts of as a submission (which is usually the case on /r/YouEnterADungeon and /r/playzorkwithme) you should use CrawlSubmission(url, submission_id) instead, providing either the url or the submission id.

Importing / Exporting

Once you've crawled the story, you can export it as JSON using the Export method and import it again using the Import method. See below for examples.

Reading the Story

As of now there are two ways to read a story, one is by using the 'HTML conversion' example listed below. The other is by using the HTML viewer (available online here).

Credits

Thanks to /u/XiuathoTheWizard and everyone else who contributed to his original story, for inspiring this project and bringing the great folks at /r/TalesFromTechSupport a tale that won't soon be forgotten.

For more info see the Github repository

Code by /u/XkF21WNJ.

Examples

Crawl and Save XiuathoTheWizard's first tale as JSON

In [ ]:
import WizardTree as wt

story = wt.CrawlComment(comment_id = "cy8z5uv")

wt.Export(story, "Output/WizardTree.json")
Load story from JSON file

In [13]:
import WizardTree as wt

story = wt.Import("Output/WizardTree.json")

For the following examples, the variable story is assumed to contain the story

Print some basic information about the story

In [14]:
print "Title: " + story['title']
print "Author: " + story['author']
print "Url: " + story['url']

print "Contains %d events, with %d possible endings" % (
    len(story['events']), 
    len([1 for event in story['events'].values() if len(event['actions']) == 0]))


Title: Project Management seems a lot like cooking a banquet.
Author: XiuathoTheWizard
Url: https://www.reddit.com/r/talesfromtechsupport/comments/3xyesc/project_management_seems_a_lot_like_cooking_a/cy8z5uv
Contains 173 events, with 29 possible endings
Visualize story structure

In [16]:
%matplotlib inline

#Imports
import WizardTree as wt
import networkx as nx
import matplotlib.pyplot as plt

#Build graph
G = nx.DiGraph()
for event_id in story['events']:
    event = story['events'][event_id]
    for action_id in event['actions']:
        action = story['actions'][action_id]
        G.add_edge(event_id, action['consequence'])

#Plot
shells = {}
pos = {}
for node in nx.dfs_preorder_nodes(G):
    depth = len(nx.ancestors(G, node))
    if depth in shells:
        shells[depth].append(node)
    else:
        shells[depth] = [node]
    pos[node] = [len(shells[depth]), -depth]

plt.figure(figsize=(8,20))
nx.draw(G, pos=pos, with_labels=False)
plt.show()


HTML conversion

In [17]:
#Imports
from html import HTML

#Definitions
def Truncate(text):
    return text[:60] + (text[60:] and '..')

def ParseEvent(story, body, event_id, index):
    event = story['events'][event_id]
    div = body.div(klass = 'event')
    div.a('', name = event_id)
    
    links = div.div(klass = 'link-container')
    if event['parent'] != None:     
        links.a('Go to Previous', 
                href = '#'+event['parent'],
                klass = 'left-link')
    else:
        links.a('Go to Index', href = '#index', klass = 'left-link')
    links.a('Go to Original', href = event['url'], klass = 'right-link')
    
    div.text(event['description'], escape=False)
    
    for action_id in event['actions']:
        ParseAction(story, body, action_id, div.ul(), index)

def ParseAction(story, body, action_id, ul, index):
    action = story['actions'][action_id]
    href = '#'+action['consequence']
    
    indli = index.li()
    indli.a(action['description'], href = href, klass='action', style = 'display: block;', escape=False)
    
    li = ul.li(klass = 'action')
    a = li.a(href = href,
             style = "display: block;", 
             title = '/u/'+ action['actor'])
    
    a.span(action['description'], escape=False)
    ParseEvent(story, body, action['consequence'], index)

def ParseStory(story):    
    #Styling
    css = """
        body {
            font-family: arial, sans-serif;
            width: 40em;
            margin-left:auto;
            margin-right:auto;
        }
        
        p {
            text-align: justiy;
            -moz-hyphens: auto;
            hyphens: auto;
        }
        
        .event {
            margin-bottom: 1000ex;
        }
        
        .event > p {
            font-family: Georgia, serif;
            font-size: larger;
            line-height: 1.5em;
        }
        
        .action p {
            margin-top: 0.25em;
            margin-bottom: 0.25em;
        }
        
        .action {
            font-family: consolas, monospace;
            margin: 0 0 2ex 0;
        }
        
        .link-container {
            font-family: consolas, monospace;
            display: flex;
        }
        
        .left-link{
            width: 40ex;
        }
        
        .right-link{
            flex-grow: 1;
            text-align: right;
        }
    """

    #Build HTML file
    doc = HTML()
    doc.text("<!DOCTYPE html>", escape=False)

    html = doc.html()
    header = html.head()
    header.meta(charset = "UTF-8")
    header.title(story['title'])
    header.style(css, escape = False)

    body = html.body()
    index = HTML().ul()

    #Parse tree
    ParseEvent(story, body, story['start'], index)

    #Add index
    body.a('', name = 'index')
    body.h1('Index')
    body.div(index, escape = False)

    #Add credits
    body.a('', name = 'credits')
    body.h1('Credits')
    body.p("A special thanks to " + story['author'] + " for creating the story")
    body.p("Also thanks to all the commenters appearing in the story:")
    users_ul = body.ul()
    for actor in story['actors']: users_ul.li("/u/"+actor)

    body.p("Powered by python, coded by /u/XkF21WNJ.")
    
    return doc
    
doc = ParseStory(story)

#Export to file
with open("Output/WizardTree.html", 'w') as f:
    f.write(unicode(doc).encode('utf-8'))

In [ ]: