Each instance of this class holds the information in one row of the index table: the year number, and a list of the months for which valid HTML monthly pages have been built.
# - - - - - c l a s s Y e a r R o w
class YearRow:
'''Represents one year's line in the index table.
Exports:
YearRow ( yearCollection, txny, yyyy ):
[ (yearCollection is the parent YearCollection) and
(txny is a taxonomy.Txny instance) and
(yyyy is a four-digit year as a string) ->
return a new, empty YearRow with year=yyyy ]
.yearCollection: [ as passed to constructor, read-only ]
.txny: [ as passed to constructor, read-only ]
.yyyy: [ as passed to constructor, read-only ]
.__len__(self):
[ return the number of months in self ]
.__getitem(self, mm):
[ mm is a month key as 'mm' ->
if self has a month mm ->
return the corresponding MonthCell
else -> raise KeyError ]
.firstMonth():
[ if self has any months ->
return the month key of the first as 'mm'
else -> raise KeyError ]
.lastMonth():
[ if self has any months ->
return the month key of the last as 'mm'
else -> raise KeyError ]
.predecessor ( mm ):
[ if 'mm' is a month key in self ->
if self has any months before mm ->
return the last such month key as 'mm'
else -> raise KeyError ]
.successor ( mm ):
[ if 'mm' is a month key in self ->
if self has any months after mm ->
return the first such month key as 'mm'
else -> raise KeyError ]
.readAllMonths():
[ self := self with MonthCell instances added for
XML notes files in directory yyyy valid
against birdnotes.rnc and txny
sys.stderr +:= error message(s) for invalid XML
notes files in that directory, if any ]
State/Invariants:
.__monthMap:
[ a dictionary whose keys are two-digit, zero-filled
month numbers, and each corresponding value is the
MonthCell instance representing that month's
field notes ]
'''
The constructor has little to do: save the year number,
and set up an empty __monthMap.
# - - - Y e a r R o w . _ _ i n i t _ _
def __init__ ( self, yearCollection, txny, yyyy ):
'''Constructor
'''
self.yearCollection = yearCollection
self.txny = txny
self.yyyy = yyyy
self.__monthMap = {}
This method returns the number of months in self. Note: if there is a year directory but
there were no valid month files in it, this method will
return zero.
# - - - Y e a r R o w . _ _ l e n _ _
def __len__ ( self ):
'''Return the number of contained months.
'''
return len(self.__monthMap)
# - - - Y e a r R o w . _ _ g e t i t e m _ _
def __getitem__ ( self, mm ):
'''Retrieve one month
'''
return self.__monthMap[mm]
# - - - Y e a r R o w . f i r s t M o n t h
def firstMonth(self):
'''Retrieve self's first month number, if any.
'''
#-- 1 --
if len(self) == 0:
raise KeyError
#-- 2 --
# [ monthKeyList := month keys in self in ascending order ]
monthKeyList = self.__monthMap.keys()
monthKeyList.sort()
#-- 3 --
# [ return the month number from the first element
# of monthKeyList ]
return self[monthKeyList[0]].mm
# - - - Y e a r R o w . l a s t M o n t h
def lastMonth(self):
'''Retrieve self's last month, if any.
'''
#-- 1 --
if len(self) == 0:
raise KeyError
#-- 2 --
# [ monthKeyList := month keys in self in ascending order ]
monthKeyList = self.__monthMap.keys()
monthKeyList.sort()
#-- 3 --
# [ return the month number from the last element
# of monthKeyList ]
return self[monthKeyList[-1]].mm
This method finds the month that precedes a given month, if there is one.
# - - - Y e a r R o w . p r e d e c e s s o r
def predecessor ( self, mm ):
'''Find the month preceding month mm, if any
'''
First we find the set of month keys in self, then sort it in descending order, so that the
preceding month's key (if any) will follow mm's position in that list. We get to assume
that mm is a member of the list, by the
precondition on this method.
#-- 1 --
# [ mmList := keys of self.__monthMap in descending order ]
mmList = self.__monthMap.keys()
mmList.sort()
mmList.reverse()
#-- 2 --
# [ mm is a member of mmList ->
# pos := position of mm in mmList ]
pos = mmList.index ( mm )
#-- 3 --
# [ if pos+1 >= len(mmList) ->
# raise KeyError
# else ->
# return mmList[pos+1] ]
if pos+1 >= len(mmList):
raise KeyError
else:
return mmList[pos+1]
This method is the mirror image of Section 21.6, “YearRow.predecessor(): Find the
previous month”. The only difference
is that we sort the key list in ascending order instead
of descending.
# - - - Y e a r R o w . s u c c e s s o r
def successor ( self, mm ):
'''Find the following month.
'''
#-- 1 --
# [ mmList := keys of self.__monthMap in ascending order ]
mmList = self.__monthMap.keys()
mmList.sort()
#-- 2 --
# [ mm is a member of mmList ->
# pos := position of mm in mmList ]
pos = mmList.index ( mm )
#-- 3 --
# [ if pos+1 >= len(mmList) ->
# raise KeyError
# else ->
# return mmList[pos+1] ]
if pos+1 >= len(mmList):
raise KeyError
else:
return mmList[pos+1]
This method looks in subdirectory self.yyyy to see if there are any files whose names look like
monthly XML input files, and attempts to read them all.
Those which are valid will be stored in self.__monthMap; the invalid ones will generate
error messages.
# - - - Y e a r R o w . r e a d A l l M o n t h s
def readAllMonths ( self ):
'''Find and read all monthly files for this year.
'''
We use os.listdir() to get a list of all
the names in the year directory, filtering it with the
regular expression declared in Section 10.4, “YYYY_MM_XML_PAT: Month file name pattern”.
#-- 1 --
# [ monthFileNameList := names from directory
# ('./'+self.yyyy) that match YYYY_MM_XML_PAT ]
monthFileNameList = [ fileName
for fileName in os.listdir ( self.yyyy )
if YYYY_MM_XML_PAT.match(fileName) is not None ]
For each month file, we attempt to open and read that
file using the birdnotes.BirdNoteSet
constructor. The txny argument is
necessary to define the valid bird codes. See Section 21.9, “YearRow.readOneMonth(): Read one
monthly file”.
#-- 2 --
# [ self := self with MonthCell instances added
# representing files in monthFileNameList
# valid against birdnotes.rnc and txny
# sys.stderr +:= error message(s) for invalid
# files, if any ]
for monthFileName in monthFileNameList:
#-- 2 body --
# [ if monthFileName names a readable file
# valid against birdnotes.rnc and txny ->
# self.__monthMap +:= an entry whose key is the
# month number and whose value is a MonthCell
# representing that file
# else ->
# sys.stderr +:= error message(s) ]
self.readOneMonth ( monthFileName )
This method attempts to read one monthly XML input file.
# - - - Y e a r R o w . r e a d O n e M o n t h
def readOneMonth ( self, monthFileName ):
'''Try to read one month's field data.
[ monthFileName is a string matching YYYY_MM_XML_PAT ->
if monthFileName names a readable file
valid against birdnotes.rnc and txny ->
self.__monthMap +:= an entry whose key is the
month number and whose value is a MonthCell
representing that file
else ->
sys.stderr +:= error message(s) ]
'''
Since our precondition guarantees that monthFileName has the form ', we can extract the month name by simple
slicing.
yyyy-mm.xml'
#-- 1 --
# [ mm := month number from monthFileName
# monthPath := self.yyyy + '/' + monthFileName ]
#--
# 0 1
# 01234567890
# yyyy-mm.xml
#--
mm = monthFileName[5:7]
monthPath = '%s/%s' % (self.yyyy, monthFileName)
We'll create a BirdNoteSet instance
whose taxonomy comes from our self.txny
attribute. Then we'll call its .readFile() method to add the month file (if valid).
#-- 2 --
# [ birdNoteSet := a new birdnotes.BirdNoteSet instance
# with taxonomy self.txny ]
birdNoteSet = birdnotes.BirdNoteSet ( self.txny )
#-- 3 --
# [ if monthPath names a readable file valid against
# birdnotes.rnc ->
# birdNoteSet := birdNoteSet with data added
# from that file
# else ->
# sys.stderr +:= error message(s)
# return ]
try:
birdNoteSet.readFile ( monthPath )
except IOError, detail:
print >>sys.stderr, ( "*** Invalid monthly file "
"'%s': %s" % (monthFileName, detail) )
return
Finally, create a new MonthCell instance
and add it to self.__monthMap. See Section 22, “class MonthCell: One table cell”.
#-- 4 --
# [ self.__monthMap[mm] := a new MonthCell instance
# with self as the parent, month=mm, and
# birdNoteSet=birdNoteSet ]
self.__monthMap[mm] = MonthCell ( self, mm, birdNoteSet )
This method looks in the year directory for each XML file that contains a month's worth of notes. It translates the valid ones to HTML and updates the list of months.
# - - - Y e a r R o w . w r i t e M o n t h P a g e s
def writeMonthPages ( self ):
'''Generate all monthly pages for this year.
[ monthly web pages := rendering of valid monthly XML
files under directory self.year, if any ]
'''
The self.__monthMap dictionary contains
month keys of the form ', with each value a mm'MonthCell instance that knows how to translate
its monthly page. See Section 22.3, “MonthCell.writePage(): Render as
XHTML”.
#-- 1 --
for mm in self.__monthMap:
monthCell = self.__monthMap[mm]
monthCell.writePage()