add 'blank' field option to allow empty text in required fields (default: false)
This commit is contained in:
parent
74b7935ced
commit
5f4dc8b80f
5 changed files with 71 additions and 9 deletions
|
@ -66,9 +66,12 @@ def dump(fp, records, delim=None):
|
||||||
fp.write(delim)
|
fp.write(delim)
|
||||||
|
|
||||||
|
|
||||||
def dumps(records, delim=None):
|
def dumps(records, delim=None, skip_validation=False):
|
||||||
import io
|
import io
|
||||||
fp = io.BytesIO()
|
fp = io.BytesIO()
|
||||||
|
if not skip_validation:
|
||||||
|
for record in records:
|
||||||
|
record.validate()
|
||||||
dump(fp, records, delim=delim)
|
dump(fp, records, delim=delim)
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
return fp.read()
|
return fp.read()
|
||||||
|
|
|
@ -23,11 +23,12 @@ class Field(object):
|
||||||
is_read_only = False
|
is_read_only = False
|
||||||
_value = None
|
_value = None
|
||||||
|
|
||||||
def __init__(self, name=None, max_length=0, required=True, uppercase=True, creation_counter=None):
|
def __init__(self, name=None, max_length=0, blank=False, required=True, uppercase=True, creation_counter=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self._value = None
|
self._value = None
|
||||||
self._orig_value = None
|
self._orig_value = None
|
||||||
self.max_length = max_length
|
self.max_length = max_length
|
||||||
|
self.blank = blank
|
||||||
self.required = required
|
self.required = required
|
||||||
self.uppercase = uppercase
|
self.uppercase = uppercase
|
||||||
self.creation_counter = creation_counter or Field.creation_counter
|
self.creation_counter = creation_counter or Field.creation_counter
|
||||||
|
@ -97,9 +98,9 @@ class Field(object):
|
||||||
wrapper.width = 100
|
wrapper.width = 100
|
||||||
value = wrapper.wrap(value)
|
value = wrapper.wrap(value)
|
||||||
value = list([(" " * 9) + ('"' + x + '"') for x in value])
|
value = list([(" " * 9) + ('"' + x + '"') for x in value])
|
||||||
value.append(" " * 10 + ('_' * 10) * (wrapper.width / 10))
|
value.append(" " * 10 + ('_' * 10) * int(wrapper.width / 10))
|
||||||
value.append(" " * 10 + ('0123456789') * (wrapper.width / 10))
|
value.append(" " * 10 + ('0123456789') * int(wrapper.width / 10))
|
||||||
value.append(" " * 10 + ''.join(([str(x) + (' ' * 9) for x in range(wrapper.width / 10 )])))
|
value.append(" " * 10 + ''.join(([str(x) + (' ' * 9) for x in range(int(wrapper.width / 10))])))
|
||||||
|
|
||||||
start = counter['c']
|
start = counter['c']
|
||||||
counter['c'] += len(self._orig_value or self.value)
|
counter['c'] += len(self._orig_value or self.value)
|
||||||
|
@ -121,6 +122,9 @@ class TextField(Field):
|
||||||
raise ValidationError("value required", field=self)
|
raise ValidationError("value required", field=self)
|
||||||
if len(self.get_data()) > self.max_length:
|
if len(self.get_data()) > self.max_length:
|
||||||
raise ValidationError("value is too long", field=self)
|
raise ValidationError("value is too long", field=self)
|
||||||
|
if len(self.get_data().strip()) == 0 and (not self.blank and self.required):
|
||||||
|
print(self.name, 'blank', self.blank, self.required)
|
||||||
|
raise ValidationError("field cannot be blank", field=self)
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
value = str(self.value or '').encode('ascii') or b''
|
value = str(self.value or '').encode('ascii') or b''
|
||||||
|
@ -144,7 +148,7 @@ class TextField(Field):
|
||||||
|
|
||||||
class StateField(TextField):
|
class StateField(TextField):
|
||||||
def __init__(self, name=None, required=True, use_numeric=False, max_length=2):
|
def __init__(self, name=None, required=True, use_numeric=False, max_length=2):
|
||||||
super(StateField, self).__init__(name=name, max_length=2, required=required)
|
super(StateField, self).__init__(name=name, max_length=max_length, required=required)
|
||||||
self.use_numeric = use_numeric
|
self.use_numeric = use_numeric
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
|
@ -219,6 +223,10 @@ class BlankField(TextField):
|
||||||
def parse(self, s):
|
def parse(self, s):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if len(self.get_data()) != self.max_length:
|
||||||
|
raise ValidationError("blank field did not match expected length", field=self)
|
||||||
|
|
||||||
|
|
||||||
class ZeroField(BlankField):
|
class ZeroField(BlankField):
|
||||||
is_read_only = True
|
is_read_only = True
|
||||||
|
|
|
@ -39,7 +39,11 @@ class Model(object):
|
||||||
getattr(self, field_name).value = value
|
getattr(self, field_name).value = value
|
||||||
|
|
||||||
def get_fields(self):
|
def get_fields(self):
|
||||||
identifier = TextField("record_identifier", max_length=len(self.record_identifier), creation_counter=-1)
|
identifier = TextField(
|
||||||
|
"record_identifier",
|
||||||
|
max_length = len(self.record_identifier),
|
||||||
|
blank = len(self.record_identifier) == 0,
|
||||||
|
creation_counter=-1)
|
||||||
identifier.value = self.record_identifier
|
identifier.value = self.record_identifier
|
||||||
fields = [identifier]
|
fields = [identifier]
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -7,7 +7,7 @@ def pyaccuwage_tests():
|
||||||
return test_suite
|
return test_suite
|
||||||
|
|
||||||
setup(name='pyaccuwage',
|
setup(name='pyaccuwage',
|
||||||
version='0.2024.0',
|
version='0.2024.1',
|
||||||
packages=['pyaccuwage'],
|
packages=['pyaccuwage'],
|
||||||
scripts=[
|
scripts=[
|
||||||
'scripts/pyaccuwage-checkseq',
|
'scripts/pyaccuwage-checkseq',
|
||||||
|
|
|
@ -8,6 +8,7 @@ from pyaccuwage.fields import StateField
|
||||||
from pyaccuwage.fields import TextField
|
from pyaccuwage.fields import TextField
|
||||||
from pyaccuwage.fields import ZeroField
|
from pyaccuwage.fields import ZeroField
|
||||||
from pyaccuwage.fields import StaticField
|
from pyaccuwage.fields import StaticField
|
||||||
|
from pyaccuwage.fields import ValidationError
|
||||||
from pyaccuwage.model import Model
|
from pyaccuwage.model import Model
|
||||||
|
|
||||||
class TestModelOutput(unittest.TestCase):
|
class TestModelOutput(unittest.TestCase):
|
||||||
|
@ -90,7 +91,7 @@ class TestFileFormats(unittest.TestCase):
|
||||||
record_identifier = 'B' # 1 byte
|
record_identifier = 'B' # 1 byte
|
||||||
zero1 = ZeroField(max_length=32)
|
zero1 = ZeroField(max_length=32)
|
||||||
text1 = TextField(max_length=71)
|
text1 = TextField(max_length=71)
|
||||||
text2 = TextField(max_length=20)
|
text2 = TextField(max_length=20, required=False)
|
||||||
blank2 = BlankField(max_length=4)
|
blank2 = BlankField(max_length=4)
|
||||||
|
|
||||||
record_types = [TestModelA, TestModelB]
|
record_types = [TestModelA, TestModelB]
|
||||||
|
@ -130,3 +131,49 @@ class TestFileFormats(unittest.TestCase):
|
||||||
original_bytes = pyaccuwage.dumps(records)
|
original_bytes = pyaccuwage.dumps(records)
|
||||||
reloaded_bytes = pyaccuwage.dumps(records_loaded)
|
reloaded_bytes = pyaccuwage.dumps(records_loaded)
|
||||||
self.assertEqual(original_bytes, reloaded_bytes)
|
self.assertEqual(original_bytes, reloaded_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRequiredFields(unittest.TestCase):
|
||||||
|
def createTestRecord(self, required=False, blank=False):
|
||||||
|
class Record(pyaccuwage.model.Model):
|
||||||
|
record_length = 16
|
||||||
|
record_identifier = ''
|
||||||
|
test_field = TextField(max_length=16, required=required, blank=blank)
|
||||||
|
record = Record()
|
||||||
|
def dump():
|
||||||
|
return pyaccuwage.dumps([record])
|
||||||
|
return (record, dump)
|
||||||
|
|
||||||
|
def testRequiredBlankField(self):
|
||||||
|
(record, dump) = self.createTestRecord(required=True, blank=True)
|
||||||
|
record.test_field.value # if nothing is ever assigned, raise error
|
||||||
|
self.assertRaises(ValidationError, dump)
|
||||||
|
record.test_field.value = '' # value may be empty string
|
||||||
|
dump()
|
||||||
|
|
||||||
|
def testRequiredNonblankField(self):
|
||||||
|
(record, dump) = self.createTestRecord(required=True, blank=False)
|
||||||
|
record.test_field.value # if nothing is ever assigned, raise error
|
||||||
|
self.assertRaises(ValidationError, dump)
|
||||||
|
record.test_field.value = '' # value must not be empty string
|
||||||
|
self.assertRaises(ValidationError, dump)
|
||||||
|
record.test_field.value = 'hello'
|
||||||
|
dump()
|
||||||
|
|
||||||
|
def testOptionalBlankField(self):
|
||||||
|
(record, dump) = self.createTestRecord(required=False, blank=True)
|
||||||
|
record.test_field.value # OK if nothing is ever assigned
|
||||||
|
dump()
|
||||||
|
record.test_field.value = '' # OK if empty string is assigned
|
||||||
|
dump()
|
||||||
|
record.test_field.value = 'hello'
|
||||||
|
dump()
|
||||||
|
|
||||||
|
def testOptionalNonBlankField(self):
|
||||||
|
(record, dump) = self.createTestRecord(required=False, blank=False)
|
||||||
|
record.test_field.value # OK if nothing is ever assigned
|
||||||
|
dump()
|
||||||
|
record.test_field.value = '' # OK if empty string is assigned
|
||||||
|
dump()
|
||||||
|
record.test_field.value = 'hello'
|
||||||
|
dump()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue