Next / Previous / Contents / Shipman's homepage

74.13. BaseEncounter.scanBody(): Scan encounter line body and tail sections

This method parses the common part of unbanded, new, and recap encounter lines: the body and tail. The “body” is the fixed-format part, and the “tail” is the free-format part that follows the body.

We define a class variable named BODY_FIELD_LIST that shows the classes and field names of the body part. For the classes that process each field type, see:

baseclasses.py
# - - -   B a s e E n c o u n t e r . s c a n B o d y   - - -

    BODY_FIELD_LIST = [
      (Spec4Field,        SPEC4_ATTR),
      (AgeCodeField,      AGE_CODE_ATTR),
      (HowGroupField,     HOW_AGED_ATTR),
      (WrpField,          WRP_ATTR),
      (SexCodeField,      SEX_CODE_ATTR),
      (HowGroupField,     HOW_SEXED_ATTR),
      (SkullField,        SKULL_ATTR),
      (CloacalField,      CLOACAL_ATTR),
      (BroodField,        BROOD_ATTR),
      (FatField,          FAT_ATTR),
      (BodyMoltField,     BODY_MOLT_ATTR),
      (FlightMoltField,   FLIGHT_MOLT_ATTR),
      (FlightWearField,   FLIGHT_WEAR_ATTR),
      (JuvenalField,      JUVENAL_ATTR),
      (MicroPPCField,     MICRO_PPC_ATTR),
      (MicroSSCField,     MICRO_SSC_ATTR),
      (MicroPPFField,     MICRO_PPF_ATTR),
      (MicroSSFField,     MICRO_SSF_ATTR),
      (MicroTTField,      MICRO_TT_ATTR),
      (MicroRRField,      MICRO_RR_ATTR),
      (MicroBPLField,     MICRO_BPL_ATTR),
      (MicroNFField,      MICRO_NF_ATTR),
      (WingField,         WING_ATTR),
      (MassField,         MASS_ATTR),
      (StatusField,       STATUS_ATTR),
      (DateField,         DATE_ATTR),
      (HHMField,          TIME_ATTR) ]

    def scanBody(self, scan):
        '''Scan encounter record body and tail.

          [ scan is a Scan object ->
              if scan contains a valid encounter body and tail in
              the context of self ->
                scan  :=  scan advanced to end of line
                self  :=  self with all fields from that line added
              else ->
                Log()  +:=  error message(s)
                raise SyntaxError ]
        '''

The body part of an encounter record contains a long sequence of fixed-size fields. We can parse these fields in a table-driven fashion by using Section 7, “scanFieldItems(): Parse a sequence of FieldItem objects”.

The logic here parses the MAPS 2006 format. Other body formats should use a class derived from BaseEncounter, and redefine the BODY_FIELD_LIST table appropriately.

baseclasses.py
        #-- 1 --
        # [ if the line in scan starts with a sequence of valid
        #   fields as described by self.BODY_FIELD_LIST ->
        #     scan  :=  scan advanced past those fields
        #     self  :=  self with values of those fields stored by
        #               the attribute names in self.BODY_FIELD_LIST
        #   else ->
        #     scan   :=   scan advanced no further than end of line
        #     Log()  +:=  error message(s)
        #     raise SyntaxError ]
        scanFieldItems(self, self.BODY_FIELD_LIST, scan)

The fields in self.BODY_FIELD_LIST stop just before where the station code field will be in multi-station sets; in single-station sets, there will be no station code. See Section 66.1, “StationCodeField.scanField().

baseclasses.py
        #-- 2 --
        # [ if (self.compiler is a multi-station set) and
        #   (line in scan starts with a valid station code in
        #   the context of self.compiler) ->
        #     scan  :=  scan advanced past that field
        #     self.(STATION_ATTR)  :=  a StationCodeField containing
        #                              that field
        #   else if (self.compiler is a multi-station set) and
        #   (line in scan does not start with a valid station code in
        #   the context of self.compiler) ->
        #     scan   :=   scan advanced no further than end of line
        #     Log()  +:=  error message(s)
        #     raise SyntaxError
        #   else ->
        #     self.(STATION_ATTR)  :=  a StationCodeField containing
        #         self.compiler.station.staCode ]
        if  self.compiler.location is not None:
            #-- 2.1 --
            # [ if line in scan starts with a valid station code in
            #   the context of self.compiler ->
            #     scan  :=  scan advanced past that field
            #     self.(STATION_ATTR)  :=  a Station object representing
            #         that field
            #   else ->
            #     scan   :=   scan advanced no further than end of line
            #     Log()  +:=  error message(s)
            #     raise SyntaxError ]
            StationCodeField.scanField(self, scan, STATION_ATTR)
        else:
            setattr(self, STATION_ATTR,
                    StationCodeField(self,
                                     self.compiler.station.staCode))

Next we parse the net field. In this protocol, we use the two-character version. Variants may wish to use a three- or four-character net field; the output record has that capacity. See Section 67.1, “Net2Field.scanField().

baseclasses.py
        #-- 3 --
        # [ if scan starts with a valid 2-character net field ->
        #     scan  :=  scan advanced past that field
        #     self.(NET_ATTR)  :=  that field
        #   else ->
        #     scan   :=   scan advanced no further than end of line
        #     Log()  +:=  error message(s)
        #     raise SyntaxError ]
        Net2Field.scanField(self, scan, NET_ATTR)

Next comes the alignment check character.

baseclasses.py
        #-- 4 --
        # [ if scan starts with an alignment check character ->
        #     scan  :=  scan advanced past that character
        #   else ->
        #     Log()  +:=  error message(s)
        #     raise SyntaxError ]
        m = scan.tabMatch(ALIGNMENT_CHAR)
        if  m is None:
            scan.syntax("Expecting the alignment character, '%s'." %
                        ALIGNMENT_CHAR)

All that remains is to scan the tail section; see Section 74.14, “BaseEncounter.scanTail(): Scan tail fields”.

baseclasses.py
        #-- 5 --
        # [ if the line in scan starts with a valid tail section ->
        #     scan  :=  scan advanced to end of line
        #     self  :=  self with values from the tail stored using
        #               attribute names from self.OUT_FIELD_LIST
        #   else ->
        #     scan   :=   scan advanced no further than end of line
        #     Log()  +:=  error message(s)
        #     raise SyntaxError ]
        self.scanTail(scan)