pyaccuwage/fields.py
Binh Van Nguyen 0646bf7b9b Added record validation functions for everything (that we saw in the PDF),
we should go over them once more to make sure we didn't miss anything, but
testing validation should probably be done after that. Verify that the
record ordering enforcement code is correct, then start thinking of how
to get data from external sources into the record generator.
2011-06-11 14:45:12 -05:00

222 lines
6.6 KiB
Python

import decimal, datetime
import inspect
import enums
class ValidationError(Exception):
def __init__(self, msg, field=None):
self.msg = msg
self.field = field
def __str__(self):
if self.field:
return "(%s.%s) %s" % (self.field.parent_name, self.field.name, self.msg)
else:
return repr(self.msg)
class Field(object):
creation_counter = 0
def __init__(self, name=None, max_length=0, required=True, uppercase=True):
self.name = name
self._value = None
self.max_length = max_length
self.required = required
self.uppercase = uppercase
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
def validate(self):
raise NotImplemented
def get_data(self):
raise NotImplemented
def __setvalue(self, value):
self._value = value
def __getvalue(self):
return self._value
value = property(__getvalue, __setvalue)
def read(self, fp):
if fp.tell() + self.max_length <= fp.len:
data = fp.read(self.max_length)
return self.parse(data)
return None
def parse(self, s):
self.value = s.strip()
class TextField(Field):
def validate(self):
if self.value == None and self.required:
raise ValidationError("value required", field=self)
if len(self.get_data()) > self.max_length:
raise ValidationError("value is too long", field=self)
def get_data(self):
value = self.value or ""
if self.uppercase:
value = value.upper()
return value.ljust(self.max_length).encode('ascii')
class StateField(TextField):
def __init__(self, name=None, required=True, use_numeric=False):
super(StateField, self).__init__(name=name, max_length=2, required=required)
self.use_numeric = use_numeric
def get_data(self):
value = self.value or ""
if value.strip() and self.use_numeric:
return str(enums.state_postal_numeric[value.upper()]).zfill(self.max_length)
else:
return value.ljust(self.max_length).encode('ascii')
def validate(self):
super(StateField, self).validate()
if self.value and self.value.upper() not in enums.state_postal_numeric.keys():
raise ValidationError("%s is not a valid state abbreviation" % self.value, field=self)
def parse(self, s):
if s.strip() and self.use_numeric:
states = dict( [(v,k) for (k,v) in enums.state_postal_numeric.items()] )
self.value = states[int(s)]
else:
self.value = s
class EmailField(TextField):
def __init__(self, name=None, required=True, max_length=None):
return super(EmailField, self).__init__(name=name, max_length=max_length,
required=required, uppercase=False)
class NumericField(TextField):
def validate(self):
super(NumericField, self).validate()
if self.value:
try:
int(self.value)
except ValueError:
raise ValidationError("field contains non-numeric characters", field=self)
def get_data(self):
value = self.value or ""
return str(value).zfill(self.max_length)
def parse(self, s):
self.value = int(s)
class StaticField(TextField):
def __init__(self, name=None, required=True, value=None):
super(StaticField, self).__init__(name=name, required=required,
max_length=len(value))
self._value = value
def parse(self, s):
pass
class BlankField(TextField):
def __init__(self, name=None, max_length=0, required=False):
super(TextField, self).__init__(name=name, max_length=max_length, required=required, uppercase=False)
def get_data(self):
return " " * self.max_length
def parse(self, s):
pass
class BooleanField(Field):
def __init__(self, name=None, required=True, value=None):
super(BooleanField, self).__init__(name=name, required=required, max_length=1)
self._value = value
def validate(self):
pass
def get_data(self):
return '1' if self._value else '0'
def parse(self, s):
self.value = (s == '1')
class MoneyField(Field):
def validate(self):
if self.value == None and self.required:
raise ValidationError("value required", field=self)
if len(str(int((self.value or 0)*100))) > self.max_length:
raise ValidationError("value is too long", field=self)
def get_data(self):
return str(int((self.value or 0)*100)).encode('ascii').zfill(self.max_length)
def parse(self, s):
self.value = decimal.Decimal(s) * decimal.Decimal('0.01')
class DateField(TextField):
def __init__(self, name=None, required=True, value=None):
super(TextField, self).__init__(name=name, required=required, max_length=8)
if value:
self.value = value
def get_data(self):
if self._value:
return self._value.strftime('%m%d%Y')
return '0' * self.max_length
def parse(self, s):
if int(s) > 0:
self.value = datetime.date(*[int(x) for x in s[4:8], s[0:2], s[2:4]])
else:
self.value = None
def __setvalue(self, value):
if isinstance(value, datetime.date):
self._value = value
elif value:
self._value = datetime.date(*[int(x) for x in value[4:8], value[0:2], value[2:4]])
else:
self._value = None
def __getvalue(self):
return self._value
value = property(__getvalue, __setvalue)
class MonthYearField(TextField):
def __init__(self, name=None, required=True, value=None):
super(TextField, self).__init__(name=name, required=required, max_length=6)
if value:
self.value = value
def get_data(self):
if self._value:
return self._value.strftime("%m%Y")
return '0' * self.max_length
def parse(self, s):
if int(s) > 0:
self.value = datetime.date(*[int(x) for x in s[2:6], s[0:2], 1])
else:
self.value = None
def __setvalue(self, value):
if isinstance(value, datetime.date):
self._value = value
elif value:
self._value = datetime.date(*[int(x) for x in value[2:6], value[0:2], 1])
else:
self._value = None
def __getvalue(self):
return self._value
value = property(__getvalue, __setvalue)