update from Atlas

This commit is contained in:
Luciano Ramalho
2015-01-05 03:20:08 -02:00
parent 1edb0cd05b
commit 0618105a47
18 changed files with 822 additions and 123 deletions

View File

@@ -1,30 +1,30 @@
"""
explore1.py: Script to explore the OSCON schedule feed
# BEGIN EXPLORE1_DEMO
>>> from osconfeed import load
>>> raw_feed = load()
>>> feed = FrozenJSON(raw_feed)
>>> sorted(feed.Schedule.keys())
>>> feed = FrozenJSON(raw_feed) # <1>
>>> len(feed.Schedule.speakers) # <2>
357
>>> sorted(feed.Schedule.keys()) # <3>
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed.Schedule.items()):
... print('{:3} {}'.format(len(value), key))
...
1 conferences
484 events
357 speakers
53 venues
>>> feed.Schedule.speakers[-1].name
>>> feed.Schedule.speakers[-1].name # <4>
'Carina C. Zona'
>>> carina = feed.Schedule.speakers[-1]
>>> carina.twitter
'cczona'
>>> feed.Schedule.events[40].name
>>> talk = feed.Schedule.events[40] # <5>
>>> talk.name
'There *Will* Be Bugs'
>>> feed.Schedule.events[40].speakers
>>> talk.speakers # <6>
[3471, 5199]
>>> talk.flavor # <7>
Traceback (most recent call last):
...
KeyError: 'flavor'
# END EXPLORE1_DEMO
"""
# BEGIN EXPLORE1
from collections import abc
@@ -34,19 +34,20 @@ class FrozenJSON:
"""
def __init__(self, mapping):
self._data = dict(mapping)
self._data = dict(mapping) # <1>
def __getattr__(self, name):
def __getattr__(self, name): # <2>
if hasattr(self._data, name):
return getattr(self._data, name)
return getattr(self._data, name) # <3>
else:
return FrozenJSON.build(self._data[name])
return FrozenJSON.build(self._data[name]) # <4>
@classmethod
def build(cls, obj):
if isinstance(obj, abc.Mapping):
def build(cls, obj): # <5>
if isinstance(obj, abc.Mapping): # <6>
return cls(obj)
elif isinstance(obj, abc.MutableSequence):
elif isinstance(obj, abc.MutableSequence): # <7>
return [cls.build(item) for item in obj]
else:
else: # <8>
return obj
# END EXPLORE1

View File

@@ -4,27 +4,25 @@ explore2.py: Script to explore the OSCON schedule feed
>>> from osconfeed import load
>>> raw_feed = load()
>>> feed = FrozenJSON(raw_feed)
>>> len(feed.Schedule.speakers)
357
>>> sorted(feed.Schedule.keys())
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed.Schedule.items()):
... print('{:3} {}'.format(len(value), key))
...
1 conferences
484 events
357 speakers
53 venues
>>> feed.Schedule.speakers[-1].name
'Carina C. Zona'
>>> carina = feed.Schedule.speakers[-1]
>>> carina.twitter
'cczona'
>>> feed.Schedule.events[40].name
>>> talk = feed.Schedule.events[40]
>>> talk.name
'There *Will* Be Bugs'
>>> feed.Schedule.events[40].speakers
>>> talk.speakers
[3471, 5199]
>>> talk.flavor
Traceback (most recent call last):
...
KeyError: 'flavor'
"""
# BEGIN EXPLORE2
from collections import abc
@@ -33,11 +31,11 @@ class FrozenJSON:
using attribute notation
"""
def __new__(cls, arg):
def __new__(cls, arg): # <1>
if isinstance(arg, abc.Mapping):
return super().__new__(cls)
elif isinstance(arg, abc.MutableSequence):
return [FrozenJSON(item) for item in arg]
return super().__new__(cls) # <2>
elif isinstance(arg, abc.MutableSequence): # <3>
return [cls(item) for item in arg]
else:
return arg
@@ -48,4 +46,5 @@ class FrozenJSON:
if hasattr(self._data, name):
return getattr(self._data, name)
else:
return FrozenJSON(self._data[name])
return FrozenJSON(self._data[name]) # <4>
# END EXPLORE2

View File

@@ -2,12 +2,12 @@
{ "conferences": [{"serial": 115 }],
"events": [
{ "serial": 34505,
"name": "Why Schools Don&#x27;t Use Open Source to Teach Programming",
"name": "Why Schools Don´t Use Open Source to Teach Programming",
"event_type": "40-minute conference session",
"time_start": "2014-07-23 11:30:00",
"time_stop": "2014-07-23 12:10:00",
"venue_serial": 1462,
"description": "Aside from the fact that high school programming curricula often require proprietary IDEs, they also don&#x27;t involve examining any source code from Open Source software projects. What changes would be required in programming curricula to incorporate Open Source? And is that a desirable objective?",
"description": "Aside from the fact that high school programming...",
"website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505",
"speakers": [157509],
"categories": ["Education"] }
@@ -20,7 +20,7 @@
"position": "CTO",
"affiliation": "Sharewave",
"twitter": "sharewaveteam",
"bio": "Robert 'r0ml' Lefkowitz is the CTO at Sharewave, a startup building an investor management portal. This year, he was a resident at the winter session of Hacker School. He is a Distinguished Engineer of the ACM." }
"bio": "Robert ´r0ml´ Lefkowitz is the CTO at Sharewave, a startup..." }
],
"venues": [
{ "serial": 1462,

View File

@@ -1,43 +1,49 @@
"""
osconfeed.py: Script to download the OSCON schedule feed
>>> feed = load()
>>> sorted(feed['Schedule'].keys())
# BEGIN OSCONFEED_DEMO
>>> feed = load() # <1>
>>> sorted(feed['Schedule'].keys()) # <2>
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed['Schedule'].items()):
... print('{:3} {}'.format(len(value), key))
... print('{:3} {}'.format(len(value), key)) # <3>
...
1 conferences
484 events
357 speakers
53 venues
>>> feed['Schedule']['speakers'][-1]['name']
>>> feed['Schedule']['speakers'][-1]['name'] # <4>
'Carina C. Zona'
>>> carina = feed['Schedule']['speakers'][-1]
>>> carina['twitter']
'cczona'
>>> feed['Schedule']['speakers'][-1]['serial'] # <5>
141590
>>> feed['Schedule']['events'][40]['name']
'There *Will* Be Bugs'
>>> feed['Schedule']['events'][40]['speakers']
>>> feed['Schedule']['events'][40]['speakers'] # <6>
[3471, 5199]
# END OSCONFEED_DEMO
"""
# BEGIN OSCONFEED
from urllib.request import urlopen
import warnings
import os
import json
URL = 'http://www.oreilly.com/pub/sc/osconfeed'
JSON_NAME = 'data/osconfeed.json'
JSON = 'data/osconfeed.json'
def load():
if not os.path.exists(JSON_NAME):
msg = 'downloading {} to {}'.format(URL, JSON_NAME)
warnings.warn(msg)
with urlopen(URL) as remote, open(JSON_NAME, 'wb') as local:
if not os.path.exists(JSON):
msg = 'downloading {} to {}'.format(URL, JSON)
warnings.warn(msg) # <1>
with urlopen(URL) as remote, open(JSON, 'wb') as local: # <2>
local.write(remote.read())
with open(JSON_NAME) as fp:
return json.load(fp)
with open(JSON) as fp:
return json.load(fp) # <3>
# END OSCONFEED

View File

@@ -1,19 +1,26 @@
"""
schedule1.py: traversing OSCON schedule data
# BEGIN SCHEDULE1_DEMO
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
>>> event = db['event.33950']
>>> speaker = db['speaker.3471']
>>> speaker.name
>>> db = shelve.open(DB_NAME) # <1>
>>> if CONFERENCE not in db: # <2>
... load_db(db) # <3>
...
>>> speaker = db['speaker.3471'] # <4>
>>> type(speaker) # <5>
<class 'schedule1.Record'>
>>> speaker.name # <6>
'Anna Martelli Ravenscroft'
>>> speaker.twitter
'annaraven'
>>> db.close()
>>> db.close() # <7>
# END SCHEDULE1_DEMO
"""
# BEGIN SCHEDULE1
import warnings
import osconfeed
@@ -23,15 +30,18 @@ CONFERENCE = 'conference.115'
class Record:
def __init__(self, mapping):
self.__dict__.update(mapping)
def __init__(self, **kwargs):
self.__dict__.update(kwargs) # <1>
def load_db(db):
raw_data = osconfeed.load()
raw_data = osconfeed.load() # <2>
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
rec_type = collection[:-1]
for fields in rec_list:
key = '{}.{}'.format(rec_type, fields['serial'])
db[key] = Record(fields)
for collection, rec_list in raw_data['Schedule'].items(): # <3>
record_type = collection[:-1] # <4>
for record in rec_list:
key = '{}.{}'.format(record_type, record['serial']) # <5>
record['serial'] = key # <6>
db[key] = Record(**record) # <7>
# END SCHEDULE1

View File

@@ -4,82 +4,128 @@ schedule2.py: traversing OSCON schedule data
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
>>> DbRecord.set_db(db)
>>> event = Event.get('event.33950')
>>> event
# BEGIN SCHEDULE2_DEMO
>>> DbRecord.set_db(db) # <1>
>>> event = DbRecord.fetch('event.33950') # <2>
>>> event # <3>
<Event 'There *Will* Be Bugs'>
>>> event.speakers[0].name
'Anna Martelli Ravenscroft'
>>> event.venue # <4>
<DbRecord serial='venue.1449'>
>>> event.venue.name # <5>
'Portland 251'
>>> for spkr in event.speakers: # <6>
... print('{0.serial}: {0.name}'.format(spkr))
...
speaker.3471: Anna Martelli Ravenscroft
speaker.5199: Alex Martelli
# END SCHEDULE2_DEMO
>>> db.close()
"""
# BEGIN SCHEDULE2_RECORD
import warnings
import inspect
import inspect # <1>
import osconfeed
DB_NAME = 'data/schedule2_db'
DB_NAME = 'data/schedule2_db' # <2>
CONFERENCE = 'conference.115'
class Record:
def __init__(self, mapping):
self.__dict__.update(mapping)
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __eq__(self, other):
def __eq__(self, other): # <3>
if isinstance(other, Record):
return self.__dict__ == other.__dict__
else:
return NotImplemented
# END SCHEDULE2_RECORD
# BEGIN SCHEDULE2_DBRECORD
class MissingDatabaseError(RuntimeError):
"""Raised when a database is required but was not set.""" # <1>
class DbRecord(Record): # <2>
_db = None # <3>
@staticmethod # <4>
def set_db(db):
DbRecord._db = db # <5>
@staticmethod # <6>
def get_db():
return DbRecord._db
@classmethod # <7>
def fetch(cls, ident):
db = cls.get_db()
try:
return db[ident] # <8>
except TypeError:
if db is None: # <9>
msg = "database not set; call '{}.set_db(my_db)'"
raise MissingDatabaseError(msg.format(cls.__name__))
else: # <10>
raise
def __repr__(self):
if hasattr(self, 'name'):
ident = repr(self.name)
if hasattr(self, 'serial'): # <11>
cls_name = self.__class__.__name__
return '<{} serial={!r}>'.format(cls_name, self.serial)
else:
ident = 'object at ' + hex(id(self))
cls_name = self.__class__.__name__
return '<{} {}>'.format(cls_name, ident)
return super().__repr__() # <12>
# END SCHEDULE2_DBRECORD
class DbRecord(Record):
@classmethod
def set_db(cls, db):
cls._db = db
@classmethod
def get(cls, ident):
return cls._db[ident]
class Event(DbRecord):
# BEGIN SCHEDULE2_EVENT
class Event(DbRecord): # <1>
@property
def venue(self):
key = self.venue_serial
return self._db['venue.{}'.format(key)]
key = 'venue.{}'.format(self.venue_serial)
return self.fetch(key) # <2>
@property
def speakers(self):
spkr_serials = self.__dict__['speakers']
if not hasattr(self, '_speaker_refs'):
self._speaker_refs = [self._db['speaker.{}'.format(key)]
for key in spkr_serials]
return self._speaker_refs
if not hasattr(self, '_speaker_objs'): # <3>
spkr_serials = self.__dict__['speakers'] # <4>
self._speaker_objs = [self.fetch('speaker.{}'.format(key))
for key in spkr_serials] # <5>
return self._speaker_objs # <6>
def __repr__(self):
if hasattr(self, 'name'): # <7>
cls_name = self.__class__.__name__
return '<{} {!r}>'.format(cls_name, self.name)
else:
return super().__repr__() # <8>
# END SCHEDULE2_EVENT
# BEGIN SCHEDULE2_LOAD
def load_db(db):
raw_data = osconfeed.load()
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
rec_type = collection[:-1]
for fields in rec_list:
cls_name = rec_type.capitalize()
cls = globals().get(cls_name, Record)
if inspect.isclass(cls) and issubclass(cls, Record):
record = cls(fields)
else:
Record(fields)
key = '{}.{}'.format(rec_type, fields['serial'])
db[key] = record
record_type = collection[:-1] # <1>
cls_name = record_type.capitalize() # <2>
cls = globals().get(cls_name, DbRecord) # <3>
if inspect.isclass(cls) and issubclass(cls, DbRecord): # <4>
factory = cls # <5>
else:
factory = DbRecord # <6>
for record in rec_list: # <7>
key = '{}.{}'.format(record_type, record['serial'])
record['serial'] = key
db[key] = factory(**record) # <>
# END SCHEDULE2_LOAD

