mmif-python 1.0.19__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mmif_python-1.0.19/mmif_python.egg-info → mmif_python-1.1.1}/PKG-INFO +18 -6
- mmif_python-1.1.1/VERSION +1 -0
- mmif_python-1.1.1/mmif/__init__.py +66 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/res/clams.vocabulary.yaml +130 -6
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/serialize/annotation.py +88 -38
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/serialize/mmif.py +111 -111
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/serialize/model.py +3 -2
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/serialize/view.py +113 -44
- mmif_python-1.1.1/mmif/utils/cli/__init__.py +3 -0
- mmif_python-1.1.1/mmif/utils/cli/rewind.py +113 -0
- mmif_python-1.1.1/mmif/utils/cli/source.py +293 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/utils/sequence_helper.py +1 -1
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/utils/text_document_helper.py +2 -1
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/utils/timeunit_helper.py +14 -3
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/utils/video_document_helper.py +81 -33
- mmif_python-1.1.1/mmif/ver/__init__.py +2 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/vocabulary/annotation_types.py +27 -14
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/vocabulary/base_types.py +12 -1
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/vocabulary/document_types.py +2 -2
- {mmif_python-1.0.19 → mmif_python-1.1.1/mmif_python.egg-info}/PKG-INFO +18 -6
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif_python.egg-info/SOURCES.txt +9 -4
- mmif_python-1.1.1/mmif_python.egg-info/entry_points.txt +2 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif_python.egg-info/requires.txt +2 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/requirements.cv +1 -0
- mmif_python-1.1.1/requirements.txt +3 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/setup.py +32 -21
- {mmif_python-1.0.19 → mmif_python-1.1.1}/tests/test_serialize.py +142 -83
- {mmif_python-1.0.19 → mmif_python-1.1.1}/tests/test_utils.py +49 -21
- mmif_python-1.1.1/tests/test_utils_cli.py +170 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/tests/test_vocab.py +3 -3
- mmif_python-1.0.19/VERSION +0 -1
- mmif_python-1.0.19/mmif/__init__.py +0 -17
- mmif_python-1.0.19/mmif/ver/__init__.py +0 -2
- mmif_python-1.0.19/requirements.txt +0 -2
- {mmif_python-1.0.19 → mmif_python-1.1.1}/LICENSE +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/MANIFEST.in +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/README.md +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/res/__init__.py +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/res/do-not-edit.txt +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/res/mmif.json +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/serialize/__init__.py +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/utils/__init__.py +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/ver/do-not-edit.txt +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/vocabulary/__init__.py +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif/vocabulary/do-not-edit.txt +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif_docloc_http/__init__.py +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif_python.egg-info/dependency_links.txt +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/mmif_python.egg-info/top_level.txt +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/requirements.seq +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/setup.cfg +0 -0
- {mmif_python-1.0.19 → mmif_python-1.1.1}/tests/test_versioncompat.py +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mmif-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python implementation of MultiMedia Interchange Format specification. (https://mmif.clams.ai)
|
|
5
5
|
Home-page: https://mmif.clams.ai
|
|
6
6
|
Author: Brandeis Lab for Linguistics and Computation
|
|
7
7
|
Author-email: admin@clams.ai
|
|
8
|
-
Classifier: Development Status ::
|
|
9
|
-
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
11
10
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
-
Requires-Python: >=3.
|
|
11
|
+
Requires-Python: >=3.10
|
|
13
12
|
Description-Content-Type: text/markdown
|
|
14
13
|
License-File: LICENSE
|
|
15
14
|
Requires-Dist: deepdiff>5
|
|
15
|
+
Requires-Dist: orderly-set==5.3.*
|
|
16
16
|
Requires-Dist: jsonschema
|
|
17
17
|
Provides-Extra: seq
|
|
18
18
|
Requires-Dist: numpy; extra == "seq"
|
|
@@ -20,11 +20,23 @@ Provides-Extra: cv
|
|
|
20
20
|
Requires-Dist: pillow; extra == "cv"
|
|
21
21
|
Requires-Dist: opencv-python; extra == "cv"
|
|
22
22
|
Requires-Dist: ffmpeg-python; extra == "cv"
|
|
23
|
+
Requires-Dist: wurlitzer; extra == "cv"
|
|
23
24
|
Provides-Extra: dev
|
|
24
25
|
Requires-Dist: pytest; extra == "dev"
|
|
25
26
|
Requires-Dist: pytest-pep8; extra == "dev"
|
|
26
27
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
27
28
|
Requires-Dist: pytype; extra == "dev"
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: author-email
|
|
31
|
+
Dynamic: classifier
|
|
32
|
+
Dynamic: description
|
|
33
|
+
Dynamic: description-content-type
|
|
34
|
+
Dynamic: home-page
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: provides-extra
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
28
40
|
|
|
29
41
|
## MultiMedia Interchange Format
|
|
30
42
|
[MMIF](https://mmif.clams.ai) is a JSON(-LD)-based data format designed for transferring annotation data between computational analysis applications in [CLAMS project](https://clams.ai).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.1.1
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from importlib.resources import files
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import importlib
|
|
5
|
+
import pkgutil
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
# DO NOT CHANGE THIS ORDER, important to prevent circular imports
|
|
9
|
+
from mmif.ver import __version__
|
|
10
|
+
from mmif.ver import __specver__
|
|
11
|
+
from mmif.vocabulary import *
|
|
12
|
+
from mmif.serialize import *
|
|
13
|
+
from mmif.utils.cli import rewind
|
|
14
|
+
from mmif.utils.cli import source
|
|
15
|
+
|
|
16
|
+
_res_pkg = 'res'
|
|
17
|
+
_ver_pkg = 'ver'
|
|
18
|
+
_vocabulary_pkg = 'vocabulary'
|
|
19
|
+
_schema_res_name = 'mmif.json'
|
|
20
|
+
version_template = "{} (based on MMIF spec: {})"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_mmif_json_schema():
|
|
24
|
+
return files(f'{__package__}.{_res_pkg}').joinpath(_schema_res_name).read_text()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def find_all_modules(pkgname):
|
|
28
|
+
parent = importlib.import_module(pkgname)
|
|
29
|
+
if not hasattr(parent, '__path__'):
|
|
30
|
+
raise ImportError(f"Error: '{pkgname}' is not a package.")
|
|
31
|
+
for importer, module, ispkg in pkgutil.walk_packages(parent.__path__, parent.__name__ + '.'):
|
|
32
|
+
if not ispkg: # Only process modules, not subpackages themselves
|
|
33
|
+
yield importlib.import_module(module)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def prep_argparser_and_subcmds():
|
|
37
|
+
parser = argparse.ArgumentParser()
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
'-v', '--version',
|
|
40
|
+
action='version',
|
|
41
|
+
version=version_template.format(__version__, __specver__)
|
|
42
|
+
)
|
|
43
|
+
subparsers = parser.add_subparsers(title='sub-command', dest='subcmd')
|
|
44
|
+
return parser, subparsers
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def cli():
|
|
48
|
+
parser, subparsers = prep_argparser_and_subcmds()
|
|
49
|
+
cli_modules = {}
|
|
50
|
+
for cli_module in find_all_modules('mmif.utils.cli'):
|
|
51
|
+
cli_module_name = cli_module.__name__.rsplit('.')[-1]
|
|
52
|
+
cli_modules[cli_module_name] = cli_module
|
|
53
|
+
subcmd_parser = cli_module.prep_argparser(add_help=False)
|
|
54
|
+
subparsers.add_parser(cli_module_name, parents=[subcmd_parser],
|
|
55
|
+
help=cli_module.describe_argparser()[0],
|
|
56
|
+
description=cli_module.describe_argparser()[1],
|
|
57
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
58
|
+
)
|
|
59
|
+
if len(sys.argv) == 1:
|
|
60
|
+
parser.print_help(sys.stderr)
|
|
61
|
+
sys.exit(1)
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
if args.subcmd not in cli_modules:
|
|
64
|
+
parser.print_help(sys.stderr)
|
|
65
|
+
else:
|
|
66
|
+
cli_modules[args.subcmd].main(args)
|
|
@@ -72,11 +72,25 @@ properties:
|
|
|
72
72
|
label:
|
|
73
73
|
type: String
|
|
74
74
|
description: >-
|
|
75
|
-
A label given to this object by
|
|
76
|
-
string value of the label and must be one of the values
|
|
77
|
-
<code>labelset</code> or <code>labelsetUri</code>
|
|
75
|
+
A label given to this object by classification. The value must be a
|
|
76
|
+
simple string value of the label and must be one of the values
|
|
77
|
+
defined in the <code>labelset</code> or <code>labelsetUri</code>
|
|
78
|
+
annotation metadata.
|
|
78
79
|
<br><br>
|
|
79
|
-
|
|
80
|
+
For example, for the <code>Sentence</code> subtype, this could
|
|
81
|
+
be used to indicate the type of sentence, such as "declarative",
|
|
82
|
+
"interrogative", "exclamatory", etc. For <code>NamedEntity</code>
|
|
83
|
+
subtype, this could be used to indicate the type of named entity,
|
|
84
|
+
such as "PER", "ORG", "LOC", "MISC" (following the CoNLL-2003 labels).
|
|
85
|
+
<br><br>
|
|
86
|
+
For non-linguistic annotations, for example for <code>TimeFrame<code>,
|
|
87
|
+
this could be used to indicate the type of the time frame, such as
|
|
88
|
+
"speech", "music", "noise", "bars-and-tones", etc, for acoustic
|
|
89
|
+
classification. Or "slate", "lower-third", "credits" for visual
|
|
90
|
+
classification of video frames.
|
|
91
|
+
<br><br>
|
|
92
|
+
[Note] Annotations from a type of classifier model must have this
|
|
93
|
+
property.
|
|
80
94
|
classifications:
|
|
81
95
|
type: Map from String to Number
|
|
82
96
|
description: >-
|
|
@@ -110,7 +124,9 @@ metadata:
|
|
|
110
124
|
description: >-
|
|
111
125
|
Specifies which unit of time the measurement is based.
|
|
112
126
|
Can be *seconds* or *milliseconds*, or in case of annotations on
|
|
113
|
-
a VideoDocument, *frames*.
|
|
127
|
+
a VideoDocument, *frames*. If not specified, *milliseconds* (in whole numbers) is assumed.
|
|
128
|
+
<br><br>
|
|
129
|
+
[Note] This metadata is only relevant for time-based annotations.
|
|
114
130
|
|
|
115
131
|
---
|
|
116
132
|
|
|
@@ -174,6 +190,115 @@ description: >-
|
|
|
174
190
|
pointing directly into primary data (by using start and end offsets) or
|
|
175
191
|
by linking to one or more other Annotations with the targets property.
|
|
176
192
|
|
|
193
|
+
properties:
|
|
194
|
+
text:
|
|
195
|
+
type: String
|
|
196
|
+
description: The surface string in the primary data covered by this span.
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
name: Token
|
|
200
|
+
parent: Span
|
|
201
|
+
|
|
202
|
+
similarTo:
|
|
203
|
+
- http://vocab.lappsgrid.org/Token
|
|
204
|
+
|
|
205
|
+
description: >-
|
|
206
|
+
A string of one or more characters that serves as an indivisible unit
|
|
207
|
+
for the purposes of morpho-syntactic labeling (part of speech tagging).
|
|
208
|
+
|
|
209
|
+
properties:
|
|
210
|
+
pos:
|
|
211
|
+
type: String or URI
|
|
212
|
+
description: Part-of-speech tag associated with the token.
|
|
213
|
+
lemma:
|
|
214
|
+
type: String or URI
|
|
215
|
+
description: >-
|
|
216
|
+
The root (base) form associated with the token. URI may point to a
|
|
217
|
+
lexicon entry.
|
|
218
|
+
orth:
|
|
219
|
+
type: String or URI
|
|
220
|
+
description: >-
|
|
221
|
+
Orthographic properties of the token such as LowerCase, UpperCase,
|
|
222
|
+
UpperInitial, etc. Ideally a URI referencing a pre-defined descriptor.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
name: Sentence
|
|
227
|
+
parent: Span
|
|
228
|
+
|
|
229
|
+
similarTo:
|
|
230
|
+
- http://vocab.lappsgrid.org/Sentence
|
|
231
|
+
|
|
232
|
+
description: >
|
|
233
|
+
A sequence of words capable of standing alone to make an assertion, ask a
|
|
234
|
+
question, or give a command, usually consisting of a subject and a predicate
|
|
235
|
+
containing a finite verb.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
name: Paragraph
|
|
240
|
+
parent: Span
|
|
241
|
+
|
|
242
|
+
similarTo:
|
|
243
|
+
- http://vocab.lappsgrid.org/Paragraph
|
|
244
|
+
|
|
245
|
+
description: >-
|
|
246
|
+
A division of a piece of writing, usually dealing with a single theme and
|
|
247
|
+
indicated by a new line, indentation, and/or numbering.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
name: NamedEntity
|
|
252
|
+
parent: Span
|
|
253
|
+
|
|
254
|
+
similarTo:
|
|
255
|
+
- http://vocab.lappsgrid.org/NamedEntity
|
|
256
|
+
|
|
257
|
+
description: >-
|
|
258
|
+
A phrase that clearly identifies an individual from others that have similar
|
|
259
|
+
attributes, such as the name of a person, organization, location, artifact,
|
|
260
|
+
etc. as well as temporal expressions.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
name: NounChunk
|
|
265
|
+
parent: Span
|
|
266
|
+
|
|
267
|
+
similarTo:
|
|
268
|
+
- http://vocab.lappsgrid.org/NounChunk
|
|
269
|
+
|
|
270
|
+
description: >-
|
|
271
|
+
The initial portion of a non-recursive noun phrase up to the head, including
|
|
272
|
+
determiners but not including postmodifying prepositional phrases or clauses.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
name: VerbChunk
|
|
277
|
+
parent: Span
|
|
278
|
+
|
|
279
|
+
similarTo:
|
|
280
|
+
- http://vocab.lappsgrid.org/VerbChunk
|
|
281
|
+
|
|
282
|
+
description: >-
|
|
283
|
+
Non-recursive verb groups, which include modals, auxiliary verbs, and medial
|
|
284
|
+
adverbs, and end at the head verb or predicate adjective.
|
|
285
|
+
|
|
286
|
+
properties:
|
|
287
|
+
tense:
|
|
288
|
+
type: String or URI
|
|
289
|
+
description: >-
|
|
290
|
+
Provides tense information for the verb. Example values include BeVBG,
|
|
291
|
+
BeVBN, FutCon, HaveVBN, Pas, PasCon, PasPer, PasPerCon, Per, Pre, PreCon,
|
|
292
|
+
PrePer, PrePerCon, SimFut, SimPas, SimPre, none
|
|
293
|
+
voice:
|
|
294
|
+
type: String or URI
|
|
295
|
+
description: >-
|
|
296
|
+
Indicates if the verb group is active or passive. Possible values include
|
|
297
|
+
ACTIVE, PASSIVE, or NONE
|
|
298
|
+
neg:
|
|
299
|
+
type: String or URI
|
|
300
|
+
description: Indicates whether or not the verb is negated. Values include YES, NO.
|
|
301
|
+
|
|
177
302
|
---
|
|
178
303
|
|
|
179
304
|
name: TimeFrame
|
|
@@ -195,7 +320,6 @@ properties:
|
|
|
195
320
|
No longer encouraged to use, instead <code>label</code> property
|
|
196
321
|
should replace this property.
|
|
197
322
|
|
|
198
|
-
|
|
199
323
|
---
|
|
200
324
|
|
|
201
325
|
name: Chapter
|
|
@@ -49,10 +49,9 @@ class Annotation(MmifObject):
|
|
|
49
49
|
def __init__(self, anno_obj: Optional[Union[bytes, str, dict]] = None, *_) -> None:
|
|
50
50
|
self._type: ThingTypesBase = ThingTypesBase('')
|
|
51
51
|
# to store the parent view ID
|
|
52
|
-
self._parent_view_id = ''
|
|
53
52
|
self._props_ephemeral: AnnotationProperties = AnnotationProperties()
|
|
54
53
|
self._alignments = {} # to hold alignment information (Alignment anno long_id -> aligned anno long_id)
|
|
55
|
-
self.reserved_names.update(('
|
|
54
|
+
self.reserved_names.update(('_props_ephemeral', '_alignments'))
|
|
56
55
|
if not hasattr(self, 'properties'): # don't overwrite DocumentProperties on super() call
|
|
57
56
|
self.properties: AnnotationProperties = AnnotationProperties()
|
|
58
57
|
self._attribute_classes = {'properties': AnnotationProperties}
|
|
@@ -116,17 +115,18 @@ class Annotation(MmifObject):
|
|
|
116
115
|
in `TimeFrame` and `BoundingBox` respectively.
|
|
117
116
|
"""
|
|
118
117
|
prop_aliases = AnnotationTypes._prop_aliases.get(self._type.shortname, {})
|
|
119
|
-
for
|
|
118
|
+
for alias_rep, alias_group in prop_aliases.items():
|
|
120
119
|
if key_to_add in alias_group:
|
|
121
120
|
for alias in alias_group:
|
|
122
121
|
if alias != key_to_add:
|
|
123
122
|
self._props_ephemeral[alias] = val_to_add
|
|
124
123
|
if alias in self.properties.keys():
|
|
125
|
-
warning_msg = f'
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
warning_msg = (f'Both "{key_to_add}" and "{alias}" are in the properties of "{self.id}", '
|
|
125
|
+
f'however ')
|
|
126
|
+
if alias == alias_rep:
|
|
127
|
+
warning_msg += f'"{key_to_add}" is an alias of "{alias_rep}".'
|
|
128
128
|
else:
|
|
129
|
-
warning_msg += f'
|
|
129
|
+
warning_msg += f'"{key_to_add}" and "{alias}" are both aliases of "{alias_rep}".'
|
|
130
130
|
warning_msg += f'Having two synonyms in the same annotation can cause unexpected behavior. '
|
|
131
131
|
warnings.warn(warning_msg, UserWarning)
|
|
132
132
|
|
|
@@ -134,6 +134,8 @@ class Annotation(MmifObject):
|
|
|
134
134
|
"""
|
|
135
135
|
Check if the @type of this object matches.
|
|
136
136
|
"""
|
|
137
|
+
if isinstance(at_type, str):
|
|
138
|
+
at_type = ThingTypesBase.from_str(at_type)
|
|
137
139
|
return self.at_type == at_type
|
|
138
140
|
|
|
139
141
|
@property
|
|
@@ -149,13 +151,28 @@ class Annotation(MmifObject):
|
|
|
149
151
|
|
|
150
152
|
@property
|
|
151
153
|
def parent(self) -> str:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
if self.id_delimiter in self.properties.id:
|
|
155
|
+
id_split = self.properties.id.split(self.id_delimiter)
|
|
156
|
+
if len(id_split) == 2:
|
|
157
|
+
return id_split[0]
|
|
158
|
+
raise ValueError(f'Annotation {self.id} does not have a parent view, or its ID does not follow the expected format. ')
|
|
159
|
+
|
|
154
160
|
@parent.setter
|
|
155
|
-
def parent(self,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
def parent(self, parent_id: str) -> None:
|
|
162
|
+
"""
|
|
163
|
+
.. deprecated:: 1.1.0
|
|
164
|
+
Will be removed in 2.0.0.
|
|
165
|
+
Setting parent ID is no longer allowed. Instead, the parent ID
|
|
166
|
+
should be prefixed to the annotation ID.
|
|
167
|
+
|
|
168
|
+
Sets the parent view ID of this annotation.
|
|
169
|
+
:param parent_id:
|
|
170
|
+
"""
|
|
171
|
+
warnings.warn(
|
|
172
|
+
"Setting parent ID is deprecated and nothing happened. Use prefixed annotation ID instead.",
|
|
173
|
+
DeprecationWarning
|
|
174
|
+
)
|
|
175
|
+
|
|
159
176
|
|
|
160
177
|
@property
|
|
161
178
|
def id(self) -> str:
|
|
@@ -167,17 +184,27 @@ class Annotation(MmifObject):
|
|
|
167
184
|
|
|
168
185
|
@property
|
|
169
186
|
def long_id(self) -> str:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
187
|
+
warnings.warn(
|
|
188
|
+
'long_id is deprecated. Use `id` instead. ', DeprecationWarning
|
|
189
|
+
)
|
|
190
|
+
return self.id
|
|
174
191
|
|
|
175
192
|
@long_id.setter
|
|
176
193
|
def long_id(self, long_id: str) -> None:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
warnings.warn(
|
|
195
|
+
'long_id is deprecated. Use `id` instead. ', DeprecationWarning
|
|
196
|
+
)
|
|
197
|
+
self.id = long_id
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def _short_id(self) -> str:
|
|
201
|
+
# TODO (krim @ 6/27/25): DELETE THIS METHOD!
|
|
202
|
+
"""
|
|
203
|
+
Method to directly get "short" form of the ID, not supposed to be used for general purpose, since
|
|
204
|
+
we want to force usage of "long" form ID when recording and referring annotations.
|
|
205
|
+
:return:
|
|
206
|
+
"""
|
|
207
|
+
return self.properties.id
|
|
181
208
|
|
|
182
209
|
@staticmethod
|
|
183
210
|
def check_prop_value_is_simple_enough(
|
|
@@ -215,14 +242,7 @@ class Annotation(MmifObject):
|
|
|
215
242
|
# f"(\"{name}\": \"{str(value)}\"")
|
|
216
243
|
self._add_prop_aliases(name, value)
|
|
217
244
|
|
|
218
|
-
def
|
|
219
|
-
"""
|
|
220
|
-
A special getter for Annotation properties. This is to allow for
|
|
221
|
-
directly accessing properties without having to go through the
|
|
222
|
-
properties object, or view-level annotation properties encoded in the
|
|
223
|
-
``view.metadata.contains`` dict. Note that the regular props will take
|
|
224
|
-
the priority over the ephemeral props when there are conflicts.
|
|
225
|
-
"""
|
|
245
|
+
def __getitem__(self, prop_name: str):
|
|
226
246
|
if prop_name in {'at_type', '@type'}:
|
|
227
247
|
return str(self._type)
|
|
228
248
|
elif prop_name == 'properties':
|
|
@@ -234,14 +254,31 @@ class Annotation(MmifObject):
|
|
|
234
254
|
else:
|
|
235
255
|
raise KeyError(f"Property {prop_name} does not exist in this annotation.")
|
|
236
256
|
|
|
257
|
+
def get(self, prop_name: str, default=None):
|
|
258
|
+
"""
|
|
259
|
+
A getter for Annotation, will search for a property by its name,
|
|
260
|
+
and return the value if found, or the default value if not found.
|
|
261
|
+
This is designed to allow for directly accessing properties without
|
|
262
|
+
having to go through the properties object, or view-level
|
|
263
|
+
annotation metadata (common properties) encoded in the
|
|
264
|
+
``view.metadata.contains`` dict. Note that the regular properties
|
|
265
|
+
will take the priority over the view-level common properties when
|
|
266
|
+
there are name conflicts.
|
|
267
|
+
|
|
268
|
+
:param prop_name: the name of the property to get
|
|
269
|
+
:param default: the value to return if the property is not found
|
|
270
|
+
:return: the value of the property
|
|
271
|
+
"""
|
|
272
|
+
try:
|
|
273
|
+
return self.__getitem__(prop_name)
|
|
274
|
+
except KeyError:
|
|
275
|
+
return default
|
|
276
|
+
|
|
237
277
|
get_property = get
|
|
238
278
|
|
|
239
|
-
def __getitem__(self, prop_name: str):
|
|
240
|
-
return self.get(prop_name)
|
|
241
|
-
|
|
242
279
|
def __contains__(self, item):
|
|
243
280
|
try:
|
|
244
|
-
self.
|
|
281
|
+
self.__getitem__(item)
|
|
245
282
|
return True
|
|
246
283
|
except KeyError:
|
|
247
284
|
return False
|
|
@@ -339,7 +376,7 @@ class Document(Annotation):
|
|
|
339
376
|
else:
|
|
340
377
|
super().add_property(name, value)
|
|
341
378
|
|
|
342
|
-
def get(self, prop_name):
|
|
379
|
+
def get(self, prop_name, default=None):
|
|
343
380
|
"""
|
|
344
381
|
A special getter for Document properties. The major difference from
|
|
345
382
|
the super class's :py:meth:`Annotation.get` method is that Document
|
|
@@ -448,13 +485,21 @@ class AnnotationProperties(MmifObject, MutableMapping[str, T]):
|
|
|
448
485
|
"""
|
|
449
486
|
|
|
450
487
|
def __delitem__(self, key: str) -> None:
|
|
488
|
+
frm = None
|
|
451
489
|
for k in self.__iter__():
|
|
452
490
|
if k == key:
|
|
453
|
-
if k
|
|
454
|
-
del self.__dict__[k]
|
|
455
|
-
else:
|
|
491
|
+
if k in self._required_attributes:
|
|
456
492
|
raise AttributeError(f'Cannot delete a required attribute "{key}"!')
|
|
457
|
-
|
|
493
|
+
elif k in self._unnamed_attributes:
|
|
494
|
+
frm = self._unnamed_attributes
|
|
495
|
+
break
|
|
496
|
+
else:
|
|
497
|
+
frm = self.__dict__
|
|
498
|
+
break
|
|
499
|
+
if frm is not None:
|
|
500
|
+
del frm[key]
|
|
501
|
+
else:
|
|
502
|
+
raise KeyError(f'Key "{key}" not found.')
|
|
458
503
|
|
|
459
504
|
def __iter__(self) -> Iterator[str]:
|
|
460
505
|
"""
|
|
@@ -576,6 +621,11 @@ class DocumentProperties(AnnotationProperties):
|
|
|
576
621
|
return "".join((parsed_location.netloc, parsed_location.path))
|
|
577
622
|
|
|
578
623
|
def location_path(self) -> Optional[str]:
|
|
624
|
+
"""
|
|
625
|
+
.. deprecated:: 1.0.2
|
|
626
|
+
Will be removed in 2.0.0.
|
|
627
|
+
Use :meth:`location_path_resolved` instead.
|
|
628
|
+
"""
|
|
579
629
|
warnings.warn('location_path() is deprecated. Use location_path_resolved() instead.', DeprecationWarning)
|
|
580
630
|
return self.location_path_resolved()
|
|
581
631
|
|