Next / Previous / Contents / Shipman's homepage

37. class HowGroupField: How-code group

This class handles scanning and flattening of two different areas of the encounter line: the how-aged codes and the how-sexed codes. The structure and code set is the same in both applications. See the relevant section of the specification.

At present, up to two how-codes are allowed, and if only one code is used, recommended practice is to use the first column and leave the second blank. However, many banders are careless about this, so we will effectively scoot the code one space to the left in this case.

Internally, the .s attribute contains only the non-blank how-codes, so if there are no how-codes (this is not recommended practice, but occurs frequently), the value of .s will be an empty string.
# - - - - -   c l a s s   H o w G r o u p F i e l d   - - - - -

class HowGroupField(FieldItem):
    '''Represents a group of how-codes.

      Exports:  As inherited.

          [ a string containing zero, one or two nonblank how-codes ]

37.1. HowGroupField.scanField(): Scan a group of how-codes

This method scans one how-codes field.

The class attribute VALID_HOW_CODES is a string containing all valid how-codes. HowGroupField.OLD_HOW_TRANSLATOR is a translation table that translates old-style how-codes to current practice. For more on the format of this translation table, see Section 27, “class SingleField: Generic single-character field”.

(Two obsolete how-codes are mentioned in an earlier version of this program: A or 1 for adult plumage; and H for hatching-year plumage. These are omitted unless they actually show up.)
# - - -   H o w G r o u p F i e l d . s c a n F i e l d   - - -

    OLD_HOW_TRANSLATOR = string.maketrans (

    def scanField(encounter, scan, fieldName):
        '''Parse a how-codes group.

        #-- 1 --
        # [ if the line in scan contains at least HOW_GROUP_L
        #   characters ->
        #     scan  :=  scan advanced by HOW_GROUP_L
        #     rawText  :=  those characters, uppercased
        #   else ->
        #     Log()  +:=  error message
        #     raise SyntaxError ]
            rawText = scan.move(HOW_GROUP_L).upper()
        except IndexError:
            scan.syntax("Expecting a field containing %s "
                          "how-codes." % HOW_GROUP_L)

Next we must perform single-ditto substitution, if any. See Section 74.22, “BaseEncounter.copyDitto(): Implement single-column ditto”.
        #-- 2 --
        # [ if (rawText contains at least one DITTO_CHAR and it validly
        #   dittoes a character from encounter.compiler.oldEncounter ->
        #     dittoFree  :=  rawText with all occurrences of DITTO_CHAR
        #         replaced from encounter.compiler.oldEncounter
        #   else if (rawText contains at least one DITTO_CHAR not
        #   validly used ->
        #     Log()  +:=  error message
        #     raise SyntaxError
        #   else -> I ]
        dittoFree = encounter.copyDitto(rawText,
            HowGroupField, fieldName, scan)

We discard any space characters (this logic, by the way, assumes HOW_GROUP_L is two or less; if it were three, we'd have to consider the possibility of internal space with valid characters on the outside) and uppercase letters if there are any.
        #-- 3 --
        # [ deblanked  :=  dittoFree with all leading and trailing
        #       space discarded, and uppercased ]
        deblanked = dittoFree.strip().upper()

What remains should contain only characters found in VALID_HOW_CODES or HowGroupField.OLD_HOW_TRANSLATOR.
        #-- 4 --
        # [ updated  :=  a list made of the elements of
        #       deblanked, with any characters from self.OLD_HOW_MAP
        #       replaced by the corresponding value in that map ]
        updated = deblanked.translate (

At this point, if all the remaining characters are in VALID_HOW_CODES, we're happy.
        #-- 5 --
        # [ if any characters in updated aren't in
        #   HowGroupField.VALID_HOW_CODES ->
        #     Log()  +:=  error message
        #     raise SyntaxError
        #   else -> I ]
        for  s in updated:
            if  s not in HowGroupField.VALID_HOW_CODES:
                scan.syntax("How-code '%s' is not valid." % s)

        #-- 6 --
        setattr(encounter, fieldName,
                HowGroupField(encounter, updated))