mmif-python 1.0.17__tar.gz → 1.0.19__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.17 → mmif_python-1.0.19}/PKG-INFO +1 -1
- mmif_python-1.0.19/VERSION +1 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/serialize/annotation.py +4 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/serialize/mmif.py +50 -15
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/serialize/view.py +11 -0
- mmif_python-1.0.19/mmif/utils/__init__.py +0 -0
- mmif_python-1.0.19/mmif/ver/__init__.py +2 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_docloc_http/__init__.py +4 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_python.egg-info/PKG-INFO +1 -1
- {mmif_python-1.0.17 → mmif_python-1.0.19}/tests/test_serialize.py +33 -11
- mmif_python-1.0.17/VERSION +0 -1
- mmif_python-1.0.17/mmif/utils/__init__.py +0 -1
- mmif_python-1.0.17/mmif/ver/__init__.py +0 -2
- {mmif_python-1.0.17 → mmif_python-1.0.19}/LICENSE +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/MANIFEST.in +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/README.md +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/__init__.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/res/__init__.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/res/clams.vocabulary.yaml +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/res/do-not-edit.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/res/mmif.json +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/serialize/__init__.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/serialize/model.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/utils/sequence_helper.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/utils/text_document_helper.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/utils/timeunit_helper.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/utils/video_document_helper.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/ver/do-not-edit.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/vocabulary/__init__.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/vocabulary/annotation_types.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/vocabulary/base_types.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/vocabulary/do-not-edit.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif/vocabulary/document_types.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_python.egg-info/SOURCES.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_python.egg-info/dependency_links.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_python.egg-info/requires.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/mmif_python.egg-info/top_level.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/requirements.cv +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/requirements.seq +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/requirements.txt +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/setup.cfg +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/setup.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/tests/test_utils.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/tests/test_versioncompat.py +0 -0
- {mmif_python-1.0.17 → mmif_python-1.0.19}/tests/test_vocab.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mmif-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.19
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.19
|
|
@@ -323,6 +323,10 @@ class Document(Annotation):
|
|
|
323
323
|
only keep the latest value (in order of appearances in views list) of
|
|
324
324
|
the property, effectively overwriting the previous values.
|
|
325
325
|
"""
|
|
326
|
+
# we don't checking if this k-v already exists in _original (new props) or _ephemeral (read from existing MMIF)
|
|
327
|
+
# because it is impossible to keep the _original updated when a new annotation is added (via `new_annotation`)
|
|
328
|
+
# without look across other views and top-level documents list. Also see
|
|
329
|
+
# ``mmif.serialize.mmif.Mmif.generate_capital_annotations`` for where the "de-duplication" happens.
|
|
326
330
|
if name == "text":
|
|
327
331
|
self.properties.text = Text(value)
|
|
328
332
|
elif name == "mime":
|
|
@@ -115,13 +115,24 @@ class ViewsList(DataList[View]):
|
|
|
115
115
|
"""
|
|
116
116
|
super()._append_with_key(value.id, value, overwrite)
|
|
117
117
|
|
|
118
|
-
def
|
|
118
|
+
def get_last_contentful_view(self) -> Optional[View]:
|
|
119
119
|
"""
|
|
120
|
-
Returns the last view
|
|
120
|
+
Returns the last view that is contentful, i.e., has no error or warning .
|
|
121
121
|
"""
|
|
122
122
|
for view in reversed(self._items.values()):
|
|
123
|
-
if 'error' not in view.metadata and '
|
|
123
|
+
if 'error' not in view.metadata and 'warnings' not in view.metadata:
|
|
124
124
|
return view
|
|
125
|
+
|
|
126
|
+
def get_last_view(self) -> Optional[View]:
|
|
127
|
+
"""
|
|
128
|
+
Returns the last view appended.
|
|
129
|
+
"""
|
|
130
|
+
if self._items:
|
|
131
|
+
return self._items[list(self._items.keys())[-1]]
|
|
132
|
+
|
|
133
|
+
def get_last(self) -> Optional[View]:
|
|
134
|
+
warnings.warn('get_last() is deprecated, use get_last_contentful_view() instead.', DeprecationWarning)
|
|
135
|
+
return self.get_last_contentful_view()
|
|
125
136
|
|
|
126
137
|
|
|
127
138
|
class Mmif(MmifObject):
|
|
@@ -278,13 +289,23 @@ class Mmif(MmifObject):
|
|
|
278
289
|
See https://github.com/clamsproject/mmif-python/issues/226 for rationale
|
|
279
290
|
behind this behavior and discussion.
|
|
280
291
|
"""
|
|
281
|
-
# this view will be the default kitchen sink for all generated annotations
|
|
282
|
-
last_view = self.views.
|
|
292
|
+
# this view will be the default kitchen sink for all generated annotations.
|
|
293
|
+
last_view = self.views.get_last_contentful_view()
|
|
294
|
+
|
|
283
295
|
# proceed only when there's at least one view
|
|
284
296
|
if last_view:
|
|
297
|
+
|
|
298
|
+
# this app name is used to check a view is generated by the "currently running" app.
|
|
299
|
+
# knowing the currently running app is important so that properties of `Document` objects generated by the
|
|
300
|
+
# current app can be properly recorded inside the `Document` objects (since they are "writable" to the
|
|
301
|
+
# current app), instead of being recorded in a separate `Annotation` object.
|
|
302
|
+
current_app = last_view.metadata.app
|
|
303
|
+
|
|
285
304
|
# to avoid duplicate property recording, this will be populated with
|
|
286
305
|
# existing Annotation objects from all existing views
|
|
287
306
|
existing_anns = defaultdict(lambda: defaultdict(dict))
|
|
307
|
+
# ideally, if we can "de-duplicate" props at `add_property()` time, that'd be more efficient,
|
|
308
|
+
# but that is impossible without looking for the target `document` across other views and top documents list
|
|
288
309
|
|
|
289
310
|
# new properties to record in the current serialization call
|
|
290
311
|
anns_to_write = defaultdict(dict)
|
|
@@ -296,20 +317,30 @@ class Mmif(MmifObject):
|
|
|
296
317
|
for ann in view.get_annotations(AnnotationTypes.Annotation):
|
|
297
318
|
if doc_id is None:
|
|
298
319
|
doc_id = ann.get_property('document')
|
|
320
|
+
# only if we are sure that the document ID is unique across all views... (with v_id prefix)
|
|
321
|
+
# TODO (krim @ 7/15/24): update id checking once https://github.com/clamsproject/mmif/issues/228 is resolved
|
|
322
|
+
if not any([doc_id == doc.id for doc in self.documents]) and Mmif.id_delimiter not in doc_id:
|
|
323
|
+
doc_id = f"{view.id}{Mmif.id_delimiter}{doc_id}"
|
|
299
324
|
existing_anns[doc_id].update(ann.properties)
|
|
300
325
|
for doc in view.get_documents():
|
|
301
|
-
anns_to_write[doc.
|
|
326
|
+
anns_to_write[doc.long_id].update(doc._props_pending)
|
|
302
327
|
for doc in self.documents:
|
|
303
|
-
anns_to_write[doc.
|
|
328
|
+
anns_to_write[doc.long_id].update(doc._props_pending)
|
|
304
329
|
# additional iteration of views, to find a proper view to add the
|
|
305
330
|
# generated annotations. If none found, use the last view as the kitchen sink
|
|
306
331
|
last_view_for_docs = defaultdict(lambda: last_view)
|
|
307
332
|
doc_ids = set(anns_to_write.keys())
|
|
308
333
|
for doc_id in doc_ids:
|
|
334
|
+
if len(last_view.annotations) == 0:
|
|
335
|
+
# meaning, this new app didn't generate any annotation except for these document properties
|
|
336
|
+
# thus, we should add capital annotations to the last (empty) view
|
|
337
|
+
last_view_for_docs[doc_id] = last_view
|
|
338
|
+
break
|
|
309
339
|
for view in reversed(self.views):
|
|
310
340
|
# first try to find out if this view "contains" any annotation to the doc
|
|
311
341
|
# then, check for individual annotations
|
|
312
|
-
|
|
342
|
+
# TODO (krim @ 7/15/24): update id checking once https://github.com/clamsproject/mmif/issues/228 is resolved
|
|
343
|
+
if [cont for cont in view.metadata.contains.values() if doc_id.endswith(cont.get('document', 'TODO:this endswith test is a temporal solution we use until long_id is forced everywhere'))] \
|
|
313
344
|
or list(view.get_annotations(document=doc_id)):
|
|
314
345
|
last_view_for_docs[doc_id] = view
|
|
315
346
|
break
|
|
@@ -323,14 +354,18 @@ class Mmif(MmifObject):
|
|
|
323
354
|
if k != 'id' and existing_anns[doc_id][k] != v:
|
|
324
355
|
props[k] = v
|
|
325
356
|
if props:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
props.pop('document', None)
|
|
357
|
+
view_to_write = last_view_for_docs[doc_id]
|
|
358
|
+
if view_to_write.metadata.app == current_app and view_to_write.annotations.get(doc_id) is not None:
|
|
359
|
+
view_to_write.get_document_by_id(doc_id).properties.update(props)
|
|
330
360
|
else:
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
361
|
+
if len(anns_to_write) == 1:
|
|
362
|
+
# if there's only one document, we can record the doc_id in the contains metadata
|
|
363
|
+
view_to_write.metadata.new_contain(AnnotationTypes.Annotation, document=doc_id)
|
|
364
|
+
props.pop('document', None)
|
|
365
|
+
else:
|
|
366
|
+
# otherwise, doc_id needs to be recorded in the annotation property
|
|
367
|
+
props['document'] = doc_id
|
|
368
|
+
view_to_write.new_annotation(AnnotationTypes.Annotation, **props)
|
|
334
369
|
|
|
335
370
|
def sanitize(self):
|
|
336
371
|
"""
|
|
@@ -10,6 +10,7 @@ from datetime import datetime
|
|
|
10
10
|
from typing import Dict, Union, Optional, Generator, List, cast
|
|
11
11
|
|
|
12
12
|
from mmif import DocumentTypes, AnnotationTypes, ThingTypesBase, ClamsTypesBase
|
|
13
|
+
from mmif.serialize import model
|
|
13
14
|
from mmif.serialize.annotation import Annotation, Document
|
|
14
15
|
from mmif.serialize.model import PRMTV_TYPES, MmifObject, DataList, DataDict
|
|
15
16
|
|
|
@@ -439,6 +440,16 @@ class AnnotationsList(DataList[Union[Annotation, Document]]):
|
|
|
439
440
|
:return: None
|
|
440
441
|
"""
|
|
441
442
|
super()._append_with_key(value.id, value, overwrite)
|
|
443
|
+
|
|
444
|
+
def __getitem__(self, key: str):
|
|
445
|
+
"""
|
|
446
|
+
specialized getter implementation to workaround https://github.com/clamsproject/mmif/issues/228
|
|
447
|
+
# TODO (krim @ 7/12/24): annotation ids must be in the long form in the future, so this check will be unnecessary once https://github.com/clamsproject/mmif/issues/228 is resolved.
|
|
448
|
+
"""
|
|
449
|
+
if ":" in key:
|
|
450
|
+
_, aid = key.split(":")
|
|
451
|
+
return self._items.__getitem__(aid)
|
|
452
|
+
return self._items.get(key, None)
|
|
442
453
|
|
|
443
454
|
|
|
444
455
|
class ContainsDict(DataDict[ThingTypesBase, Contain]):
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mmif-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.19
|
|
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
|
|
@@ -1118,11 +1118,11 @@ class TestDocument(unittest.TestCase):
|
|
|
1118
1118
|
# then converted to an `Annotation` annotation during serialization at `Mmif`-level
|
|
1119
1119
|
mmif.add_document(doc1)
|
|
1120
1120
|
## no Annotation before serialization
|
|
1121
|
-
self.assertEqual(0, len(list(mmif.views.
|
|
1121
|
+
self.assertEqual(0, len(list(mmif.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation, author='me'))))
|
|
1122
1122
|
## after serialization, the `Annotation` annotation should be added to the last view
|
|
1123
1123
|
## note that we didn't add any views, so the last view before and after the serialization are the same
|
|
1124
1124
|
mmif_roundtrip = Mmif(mmif.serialize())
|
|
1125
|
-
self.assertTrue(next(mmif_roundtrip.views.
|
|
1125
|
+
self.assertTrue(next(mmif_roundtrip.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation, author='me')))
|
|
1126
1126
|
# finally, when deserialized back to a Mmif instance, the `Annotation` props should be added
|
|
1127
1127
|
# as a property of the document
|
|
1128
1128
|
doc1_mmif_roundtrip = mmif_roundtrip.get_document_by_id('doc1')
|
|
@@ -1135,7 +1135,7 @@ class TestDocument(unittest.TestCase):
|
|
|
1135
1135
|
mmif.add_document(doc1)
|
|
1136
1136
|
did = 'doc1'
|
|
1137
1137
|
# last view before serialization rounds
|
|
1138
|
-
r0_vid = mmif.views.
|
|
1138
|
+
r0_vid = mmif.views.get_last_contentful_view().id
|
|
1139
1139
|
doc1.at_type = DocumentTypes.TextDocument
|
|
1140
1140
|
doc1.id = did
|
|
1141
1141
|
doc1.location = 'aScheme:///data/doc1.txt'
|
|
@@ -1165,7 +1165,7 @@ class TestDocument(unittest.TestCase):
|
|
|
1165
1165
|
self.assertEqual(2, len(doc1._props_pending))
|
|
1166
1166
|
mmif_roundtrip2 = Mmif(mmif_roundtrip1.serialize())
|
|
1167
1167
|
## but not serialized
|
|
1168
|
-
self.assertEqual(0, len(list(mmif_roundtrip2.views.
|
|
1168
|
+
self.assertEqual(0, len(list(mmif_roundtrip2.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1169
1169
|
|
|
1170
1170
|
# adding non-duplicate value should be serialized into a new Annotation object
|
|
1171
1171
|
# even when there is a duplicate key in a previous view
|
|
@@ -1211,7 +1211,7 @@ class TestDocument(unittest.TestCase):
|
|
|
1211
1211
|
doc1.add_property('author', 'me')
|
|
1212
1212
|
mmif_roundtrip = Mmif(mmif.serialize())
|
|
1213
1213
|
## should be only one Annotation object, from manual call of `new_annotation`
|
|
1214
|
-
self.assertEqual(1, len(list(mmif_roundtrip.views.
|
|
1214
|
+
self.assertEqual(1, len(list(mmif_roundtrip.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1215
1215
|
|
|
1216
1216
|
# test with duplicate property added by a downstream app
|
|
1217
1217
|
mmif_roundtrip = Mmif(mmif.serialize())
|
|
@@ -1223,9 +1223,9 @@ class TestDocument(unittest.TestCase):
|
|
|
1223
1223
|
# author=me is already in the input MMIF
|
|
1224
1224
|
doc1_prime.add_property('author', 'me')
|
|
1225
1225
|
mmif_roundtrip2 = Mmif(mmif_roundtrip.serialize())
|
|
1226
|
-
self.assertEqual(1, len(list(mmif.views.
|
|
1226
|
+
self.assertEqual(1, len(list(mmif.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1227
1227
|
# none should be added
|
|
1228
|
-
self.assertEqual(0, len(list(mmif_roundtrip2.views.
|
|
1228
|
+
self.assertEqual(0, len(list(mmif_roundtrip2.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1229
1229
|
|
|
1230
1230
|
# test with same key, different value
|
|
1231
1231
|
mmif_roundtrip = Mmif(mmif.serialize())
|
|
@@ -1238,13 +1238,13 @@ class TestDocument(unittest.TestCase):
|
|
|
1238
1238
|
doc1_prime.add_property('author', 'you')
|
|
1239
1239
|
mmif_roundtrip2 = Mmif(mmif_roundtrip.serialize())
|
|
1240
1240
|
# the Annotation in the previous view should be preserved
|
|
1241
|
-
self.assertEqual(1, len(list(mmif.views.
|
|
1241
|
+
self.assertEqual(1, len(list(mmif.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1242
1242
|
# but a new one should be added
|
|
1243
|
-
self.assertEqual(1, len(list(mmif_roundtrip2.views.
|
|
1243
|
+
self.assertEqual(1, len(list(mmif_roundtrip2.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))))
|
|
1244
1244
|
|
|
1245
|
-
self.assertEqual('me', list(mmif.views.
|
|
1245
|
+
self.assertEqual('me', list(mmif.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))[0].get_property(
|
|
1246
1246
|
'author'))
|
|
1247
|
-
self.assertEqual('you', list(mmif_roundtrip2.views.
|
|
1247
|
+
self.assertEqual('you', list(mmif_roundtrip2.views.get_last_contentful_view().get_annotations(AnnotationTypes.Annotation))[
|
|
1248
1248
|
0].get_property('author'))
|
|
1249
1249
|
|
|
1250
1250
|
def test_capital_annotation_generation_viewfinder(self):
|
|
@@ -1269,6 +1269,28 @@ class TestDocument(unittest.TestCase):
|
|
|
1269
1269
|
cap_anns = list(mmif_roundtrip.views[f'v{i}'].get_annotations(AnnotationTypes.Annotation))
|
|
1270
1270
|
self.assertEqual(1, len(cap_anns))
|
|
1271
1271
|
self.assertEqual(authors[i-1], cap_anns[0].get_property('author'))
|
|
1272
|
+
|
|
1273
|
+
def test_capital_annotation_nongeneration_for_writable_documents(self):
|
|
1274
|
+
mmif = Mmif(validate=False)
|
|
1275
|
+
doc = Document()
|
|
1276
|
+
doc.at_type = DocumentTypes.TextDocument
|
|
1277
|
+
doc.id = f'doc0'
|
|
1278
|
+
doc.location = f'aScheme:///data/doc0.txt'
|
|
1279
|
+
mmif.add_document(doc)
|
|
1280
|
+
|
|
1281
|
+
v = mmif.new_view()
|
|
1282
|
+
v.metadata.app = tester_appname
|
|
1283
|
+
vid = v.id
|
|
1284
|
+
new_td_id = mmif[vid].new_textdocument(text='new text', document='doc0', origin='transformation').id
|
|
1285
|
+
doc.add_property('author', 'me')
|
|
1286
|
+
|
|
1287
|
+
mmif_roundtrip = Mmif(mmif.serialize())
|
|
1288
|
+
|
|
1289
|
+
self.assertTrue(AnnotationTypes.Annotation in mmif_roundtrip[vid].metadata.contains)
|
|
1290
|
+
self.assertTrue(mmif_roundtrip.get_document_by_id('doc0').get_property('author'), 'me')
|
|
1291
|
+
self.assertTrue(next(mmif_roundtrip[vid].get_annotations(AnnotationTypes.Annotation)).get_property('author'), 'me')
|
|
1292
|
+
self.assertTrue(next(mmif_roundtrip[vid].get_annotations(AnnotationTypes.Annotation)).get_property('document'), doc.id)
|
|
1293
|
+
self.assertTrue(mmif_roundtrip[new_td_id].get_property('origin'), 'transformation')
|
|
1272
1294
|
|
|
1273
1295
|
def test_deserialize_with_whole_mmif(self):
|
|
1274
1296
|
for i, datum in self.data.items():
|
mmif_python-1.0.17/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.0.17
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from mmif.utils import video_document_helper
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|