Skip to content

Commit

Permalink
Merge branch 'master' of github.com:NaturalHistoryMuseum/inselect
Browse files Browse the repository at this point in the history
  • Loading branch information
quicklizard99 committed Sep 12, 2016
2 parents 6f3437d + 9ab5ba1 commit 7a6246d
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ This is an overview of major changes. Refer to the git repository for a full log

Version 0.1.34
-------------
- #351 Metadata templates to allow fields with fixed values
- #349 Linux instructions
- #347 Explicitly reference python2 in scripts
- #342 Conda environment files
Expand Down
27 changes: 25 additions & 2 deletions inselect/gui/views/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ def _create_field_control(self, field, template):
return CountryComboBox(template)
elif 'language' == field.name:
return LanguageComboBox(template)
elif field.fixed_value:
return FixedValueControl(field.fixed_value)
elif field.choices:
combo = ChoicesFieldComboBox(field.name, template,
labels=field.choices)
Expand Down Expand Up @@ -235,10 +237,31 @@ def __init__(self, url, label, parent=None, f=0):
self.setOpenExternalLinks(True)


class FieldEdit(QLineEdit):
"""Updates the relevant model field when _editing_finished is called.
class FixedValueControl(QLabel):
"""A read-only value that is stored only in the template.
"""
def __init__(self, fixed_value, parent=None, flags=0):
super(FixedValueControl, self).__init__(fixed_value, parent, flags)

def update_model(self):
pass

def clear_selection(self):
pass

def clear_value(self, selected):
pass

def set_multiple(self, selected):
pass

def set_value(self, selected, value):
pass


class FieldEdit(QLineEdit):
"""Updates the relevant model field when _text_edited is called.
"""
def __init__(self, field, template, parent=None):
super(FieldEdit, self).__init__(parent)

Expand Down
14 changes: 10 additions & 4 deletions inselect/lib/persist_user_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class _FieldModel(Model):
label = StringType(serialized_name='Label')
group = StringType(serialized_name='Group')
uri = URLType(serialized_name='URI')
fixed_value = StringType(serialized_name='Fixed value')
mandatory = BooleanType(default=False, serialized_name='Mandatory')
choices = _UniqueListType(StringType, serialized_name='Choices')
choices_with_data = OrderedDictType(
Expand All @@ -148,10 +149,15 @@ def validate_name(self, data, value):
msg = u"Should not be one of {0}."
raise ValidationError(msg.format(RESERVED_FIELD_NAMES))

def validate_fixed_value(self, data, value):
pass

def validate_choices(self, data, value):
"'Choices' and 'Choices with data' are mutually exclusive"
if data.get('choices') and data.get('choices_with_data'):
msg = "'Choices' and 'Choices with data' are mutually exclusive."
"'Choices', 'Choices with data' and 'Fixed value' are mutually exclusive"
if 1 < sum(bool(data.get(field)) for field in
('choices', 'choices_with_data', 'fixed_value')):
msg = ("'Choices', 'Choices with data' and 'Fixed value' are "
"mutually exclusive.")
raise ValidationError(msg)

def validate_parser(self, data, value):
Expand Down Expand Up @@ -259,7 +265,7 @@ def validated_specification(spec):
try:
field = _FieldModel(f)
field.validate()
except (ModelConversionError, ValidationError), e:
except (ModelConversionError, ValidationError) as e:
failures += _extract_validation_error(e, prompt=f.get('Name'))
else:
model.fields.append(field)
Expand Down
15 changes: 12 additions & 3 deletions inselect/lib/user_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

import persist_user_template
from inselect.lib.parse import parse_matches_regex
from inselect.lib.utils import debug_print, FormatDefault
from inselect.lib.utils import FormatDefault


_Field = namedtuple('_Field', ('name', 'label', 'group', 'uri', 'mandatory',
'choices', 'choices_with_data', 'parse_fn'))
'fixed_value', 'choices', 'choices_with_data',
'parse_fn'))


class UserTemplate(object):
Expand Down Expand Up @@ -58,6 +59,7 @@ def __init__(self, spec):
group=field.get('Group'),
uri=field.get('URI'),
mandatory=field.get('Mandatory', False),
fixed_value=field.get('Fixed value'),
choices=field.get('Choices'),
choices_with_data=choices_with_data,
parse_fn=parse_fn))
Expand All @@ -67,6 +69,9 @@ def __init__(self, spec):
# Map from field name to a instance of _Field
self.fields_mapping = {f.name: f for f in fields}

# Map from field name to value
self.fixed_value_mapping = {f.name: f.fixed_value for f in fields if f.fixed_value}

# Map from field name to choices list
self.choices_mapping = {f.name: f.choices for f in fields if f.choices}

Expand Down Expand Up @@ -121,10 +126,14 @@ def metadata(self, index, metadata):
md = metadata.copy()
md['ItemNumber'] = index

# Consider fields with a 'Choices with data'
# Fields with a 'Choices with data'
for field, choices in self.choices_with_data_mapping.iteritems():
if field in md:
md[u'{0}-value'.format(field)] = choices.get(md[field], '')

# Fixed value fields
for field, value in self.fixed_value_mapping.iteritems():
md[field] = value
return md

def export_items(self, crop_fnames, document):
Expand Down
7 changes: 6 additions & 1 deletion inselect/tests/gui/test_metadata_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def test_template_name(self):
def test_controls(self):
controls = self.window.view_metadata._form_container.controls
# controls is a dict { control: field name }
self.assertEqual(3, len(controls))
self.assertEqual(4, len(controls))
self.assertIn('Department', controls.values())
self.assertIn('catalogNumber', controls.values())
self.assertIn('Location', controls.values())
self.assertIn('Taxonomy', controls.values())
Expand All @@ -40,6 +41,10 @@ def _control_for_field(self, field):
else:
raise ValueError('No field [{0}]'.format(field))

def test_department(self):
department = self._control_for_field('Department')
self.assertEqual('Palaeontology', department.text())

def test_catalog_number(self):
catalog_number = self._control_for_field('catalogNumber')
self.assertIsInstance(catalog_number, FieldEdit)
Expand Down
19 changes: 12 additions & 7 deletions inselect/tests/lib/test_document_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class TestDocumentExportWithTemplate(unittest.TestCase):
{
'Name': 'catalogNumber',
},
{
'Name': 'Department',
'Fixed value': 'Entomology',
},
{
'Name': 'scientificName',
'Choices with data': [(u'A', 1),
Expand Down Expand Up @@ -115,42 +119,43 @@ def test_csv_export(self):
'NormalisedBottom', 'ThumbnailLeft', 'ThumbnailTop',
'ThumbnailRight', 'ThumbnailBottom', 'OriginalLeft',
'OriginalTop', 'OriginalRight', 'OriginalBottom',
'catalogNumber', 'scientificName', 'scientificName-value'
'catalogNumber', 'Department', 'scientificName',
'scientificName-value'
]
self.assertEqual(headers, reader.next())

# Check only the metadata columns and 'original' coordinates
# columns, ignoring thumbnail (which doesn't exist)
# and normalised (which are floating point) coordinates
metadata_cols = itemgetter(0, 1, 10, 11, 12, 13, 14, 15, 16)
metadata_cols = itemgetter(0, 1, 10, 11, 12, 13, 14, 15, 16, 17)
self.assertEqual(
(u'01_1.png', u'1',
u'0', u'0', u'189', u'189',
u'1', u'A', u'1'),
u'1', u'Entomology', u'A', u'1'),
metadata_cols(reader.next())
)
self.assertEqual(
(u'02_2.png', u'2',
u'271', u'0', u'459', u'189',
u'2', u'B', u'2'),
u'2', u'Entomology', u'B', u'2'),
metadata_cols(reader.next())
)
self.assertEqual(
(u'03_10.png', u'3',
u'194', u'196', u'257', u'232',
u'3', u'インセクト', u'10'),
u'3', u'Entomology', u'インセクト', u'10'),
metadata_cols(reader.next())
)
self.assertEqual(
(u'04_3.png', u'4',
u'0', u'248', u'189', u'437',
u'4', u'Elsinoë', u'3'),
u'4', u'Entomology', u'Elsinoë', u'3'),
metadata_cols(reader.next())
)
self.assertEqual(
(u'05_4.png', u'5',
u'271', u'248', u'459', u'437',
u'5', u'D', u'4'),
u'5', u'Entomology', u'D', u'4'),
metadata_cols(reader.next())
)
self.assertIsNone(next(reader, None))
Expand Down
18 changes: 17 additions & 1 deletion inselect/tests/lib/test_persist_user_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,30 @@ def test_duplicated_labels(self):
res = self._invalid_specification(spec)
self.assertIn('Fields: Labels must be unique', res)

def test_choices_and_fixed_value(self):
"Both Choices and Fixed value given"
spec = {'Fields': [{
'Name': 'F', 'Choices': ['1', '2'],
'Fixed value': 'Entomology',
}]}
res = self._invalid_specification(spec)
expected = (
"F: Choices: 'Choices', 'Choices with data' and 'Fixed value' are "
"mutually exclusive."
)
self.assertIn(expected, res)

def test_choices_and_choices_with_data(self):
"Both Choices and Choices with data given"
spec = {'Fields': [{
'Name': 'F', 'Choices': ['1', '2'],
'Choices with data': [('1', 1), ('2', 2)],
}]}
res = self._invalid_specification(spec)
expected = "F: Choices: 'Choices' and 'Choices with data' are mutually exclusive."
expected = (
"F: Choices: 'Choices', 'Choices with data' and 'Fixed value' are "
"mutually exclusive."
)
self.assertIn(expected, res)

def test_duplicated_choices(self):
Expand Down
18 changes: 14 additions & 4 deletions inselect/tests/lib/test_user_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,20 @@ def test_from_file(self):
self.assertEqual(doc.name, "Test user template")
self.assertEqual(doc.cropped_file_suffix, '.jpg')
self.assertEqual(doc.thumbnail_width_pixels, 4096)
self.assertEqual(3, len(doc.fields))
self.assertEqual('catalogNumber', doc.fields[0].name)
self.assertEqual('Location', doc.fields[1].name)
self.assertEqual('Taxonomy', doc.fields[2].name)
self.assertEqual(4, len(doc.fields))
self.assertEqual('Department', doc.fields[0].name)
self.assertEqual('Palaeontology', doc.fields[0].fixed_value)
self.assertEqual('catalogNumber', doc.fields[1].name)
self.assertEqual('Catalog number', doc.fields[1].label)
self.assertEqual(
'http://rs.tdwg.org/dwc/terms/catalogNumber', doc.fields[1].uri
)
self.assertTrue(doc.fields[1].mandatory)
self.assertEqual('Catalog number', doc.fields[1].label)
self.assertEqual('Location', doc.fields[2].name)
self.assertTrue(doc.fields[2].mandatory)
self.assertEqual('Taxonomy', doc.fields[3].name)
self.assertTrue(doc.fields[3].mandatory)


if __name__ == '__main__':
Expand Down
2 changes: 2 additions & 0 deletions inselect/tests/test_data/test.inselect_template
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Name: Test user template
Fields:
- Name: Department
Fixed value: Palaeontology
- Name: catalogNumber
Label: Catalog number
URI: http://rs.tdwg.org/dwc/terms/catalogNumber
Expand Down

0 comments on commit 7a6246d

Please sign in to comment.