Interactive Program Schedule

Started by Jimbly, May 23, 2013, 10:02:16 AM

0 Members and 1 Guest are viewing this topic.

Jimbly

Interactive Program Schedule

URL: http://fanime.info/
Status: Complete!

So, in some previous years Fanime has had an online program schedule, and I've generally found it really handy to tweak it to a color-coded schedule of what I may want to see that I can pull up on my phone (e.g. http://thesilentb.com/fanime/index.2011.html from 2011).  So, last night I spent a couple hours and put together most of the tech for one, which I plan on completely finishing tonight.  Anyway, I thought others might also like such a thing and benefit from it, so I'm sharing it here, and also hoping others may help fill in the missing data.  Right now it's set to a wide-screen, open edit mode where anyone can help fill in the schedule (note: this mode only works well in Chrome, and kind of in IE, not at all in FireFox), I'll switch it to a more compact "planner" mode this evening so we can start marking what things we're interested in (and get an output like the example from 2011 which you should be able to pull up on your smart phone during the convention).

Current plan/schedule for development of this evening:
Definitely going to have:

  • Done! - Video programming data entered
  • Done! - Planner mode (viewable on IE/FF/Chrome and mobile browsers; enter a name to view plan, or name+password to edit plan, where editing is simply clicking any time slot to toggle it between normal (white), want to see (green), maybe see (yellow))
Stretch goals:

  • Done! Non-video programming data entered
  • Done! Ability to toggle which rows show up on your planner
  • Time/zoom controls (so whenever you look at a plan page it just shows -1 hours to +4 hours around the current time, not the entire thing)
  • Info/notes pages (allow people to leave a (public or private) note about any event, fill in event info, and, by default, this page will just be populated by a Google search for the event term (most useful for video programmming))

Anyway, if you're interested in using this, let me know on the forums here, or get in contact with me.  If you're a staffer and have access to a spreadsheet with all of this data already and could send it my way, that would also be awesome =).

  Jimbly
  Usual cosplay: Purple kitty, Rurouni Kenshin
  Google Chat: [email protected]
  AIM: Wasteland777
  Y!M: Wasteland7
  ICQ: 435337

See you at the con!

Jimbly

Update:  Video programming data is now complete, planner mode now works, feel free to jump over to http://fanime.info put in a username and password (if an account does not exist, it simply auto-creates one), and start planning your Fanime!

The planner mode seems to work fine on IE, FireFox, Chrome, and the Android mobile browser, if anyone could give it a whirl on an iOS browser, it would be nice to know if it works there as well.

HarpB


Jimbly

Ooh, excellent, should be pretty trivial to scrape that data and put into interactive form.  Any reason that schedule is not linked from the Fanime page, or announced anywhere?

HarpB

I have no idea.  I was reading their pocket book and saw it by surprise.
I did scrapper that I used last year. It still worked, so I use it to make a schedule for Guidebook. Here is the CSV: http://wikisend.com/download/299700/FAnime_2013.csv

Scrapper:

from pyquery.pyquery import PyQuery
from datetime import datetime, timedelta, time
import re

def parse_time(hour_time):
    try:
        return datetime.strptime(hour_time, '%I:%M%p').time()
    except ValueError:
        return datetime.strptime(hour_time, '%I%p').time()

def parse_fanime_sessions(unix_time):
    url = 'http://m.fanime.com/index.php?grid=%d' % unix_time
    dom = PyQuery(url)
    event_datetime = datetime.fromtimestamp(unix_time)
    date = event_datetime.date()
    time_start = event_datetime.time()
    time_end = (event_datetime + timedelta(hours = 1)).time()

    row_pattern = r'table.grid tr'
    col_pattern = r'td'
    rows = dom(row_pattern)
    schedule = []
    MIDNIGHT_TIME = time(0, 0, 0)
    for row in rows:
        py_row = PyQuery(row)
        cols = py_row(col_pattern)
        for index in range(len(cols)):
            td = PyQuery(cols[index])
            css_class = td.attr.class_
#             clean_text = td.text().replace(' ', ' ')
            clean_room_and_track = td.text().replace(u'\xa0', ' ')
            room_and_track_re = re.search('([^\(]+)\s\(([^\)]+)', clean_room_and_track)
            if css_class == 'track':
                schedule += [{
                    'date': date,
                    'title': None,
                    'room': room_and_track_re.group(2),
                    'track': room_and_track_re.group(1),
                    'time_start': time_start,
                    'time_end': time_end,
                }]
            elif css_class in ['skip', 'partial']:
                #===============================================================================
                # Case
                # continues from 11:30am, begins at 12:30pm  => start_time: 11:30am
                # ends at 12:30pm, runs until 2:30pm        => end_time: 12:30pm
                # [all day]    =>    start_time = end_time = 0:00am
                #===============================================================================
                event = schedule[-1]
                if clean_room_and_track == '[all day]':
                    event['time_start'] = MIDNIGHT_TIME
                    event['time_end'] = MIDNIGHT_TIME
                elif 'continues from' in clean_room_and_track or 'begins at' in clean_room_and_track:
                    text_parts = clean_room_and_track.split()
                    event['time_start'] = parse_time(text_parts[-1])
                elif 'ends at' in clean_room_and_track or 'runs until' in clean_room_and_track:
                    text_parts = clean_room_and_track.split()
                    event['time_end'] = parse_time(text_parts[-1])
                else:
                    raise Exception("UNHANDLED CASE")
            else:
                schedule[-1]['title'] = clean_room_and_track

    return schedule

