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.
.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
.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. State/Invariants: .value: [ a string containing zero, one or two nonblank 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
(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 - - - VALID_HOW_CODES = "ABCEFHIJLMOPSTW" OLD_HOW_TRANSLATOR = string.maketrans ( "23456789", "SEWCBMOO") @staticmethod 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 ] try: 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, “
#-- 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,
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
#-- 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 ( HowGroupField.OLD_HOW_TRANSLATOR)
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))