Next / Previous / Contents / TCC Help System / NM Tech homepage

21. class YearRow: Container for one year's records

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.

noteweb
# - - - - -   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 ]
    '''

21.1. YearRow.__init__(): Constructor

The constructor has little to do: save the year number, and set up an empty __monthMap.

noteweb
# - - -   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  =  {}

21.2. YearRow.__len__(): Number of contained months

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.

noteweb
# - - -   Y e a r R o w . _ _ l e n _ _

    def __len__ ( self ):
        '''Return the number of contained months.
        '''
        return len(self.__monthMap)

21.3. YearRow.__getitem__(): Retrieve one month

noteweb
# - - -   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]

21.4. YearRow.firstMonth(): Return the first month

noteweb
# - - -   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

21.5. YearRow.lastMonth(): Return the last month

noteweb
# - - -   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

21.6. YearRow.predecessor(): Find the previous month

This method finds the month that precedes a given month, if there is one.

noteweb
# - - -   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.

noteweb
        #-- 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]

21.7. YearRow.successor(): Find the following month

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.

noteweb
# - - -   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]

21.8. YearRow.readAllMonths(): Process input files for one year

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.

noteweb
# - - -   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”.

noteweb
        #-- 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”.

noteweb
        #-- 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 )

21.9. YearRow.readOneMonth(): Read one monthly file

This method attempts to read one monthly XML input file.

noteweb
# - - -   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 'yyyy-mm.xml', we can extract the month name by simple slicing.

noteweb
        #-- 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).

noteweb
        #-- 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”.

noteweb
        #-- 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 )

21.10. YearRow.writeMonthPages(): Generate monthly pages

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.

noteweb
# - - -   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 'mm', with each value a MonthCell instance that knows how to translate its monthly page. See Section 22.3, “MonthCell.writePage(): Render as XHTML”.

noteweb
        #-- 1 --
        for mm in self.__monthMap:
            monthCell  =  self.__monthMap[mm]
            monthCell.writePage()