Skip to content

Commit 1339a16

Browse files
committed
added appTags to app metadata, sibling patch to clamsproject/mmif#253
1 parent 85ae527 commit 1339a16

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

clams/app/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ def sign_view(self, view: View, runtime_conf: dict) -> None:
309309
:param runtime_conf: runtime configuration of the app as k-v pairs
310310
"""
311311
view.metadata.app = str(self.metadata.identifier)
312+
if self.metadata.app_tags:
313+
view.metadata.set_additional_property('appTags', list(self.metadata.app_tags))
312314
params_map = {p.name: p for p in self.metadata.parameters}
313315
if self._RAW_PARAMS_KEY in runtime_conf:
314316
for k, v in runtime_conf.items():

clams/appmetadata/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,14 @@ class AppMetadata(pydantic.BaseModel):
357357
None,
358358
description="(optional) A string-to-string map that can be used to store any additional metadata of the app."
359359
)
360+
app_tags: List[str] = pydantic.Field(
361+
[],
362+
description="(optional) A list of short string labels that classify what kind of work this app does "
363+
"(e.g. task name, output profile family). Used by downstream consumers as a first-pass filter "
364+
"for selecting views; not a substitute for inspecting actual output types and properties. "
365+
"The values declared here are propagated by the SDK into the ``appTags`` field of every view "
366+
"the app signs."
367+
)
360368
est_gpu_mem_min: int = pydantic.Field(
361369
0,
362370
description="(optional) Minimum GPU memory required to run the app, in megabytes (MB). "
@@ -472,6 +480,19 @@ def add_input_oneof(self, *inputs: Union[str, Input, vocabulary.ThingTypesBase])
472480
newinputs.append(i)
473481
self.input.append(newinputs)
474482

483+
def add_app_tag(self, *tags: str) -> None:
484+
"""
485+
Helper method to add one or more strings to the ``app_tags`` list,
486+
skipping any value that is already present.
487+
488+
:param tags: one or more tag strings to add
489+
"""
490+
for tag in tags:
491+
if not isinstance(tag, str) or not tag:
492+
raise ValueError(f"app tag must be a non-empty string: {tag!r}")
493+
if tag not in self.app_tags:
494+
self.app_tags.append(tag)
495+
475496
def add_output(self, at_type: Union[str, vocabulary.ThingTypesBase], **properties) -> Output:
476497
"""
477498
Helper method to add an element to the ``output`` list.

tests/test_clamsapp.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,33 @@ def test_sign_view(self):
262262
self.assertEqual(len(v4.metadata.appConfiguration), 6)
263263
self.assertEqual(len(v4.metadata.parameters['multivalued_param']), len(str(multiple_values)))
264264

265+
def test_app_tags_default_empty(self):
266+
# apps that don't declare tags should not write an appTags field to view metadata
267+
self.assertEqual(self.app.metadata.app_tags, [])
268+
m = Mmif(self.in_mmif)
269+
v = m.new_view()
270+
self.app.sign_view(v, {})
271+
self.assertIsNone(v.metadata.get('appTags'))
272+
273+
def test_app_tags_propagated_to_view(self):
274+
# tags declared on app metadata should appear verbatim in signed views
275+
self.app.metadata.add_app_tag('TemporalSegmentation', 'BarsDetection')
276+
m = Mmif(self.in_mmif)
277+
v = m.new_view()
278+
self.app.sign_view(v, {})
279+
self.assertEqual(
280+
v.metadata.get('appTags'),
281+
['TemporalSegmentation', 'BarsDetection'],
282+
)
283+
284+
def test_add_app_tag_dedupes_and_validates(self):
285+
self.app.metadata.add_app_tag('foo', 'bar', 'foo')
286+
self.assertEqual(self.app.metadata.app_tags, ['foo', 'bar'])
287+
with self.assertRaises(ValueError):
288+
self.app.metadata.add_app_tag('')
289+
with self.assertRaises(ValueError):
290+
self.app.metadata.add_app_tag(123) # type: ignore[arg-type]
291+
265292
def test_annotate(self):
266293
# The example app is hard-coded to **always** emit version mismatch warning
267294
out_mmif = self.app.annotate(self.in_mmif)

0 commit comments

Comments
 (0)