diff --git a/loris/img_info.py b/loris/img_info.py index c17e591..a3c14ba 100644 --- a/loris/img_info.py +++ b/loris/img_info.py @@ -57,7 +57,7 @@ def default(self, obj): class ImageInfo(JP2Extractor): '''Info about the image. - See: + See: , Slots: width (int) @@ -65,9 +65,14 @@ class ImageInfo(JP2Extractor): scaleFactors [(int)] sizes [(str)]: the optimal sizes of the image to request tiles: [{}] - service (dict): services associated with the image profile (Profile): Features supported by the server/available for this image + service [{}]: optional - services associated with the image + attribution [{}]: optional - text that must be shown when content obtained from the Image API service is + displayed or used + license []: optional - link to an external resource that describes the license or rights statement under which + content obtained from the Image API service may be used + logo {}: optional - small image that represents an individual or organization associated with the content src_img_fp (str): the absolute path on the file system [non IIIF] src_format (str): the format of the source image file [non IIIF] @@ -80,27 +85,14 @@ class ImageInfo(JP2Extractor): 'attribution', 'logo', 'license', 'auth_rules', 'src_format', 'src_img_fp', 'color_profile_bytes') - def __init__(self, app=None, src_img_fp="", src_format="", extra={}): + def __init__(self, app=None, src_img_fp="", src_format="", attribution=None, logo=None, license=None, service=None, auth_rules=None): self.src_img_fp = src_img_fp self.src_format = src_format - self.attribution = None - self.logo = None - self.license = None - self.service = {} - self.auth_rules = extra - - # The extraInfo parameter can be used to override specific attributes. - # If there are extra attributes, drop an error. - bad_attrs = [] - for (k, v) in extra.get('extraInfo', {}).items(): - try: - setattr(self, k, v) - except AttributeError: - bad_attrs.append(k) - if bad_attrs: - raise ImageInfoException( - "Invalid parameters in extraInfo: %s." % ', '.join(bad_attrs) - ) + self.attribution = attribution + self.logo = logo + self.license = license + self.service = service or {} + self.auth_rules = auth_rules or {} # If constructed from JSON, the pixel info will already be processed if app: diff --git a/loris/resolver.py b/loris/resolver.py index 459e694..2170f65 100644 --- a/loris/resolver.py +++ b/loris/resolver.py @@ -25,13 +25,13 @@ logger = getLogger(__name__) -class _AbstractResolver(object): +class _AbstractResolver: def __init__(self, config): self.config = config if config: self.auth_rules_ext = self.config.get('auth_rules_ext', 'rules.json') - self.use_extra_info = self.config.get('use_extra_info', True) + self.use_auth_rules = self.config.get('use_auth_rules', False) def is_resolvable(self, ident): """ @@ -65,12 +65,10 @@ def resolve(self, app, ident, base_uri): cn = self.__class__.__name__ raise NotImplementedError('resolve() not implemented for %s' % (cn,)) - def get_extra_info(self, ident, source_fp): + def get_auth_rules(self, ident, source_fp): """ - Given the identifier and any resolved source file, find the associated - extra information to include in info.json, plus any additional authorizer - specific information. It might end up there after being copied by a - caching implementation, or live there permanently. + Given the identifier and any resolved source file (ie. on the filesystem), grab the associated + authentication rules from the filesystem (if they exist). Args: ident (str): @@ -80,11 +78,12 @@ def get_extra_info(self, ident, source_fp): Returns: dict: The dict of information to embed in info.json """ + if not self.use_auth_rules: + return {} xjsfp = source_fp.rsplit('.', 1)[0] + "." + self.auth_rules_ext if exists(xjsfp): - fh = open(xjsfp) - xjs = json.load(fh) - fh.close() + with open(xjsfp) as fh: + xjs = json.load(fh) return xjs else: return {} @@ -130,14 +129,13 @@ def is_resolvable(self, ident): return not self.source_file_path(ident) is None def resolve(self, app, ident, base_uri): - if not self.is_resolvable(ident): self.raise_404_for_ident(ident) source_fp = self.source_file_path(ident) format_ = self.format_from_ident(ident) - extra = self.get_extra_info(ident, source_fp) - return ImageInfo(app, source_fp, format_, extra) + auth_rules = self.get_auth_rules(ident, source_fp) + return ImageInfo(app, source_fp, format_, auth_rules=auth_rules) class ExtensionNormalizingFSResolver(SimpleFSResolver): @@ -347,19 +345,19 @@ def copy_to_cache(self, ident): # These files are < 2k in size, so fetch in one go. # Assumes that the rules will be next to the image # cache_dir is image specific, so this is easy - - bits = split(source_url) - fn = bits[1].rsplit('.', 1)[0] + "." + self.auth_rules_ext - rules_url = bits[0] + '/' + fn - try: - resp = requests.get(rules_url) - if resp.status_code == 200: - local_rules_fp = join(cache_dir, "loris_cache." + self.auth_rules_ext) - if not exists(local_rules_fp): - with open(local_rules_fp, 'w') as fh: - fh.write(resp.text) - except requests.exceptions.RequestException: - pass + if self.use_auth_rules: + bits = split(source_url) + fn = bits[1].rsplit('.', 1)[0] + "." + self.auth_rules_ext + rules_url = bits[0] + '/' + fn + try: + resp = requests.get(rules_url) + if resp.status_code == 200: + local_rules_fp = join(cache_dir, "loris_cache." + self.auth_rules_ext) + if not exists(local_rules_fp): + with open(local_rules_fp, 'w') as fh: + fh.write(resp.text) + except requests.exceptions.RequestException: + pass return local_fp @@ -368,11 +366,8 @@ def resolve(self, app, ident, base_uri): if not cached_file_path: cached_file_path = self.copy_to_cache(ident) format_ = self.get_format(cached_file_path, None) - if self.use_extra_info: - extra = self.get_extra_info(ident, cached_file_path) - else: - extra = {} - return ImageInfo(app, cached_file_path, format_, extra) + auth_rules = self.get_auth_rules(ident, cached_file_path) + return ImageInfo(app, cached_file_path, format_, auth_rules=auth_rules) class TemplateHTTPResolver(SimpleHTTPResolver): @@ -553,5 +548,5 @@ def resolve(self, app, ident, base_uri): cache_fp = self.cache_file_path(ident) format_ = self.format_from_ident(ident) - extra = self.get_extra_info(ident, cache_fp) - return ImageInfo(app, cache_fp, format_, extra) + auth_rules = self.get_auth_rules(ident, cache_fp) + return ImageInfo(app, cache_fp, format_, auth_rules=auth_rules) diff --git a/tests/img_info_t.py b/tests/img_info_t.py index b4e1390..cc982db 100644 --- a/tests/img_info_t.py +++ b/tests/img_info_t.py @@ -262,45 +262,24 @@ def test_info_from_json(self): self.assertEqual(info.tiles, self.test_jp2_color_tiles) self.assertEqual(info.sizes, self.test_jp2_color_sizes) - def test_extrainfo_appears_in_iiif_json(self): + def test_rights_licensing_properties_in_iiif_json(self): info = ImageInfo( src_img_fp=self.test_jpeg_fp, src_format=self.test_jpeg_fmt, - extra={'extraInfo': { - 'license': 'CC-BY', - 'logo': 'logo.png', - 'service': {'@id': 'my_service'}, - 'attribution': 'Author unknown', - }} + license='CC-BY', + logo='logo.png', + attribution='Author unknown', ) info.from_image_file() iiif_json = json.loads(info.to_iiif_json(base_uri='http://localhost/1234')) assert iiif_json['license'] == 'CC-BY' assert iiif_json['logo'] == 'logo.png' - assert iiif_json['service'] == {'@id': 'my_service'} assert iiif_json['attribution'] == 'Author unknown' class TestImageInfo: - def test_extrainfo_can_override_attributes(self): - info = ImageInfo(extra={'extraInfo': { - 'license': 'CC-BY', - 'logo': 'logo.png', - 'service': {'@id': 'my_service'}, - 'attribution': 'Author unknown', - }}) - assert info.license == 'CC-BY' - assert info.logo == 'logo.png' - assert info.service == {'@id': 'my_service'} - assert info.attribution == 'Author unknown' - - def test_invalid_extra_info_is_imageinfoexception(self): - with pytest.raises(ImageInfoException) as exc: - ImageInfo(extra={'extraInfo': {'foo': 'bar', 'baz': 'bat'}}) - assert 'Invalid parameters in extraInfo' in str(exc.value) - @pytest.mark.parametrize('src_format', ['', None, 'imgX']) def test_invalid_src_format_is_error(self, src_format): info = ImageInfo(src_format=src_format) diff --git a/tests/resolver_t.py b/tests/resolver_t.py index fdf46d3..b0345fb 100644 --- a/tests/resolver_t.py +++ b/tests/resolver_t.py @@ -107,10 +107,11 @@ def test_get_extra_info(self): with open(os.path.join(tmp, rules_filename), 'wb') as f: f.write(json.dumps({'rule': 1}).encode('utf8')) config = { - 'src_img_roots' : [tmp] + 'src_img_roots' : [tmp], + 'use_auth_rules': True } resolver = SimpleFSResolver(config=config) - assert resolver.get_extra_info(ident, source_fp) == {'rule': 1} + assert resolver.get_auth_rules(ident, source_fp) == {'rule': 1} class Test_SourceImageCachingResolver(loris_t.LorisTest): diff --git a/tests/simple_http_resolver_ut.py b/tests/simple_http_resolver_ut.py index 6b24617..86ac883 100644 --- a/tests/simple_http_resolver_ut.py +++ b/tests/simple_http_resolver_ut.py @@ -174,7 +174,7 @@ def test_config_assigned_to_resolver(self): 'uri_resolvable': True, 'user': 'TestUser', 'pw': 'TestPW', - 'use_extra_info': False, + 'use_auth_rules': True, } resolver = SimpleHTTPResolver(config) @@ -186,7 +186,7 @@ def test_config_assigned_to_resolver(self): self.assertEqual(resolver.uri_resolvable, True) self.assertEqual(resolver.user, 'TestUser') self.assertEqual(resolver.pw, 'TestPW') - self.assertEqual(resolver.use_extra_info, False) + self.assertEqual(resolver.use_auth_rules, True) def test_barebones_config(self): config = { @@ -203,4 +203,4 @@ def test_barebones_config(self): self.assertEqual(resolver.uri_resolvable, True) self.assertEqual(resolver.user, None) self.assertEqual(resolver.pw, None) - self.assertEqual(resolver.use_extra_info, True) + self.assertEqual(resolver.use_auth_rules, False)