def get_schedule(start_time, end_time):
    current_unix_time = start_time
    schedule = []
    while current_unix_time < end_time:
        sessions = parse_fanime_sessions(current_unix_time)
#         for item in schedule:
#             pprint(item, indent=4)
        schedule += sessions
        current_unix_time += (60 * 60)
    return schedule

def print_guidebook_csv(schedule, filename = None):
    if filename:
        file_handle = open(filename, 'w')
    else:
        file_handle = None

    CSV_HEADER = 'Session Title,Date,Time Start,Time End,Room/Location,Schedule Track (Optional),Description (Optional)'
    if file_handle:
        file_handle.write("{}\n".format(CSV_HEADER))
    else:
        print CSV_HEADER
    for session in schedule:
#         print session
        session['title'] = session['title'].replace('"', '')
        session['track'] = session['track'].replace('Tabletop Gaming Tournaments', 'Tabletop Gaming')
        csv_line = '"%(title)s", %(date)s, %(time_start)s, %(time_end)s, %(room)s, %(track)s, ""' % session
        if file_handle:
            file_handle.write("{}\n".format(csv_line))
        else:
            print csv_line

    if file_handle:
        file_handle.close()

if __name__ == '__main__':
    #===========================================================================
    # Fanime 2013 (1369350000 to 1369695600) ( Thursday 5pm - Monday 4pm)
    # 1369688400 - Monday, 2pm
    # 1369692000 - Monday, 3pm
    #===========================================================================
    # Thursdaay 6pm - Midnight
    ONE_HOUR = 60 * 60
    SIX_HOUR = ONE_HOUR * 6
    schedule = []

    # THURSDAY
    THURSDAY_6PM = 1369357200
    THURSDAY_11PM = THURSDAY_6PM + SIX_HOUR - ONE_HOUR
    print '%d - %d ' % (THURSDAY_6PM, THURSDAY_11PM)
    schedule += get_schedule(THURSDAY_6PM, THURSDAY_11PM)

    # FRIDAY
    FRIDAY_MIDNIGHT = THURSDAY_11PM + ONE_HOUR
    FRIDAY_11PM = FRIDAY_MIDNIGHT + (SIX_HOUR * 4) - ONE_HOUR
    print '%d - %d ' % (FRIDAY_MIDNIGHT, FRIDAY_11PM)
    schedule += get_schedule(FRIDAY_MIDNIGHT, FRIDAY_11PM)

    # SATURDAY
    SATURDAY_MIDNIGHT = FRIDAY_11PM + ONE_HOUR
    SATURDAY_11PM = SATURDAY_MIDNIGHT + (SIX_HOUR * 4) - ONE_HOUR
    print '%d - %d ' % (SATURDAY_MIDNIGHT, SATURDAY_11PM)
    schedule += get_schedule(SATURDAY_MIDNIGHT, SATURDAY_11PM)

    # SUNDAY
    SUNDAY_MIDNIGHT = SATURDAY_11PM + ONE_HOUR
    SUNDAY_11PM = SUNDAY_MIDNIGHT + (SIX_HOUR * 4) - ONE_HOUR
    print '%d - %d ' % (SUNDAY_MIDNIGHT, SUNDAY_11PM)
    schedule += get_schedule(SUNDAY_MIDNIGHT, SUNDAY_11PM)

    # MONDAY
    MONDAY_MIDNIGHT = SUNDAY_11PM + ONE_HOUR
    MONDAY_3PM = 1369692000
    print '%d - %d ' % (MONDAY_MIDNIGHT, MONDAY_3PM)
    schedule += get_schedule(MONDAY_MIDNIGHT, MONDAY_3PM)

    filename = 'Fanime_2013.csv'
    print_guidebook_csv(schedule, filename)

HarpB

In case you use Guidebook on your smartphone, here are guides you can download using the Redeem Code.
Fanime 2013: yjg6wo2k
Clockwork Alchemy 2013: 29tsb4ap

Jimbly

Heh, I ended up scraping the data myself before seeing your post, but it's now scraped and all imported into the interactive planner!

Jimbly

This has now been updated to work better both without an account (plan is just saved locally) and when network connections are poor (loads much faster now).