View File

@@ -13,7 +13,7 @@ def db():
def test_record_class():
rec = schedule.Record({'spam': 99, 'eggs': 12})
rec = schedule.Record(spam=99, eggs=12)
assert rec.spam == 99
assert rec.eggs == 12

View File

@@ -13,16 +13,16 @@ def db():
def test_record_attr_access():
rec = schedule.Record({'spam': 99, 'eggs': 12})
rec = schedule.Record(spam=99, eggs=12)
assert rec.spam == 99
assert rec.eggs == 12
def test_record_repr():
rec = schedule.Record({'spam': 99, 'eggs': 12})
assert repr(rec).startswith('<Record object at 0x')
rec2 = schedule.Record({'name': 'Fido'})
assert repr(rec2) == "<Record 'Fido'>"
rec = schedule.DbRecord(spam=99, eggs=12)
assert 'DbRecord object at 0x' in repr(rec)
rec2 = schedule.DbRecord(serial=13)
assert repr(rec2) == "<DbRecord serial=13>"
def test_conference_record(db):
@@ -34,9 +34,14 @@ def test_speaker_record(db):
assert speaker.name == 'Anna Martelli Ravenscroft'
def test_missing_db_exception():
with pytest.raises(schedule.MissingDatabaseError):
schedule.DbRecord.fetch('venue.1585')
def test_dbrecord(db):
schedule.DbRecord.set_db(db)
venue = schedule.DbRecord.get('venue.1585')
venue = schedule.DbRecord.fetch('venue.1585')
assert venue.name == 'Exhibit Hall B'