For the format of encounter lines, see the specification.
This class method parses the raw encounter record and
stores the field values it finds into a BaseEncounter record, then returns that record
if all goes well.
Why is this a class method, and not a static method? In
the MAPS 2006 protocol, it was a static method. However,
in the process of writing the MAPS 2004 compiler, it
became apparent that it must be a class method, and the
class object must be used in place of references to BaseEncounter, so that it will call the derived
scanEncounter() instead of the one in the
base class.
# - - - B a s e E n c o u n t e r . s c a n L i n e - - -
# @classmethod
def scanLine(myClass, compiler, scan):
'''Scan a raw encounter record.
[ (myClass is derived from BaseEncounter) and
(compiler is a BaseCompiler) and
(scan is a Scan object) ->
if the line in scan is a valid encounter line ->
return a new myClass object representing that
line
else ->
Log() +:= error message(s)
raise SyntaxError ]
'''
The first task is to look at the first character and see what kind of record it is. In many cases, the first character is a letter, one of the “encounter codes” described in the specification under “Banding sheet layout.”. There are two exceptions:
The first letter is G for a
“general new-band record,” which is
used for new bands that are not in the current string
prefix. The format is the same as the N format except that all nine digits of the band
number are present, not just the last two.
The initial R can be omitted from
recapture lines. Therefore, we'll consider any line
that doesn't start with one of the other codes to be
a recap line.
The class attribute .dispatchMap is a
dictionary that gives the correspondence between
(uppercased) encounter codes and the methods that parse
each line type. This dictionary is defined in Section 74.12, “BaseEncounter.dispatchMap: Encounter
record routing dictionary”; this dictionary
must be declared after all the methods that it points to.
Note that all the parsing methods in this table must have
intended functions that match the generic intended
function given right before .dispatchMap.
First we pull out the first character, upshift it (to be
case-insensitive), and see if it's a key in .dispatchMap. If so, we store the upshifted
version in self.captureCode. If not, we
use CAPTURE_RECAP.
#-- 1 --
# [ if the line in scan is empty ->
# raise SyntaxError
# else ->
# first := first character of that line, uppercased ]
if scan.pos < len(scan.line):
first = scan.line[scan.pos].upper()
else:
message = ("Programming error: empty line passed to "
"BaseEncounter.scan()")
scan.syntax(message)
#-- 2 --
# [ encounter := a new myClass record with
# compiler=(compiler) ]
encounter = myClass(compiler)
If there is no page header line currently in effect, an encounter line is not valid. Normally, though, at this point we will copy the band size and page number into the encounter object.
#-- 3 --
# [ if compiler.pageHeader is None ->
# Log() +:= error message
# raise SyntaxError
# else ->
# encounter.(BAND_SIZE_ATTR) := compiler.pageHeader.size
# encounter.(PAGE_NO_ATTR) := compiler.pageHeader.pageNo ]
if compiler.pageHeader is None:
scan.syntax("There is no page header line in effect.")
else:
setattr(encounter, BAND_SIZE_ATTR,
BandSizeField(encounter,
compiler.pageHeader.size))
setattr(encounter, PAGE_NO_ATTR,
PageNoField(encounter,
compiler.pageHeader.pageNo))
All encounter records must have a location code. For a
multi-station set, that comes from the compiler's .location.locCode. For a single-station set, it
comes from the compiler's .station.loc.locCode.
We wrap the location code in a LocationField object so there will be a .flatten()
method for it.
#-- 4 --
if compiler.location is None:
setattr(encounter, LOCATION_ATTR,
LocationField(encounter,
compiler.station.loc.locCode))
else:
setattr(encounter, LOCATION_ATTR,
LocationField(encounter,
compiler.location.locCode))
Similarly, we package the capture code in a CaptureCodeField object; see Section 31, “class CaptureCodeField: Encounter
record type code”.
#-- 5 --
# [ if first is a key in encounter.dispatchMap ->
# scan := scan advanced 1
# encounter.captureCode := first
# scanMethod := corresponding value from
# encounter.dispatchMap
# else ->
# encounter.captureCode := CAPTURE_RECAP
# scanMethod := encounter.dispatchMap[CAPTURE_RECAP] ]
try:
scanMethod = encounter.dispatchMap [ first ]
scan.move(1)
encounter.captureCode = CaptureCodeField(encounter,
first)
except KeyError:
first = CAPTURE_RECAP
encounter.captureCode = CaptureCodeField(encounter,
CAPTURE_RECAP)
scanMethod = myClass.dispatchMap [ CAPTURE_RECAP ]
Now we call the appropriate method for the line type.
For Cleanroom verification, compare this intended
function against the generic intended function for
encounter line parsers, which will be found at the start
of Section 74.12, “BaseEncounter.dispatchMap: Encounter
record routing dictionary”.
#-- 6 --
# [ if (encounter.captureCode) + (line in scan) is a valid
# encounter line in the context of self.compiler ->
# scan := scan advanced to the end of the line
# encounter := encounter with all fields from
# that line added using names in self.OUT_FIELD_LIST
# else ->
# Log() +:= error messages
# scan := scan advanced no further than end of line
# raise SyntaxError ]
scanMethod(encounter, scan)
#-- 7 --
return encounter
scanLine = classmethod(scanLine)