pyaccuwage/pyaccuwage/fields.py
Binh Nguyen 961aedc0ae Added very important data cleaning added
TextField now cleans CR and LF from data, this is very important for not
breaking everything and leaving me completely confused.

Thank you, Lauren!
2014-02-01 15:10:40 -06:00

320 lines
9.5 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, creation_counter=None):
self.name = name
self._value = None
self._orig_value = None
self.max_length = max_length
self.required = required
self.uppercase = uppercase
self.creation_counter = creation_counter or 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
def __len__(self):
return self.max_length
value = property(__getvalue, __setvalue)
def read(self, fp):
data = fp.read(self.max_length)
if len(data) == self.max_length:
self._orig_value = data
return self.parse(data)
raise ValueError()
def parse(self, s):
self.value = s.strip()
def toJSON(self):
data = self.get_data()
return {
'__class__': self.__class__.__name__,
'name': self.name,
'maxLength': self.max_length,
'required': self.required,
'data': data,
'value': self._value,
'dataLength': len(data)
}
def fromJSON(self, o):
import re
self.__init__(
name=o['name'],
max_length=o['maxLength'],
required=o['required'],
)
if isinstance(o['value'], basestring) and re.match('^\d*\.\d*$', o['value']):
o['value'] = decimal.Decimal(o['value'])
self.value = o['value']
return self
def debug(self, counter):
import textwrap
value = (self._orig_value or str(self.value))
wrapper = textwrap.TextWrapper(replace_whitespace=False, drop_whitespace=False)
wrapper.width = 100
value = wrapper.wrap(value)
#value = textwrap.wrap(value, 100)
#print value
value = list(map(lambda x:(" " * 9) + ('"' + x + '"'), value))
#value[0] = '"' + value[0] + '"'
value.append(" " * 10 + ('_' * 10) * (wrapper.width / 10))
value.append(" " * 10 + ('0123456789') * (wrapper.width / 10))
value.append(" " * 10 + ''.join((map(lambda x:str(x) + (' ' * 9), range(wrapper.width / 10 )))))
#value.append((" " * 59) + map(lambda x:("%x" % x), range(16))
start = counter['c']
counter['c'] += len(self._orig_value or self.value)
end = counter['c']
return (
str(start) + '-' + str(end-1) +
' [' +
str(len(self._orig_value or '') or len(str(self.value))) +
'] ' + self.name +
'\n' +
'\n'.join(value)
)
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')[:self.max_length]
def __setvalue(self, value):
# NO NEWLINES
try:
value = value.replace('\n', '').replace('\r', '')
except AttributeError, e:
pass
self._value = value
def __getvalue(self):
return self._value
value = property(__getvalue, __setvalue)
class StateField(TextField):
def __init__(self, name=None, required=True, use_numeric=False, max_length=2):
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')[:self.max_length]
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 IntegerField(TextField):
def validate(self):
super(IntegerField, 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)[: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 CRLFField(TextField):
def __init__(self, name=None, required=False):
super(TextField, self).__init__(name=name, max_length=2, required=required, uppercase=False)
def __setvalue(self, value):
self._value = value
def __getvalue(self):
return self._value
value = property(__getvalue, __setvalue)
def get_data(self):
return '\r\n'
def parse(self, s):
self.value = s
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 __init__(self, name=None, max_length=0, required=False):
super(MoneyField, self).__init__(name=name, uppercase=False, max_length=max_length, required=required)
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)[: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)