114 lines
4 KiB
Python
114 lines
4 KiB
Python
from .fields import Field, TextField, ValidationError
|
|
import copy
|
|
import collections
|
|
|
|
|
|
class Model(object):
|
|
record_length = -1
|
|
record_identifier = ' '
|
|
required = False
|
|
target_size = 512
|
|
|
|
def __init__(self):
|
|
if self.record_length == -1:
|
|
raise ValueError(self.record_length)
|
|
|
|
for (key, value) in list(self.__class__.__dict__.items()):
|
|
if isinstance(value, Field):
|
|
# GRAB THE FIELD INSTANCE FROM THE CLASS DEFINITION
|
|
# AND MAKE A LOCAL COPY FOR THIS RECORD'S INSTANCE,
|
|
# OTHERWISE WE'LL END UP WITH VALUES BEING SHARED
|
|
# ACROSS RECORDS.
|
|
src_field = self.__class__.__dict__[key]
|
|
if not src_field.name:
|
|
setattr(src_field, 'name', key)
|
|
setattr(src_field, 'parent_name', self.__class__.__name__)
|
|
new_field_instance = copy.copy(src_field)
|
|
new_field_instance._orig_value = None
|
|
new_field_instance._value = None
|
|
self.__dict__[key] = new_field_instance
|
|
|
|
def __setattr__(self, key, value):
|
|
if hasattr(self, key) and isinstance(getattr(self, key), Field):
|
|
self.set_field_value(key, value)
|
|
else:
|
|
# MAYBE THIS SHOULD RAISE A PROPERTY ERROR?
|
|
self.__dict__[key] = value
|
|
|
|
def set_field_value(self, field_name, value):
|
|
print('setfieldval: ' + field_name + ' ' + value)
|
|
getattr(self, field_name).value = value
|
|
|
|
def get_fields(self):
|
|
identifier = TextField("record_identifier", max_length=len(self.record_identifier), creation_counter=-1)
|
|
identifier.value = self.record_identifier
|
|
fields = [identifier]
|
|
|
|
for key in list(self.__class__.__dict__.keys()):
|
|
attr = getattr(self, key)
|
|
if isinstance(attr, Field):
|
|
fields.append(attr)
|
|
return fields
|
|
|
|
def get_sorted_fields(self):
|
|
fields = self.get_fields()
|
|
fields.sort(key=lambda x: x.creation_counter)
|
|
return fields
|
|
|
|
def validate(self):
|
|
for f in self.get_fields():
|
|
f.validate()
|
|
|
|
try:
|
|
custom_validator = getattr(self, 'validate_' + f.name)
|
|
except AttributeError:
|
|
continue
|
|
if isinstance(custom_validator, collections.Callable):
|
|
custom_validator(f)
|
|
|
|
def output(self, format='binary'):
|
|
if format == 'text':
|
|
return self.output_text()
|
|
return self.output_efile()
|
|
|
|
def output_efile(self):
|
|
result = b''.join([field.get_data() for field in self.get_sorted_fields()])
|
|
if self.record_length < 0 or len(result) != self.record_length:
|
|
raise ValidationError("Record result length not equal to %d bytes (%d)" % (self.record_length, len(result)))
|
|
return result
|
|
|
|
def output_text(self):
|
|
fields = self.get_sorted_fields()[1:] # skip record identifier
|
|
fields = [field for field in fields if not field.is_read_only]
|
|
header = ''.join(['---', self.__class__.__name__, '\n'])
|
|
return header + '\n'.join([f.name + ': ' + (str(f.value) if f.value else '') for f in fields]) + '\n\n'
|
|
|
|
def read(self, fp):
|
|
# Skip the first record, since that's an identifier
|
|
for field in self.get_sorted_fields()[1:]:
|
|
field.read(fp)
|
|
print(field.name, '"' + (str(field.value) or '') + '"', field.max_length, field._orig_value)
|
|
|
|
def toJSON(self):
|
|
return {
|
|
'__class__': self.__class__.__name__,
|
|
'fields': self.get_sorted_fields()
|
|
}
|
|
|
|
def fromJSON(self, o):
|
|
fields = o['fields']
|
|
|
|
identifier, fields = fields[0], fields[1:]
|
|
assert(identifier.value == self.record_identifier)
|
|
|
|
for f in fields:
|
|
target = self.__dict__[f.name]
|
|
|
|
if (target.required != f.required
|
|
or target.max_length != f.max_length):
|
|
print("Warning: value mismatch on import")
|
|
|
|
target.value = f.value
|
|
|
|
return self
|
|
|