TextField now cleans CR and LF from data, this is very important for not breaking everything and leaving me completely confused. Thank you, Lauren!
320 lines
9.5 KiB
Python
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)
|
|
|