mmif-python 1.0.12__tar.gz → 1.0.14__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.
Files changed (39) hide show
  1. {mmif-python-1.0.12/mmif_python.egg-info → mmif-python-1.0.14}/PKG-INFO +1 -1
  2. mmif-python-1.0.14/VERSION +1 -0
  3. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/serialize/annotation.py +34 -15
  4. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/serialize/mmif.py +40 -17
  5. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/serialize/model.py +26 -19
  6. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/serialize/view.py +39 -1
  7. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/utils/timeunit_helper.py +4 -4
  8. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/utils/video_document_helper.py +10 -8
  9. mmif-python-1.0.14/mmif/ver/__init__.py +2 -0
  10. {mmif-python-1.0.12 → mmif-python-1.0.14/mmif_python.egg-info}/PKG-INFO +1 -1
  11. mmif-python-1.0.12/VERSION +0 -1
  12. mmif-python-1.0.12/mmif/ver/__init__.py +0 -2
  13. {mmif-python-1.0.12 → mmif-python-1.0.14}/LICENSE +0 -0
  14. {mmif-python-1.0.12 → mmif-python-1.0.14}/MANIFEST.in +0 -0
  15. {mmif-python-1.0.12 → mmif-python-1.0.14}/README.md +0 -0
  16. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/__init__.py +0 -0
  17. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/res/__init__.py +0 -0
  18. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/res/clams.vocabulary.yaml +0 -0
  19. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/res/do-not-edit.txt +0 -0
  20. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/res/mmif.json +0 -0
  21. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/serialize/__init__.py +0 -0
  22. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/utils/__init__.py +0 -0
  23. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/utils/sequence_helper.py +0 -0
  24. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/ver/do-not-edit.txt +0 -0
  25. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/vocabulary/__init__.py +0 -0
  26. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/vocabulary/annotation_types.py +0 -0
  27. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/vocabulary/base_types.py +0 -0
  28. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/vocabulary/do-not-edit.txt +0 -0
  29. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif/vocabulary/document_types.py +0 -0
  30. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif_docloc_http/__init__.py +0 -0
  31. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif_python.egg-info/SOURCES.txt +0 -0
  32. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif_python.egg-info/dependency_links.txt +0 -0
  33. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif_python.egg-info/requires.txt +0 -0
  34. {mmif-python-1.0.12 → mmif-python-1.0.14}/mmif_python.egg-info/top_level.txt +0 -0
  35. {mmif-python-1.0.12 → mmif-python-1.0.14}/requirements.cv +0 -0
  36. {mmif-python-1.0.12 → mmif-python-1.0.14}/requirements.seq +0 -0
  37. {mmif-python-1.0.12 → mmif-python-1.0.14}/requirements.txt +0 -0
  38. {mmif-python-1.0.12 → mmif-python-1.0.14}/setup.cfg +0 -0
  39. {mmif-python-1.0.12 → mmif-python-1.0.14}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mmif-python
3
- Version: 1.0.12
3
+ Version: 1.0.14
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.14
@@ -26,7 +26,7 @@ __all__ = ['Annotation', 'AnnotationProperties', 'Document', 'DocumentProperties
26
26
 
27
27
  T = TypeVar('T')
28
28
  LIST_PRMTV = typing.List[PRMTV_TYPES] # list of values (most cases for annotation props)
29
- LIST_LIST_PRMTV = typing.List[LIST_PRMTV] # list of list of values (e.g. for coordinates)
29
+ LIST_LIST_PRMTV = typing.List[LIST_PRMTV] # list of lists of values (e.g. for coordinates)
30
30
  DICT_PRMTV = typing.Dict[str, PRMTV_TYPES] # dict of values (`text` prop of `TextDocument` and other complex props)
31
31
  DICT_LIST_PRMTV = typing.Dict[str, LIST_PRMTV] # dict of list of values (even more complex props)
32
32
 
@@ -127,6 +127,20 @@ class Annotation(MmifObject):
127
127
  def id(self, aid: str) -> None:
128
128
  self.properties.id = aid
129
129
 
130
+ @property
131
+ def long_id(self) -> str:
132
+ if self.parent is not None and len(self.parent) > 0:
133
+ return f"{self.parent}{self.id_delimiter}{self.id}"
134
+ else:
135
+ return self.id
136
+
137
+ @long_id.setter
138
+ def long_id(self, long_id: str) -> None:
139
+ if self.id_delimiter in long_id:
140
+ self.parent, self.id = long_id.split(self.id_delimiter)
141
+ else:
142
+ self.id = long_id
143
+
130
144
  @staticmethod
131
145
  def check_prop_value_is_simple_enough(
132
146
  value: Union[PRMTV_TYPES, LIST_PRMTV, LIST_LIST_PRMTV, DICT_PRMTV, DICT_LIST_PRMTV]) -> bool:
@@ -401,26 +415,31 @@ class AnnotationProperties(MmifObject, MutableMapping[str, T]):
401
415
 
402
416
  def __iter__(self) -> Iterator[str]:
403
417
  """
404
- ``__iter__`` on Mapping should basically work as ``keys()`` method of vanilla dict
405
- however, when MMIF objects are serialized, all optional (not in ``_req_atts``),
406
- empty props are ignored (note that emtpy but required props are serialized
407
- with the *emtpy* value).
408
- Hence, this ``__iter__`` method should also work in the same way and
409
- ignore empty optional props.
418
+ ``__iter__`` on Mapping should basically work as ``keys()`` method
419
+ of vanilla dict.
410
420
  """
411
421
  for key in itertools.chain(self._named_attributes(), self._unnamed_attributes):
412
- if key in self._required_attributes:
413
- yield key
414
- else:
415
- try:
416
- self.__getitem__(key)
417
- yield key
418
- except KeyError:
419
- pass
422
+ yield key
423
+
424
+ def __getitem__(self, key):
425
+ """
426
+ Parent MmifObject class has a __getitem__ method that checks if
427
+ the value is empty when asked for an unnamed attribute. But for
428
+ AnnotationProperties, any arbitrary property that's added
429
+ explicitly by the user (developer) should not be ignored and
430
+ returned even the value is empty.
431
+ """
432
+ if key in self._named_attributes():
433
+ return self.__dict__[key]
434
+ else:
435
+ return self._unnamed_attributes[key]
420
436
 
421
437
  def __init__(self, mmif_obj: Optional[Union[bytes, str, dict]] = None) -> None:
422
438
  self.id: str = ''
439
+ # any individual at_type (subclassing this class) can have its own set of required attributes
423
440
  self._required_attributes = ["id"]
441
+ # allowing additional attributes for arbitrary annotation properties
442
+ self._unnamed_attributes = {}
424
443
  super().__init__(mmif_obj)
425
444
 
426
445
 
@@ -10,13 +10,13 @@ import math
10
10
  import warnings
11
11
  from collections import defaultdict
12
12
  from datetime import datetime
13
- from typing import List, Union, Optional, Dict, ClassVar, cast
13
+ from typing import List, Union, Optional, Dict, cast
14
14
 
15
15
  import jsonschema.validators
16
+
16
17
  import mmif
17
18
  from mmif import ThingTypesBase
18
19
  from mmif.vocabulary import AnnotationTypes, DocumentTypes
19
-
20
20
  from .annotation import Annotation, Document
21
21
  from .model import MmifObject, DataList
22
22
  from .view import View
@@ -32,9 +32,6 @@ class Mmif(MmifObject):
32
32
  :param validate: whether to validate the data against the MMIF JSON schema.
33
33
  """
34
34
 
35
- view_prefix: ClassVar[str] = 'v_'
36
- id_delimiter: ClassVar[str] = ':'
37
-
38
35
  def __init__(self, mmif_obj: Optional[Union[bytes, str, dict]] = None, *, validate: bool = True) -> None:
39
36
  self.metadata: MmifMetadata = MmifMetadata()
40
37
  self.documents: DocumentsList = DocumentsList()
@@ -362,8 +359,8 @@ class Mmif(MmifObject):
362
359
  :return: a reference to the corresponding document, if it exists
363
360
  :raises KeyError: if there is no corresponding document
364
361
  """
365
- if Mmif.id_delimiter in doc_id:
366
- vid, did = doc_id.split(Mmif.id_delimiter)
362
+ if self.id_delimiter in doc_id:
363
+ vid, did = doc_id.split(self.id_delimiter)
367
364
  view = self[vid]
368
365
  if isinstance(view, View):
369
366
  return view.get_document_by_id(did)
@@ -410,8 +407,8 @@ class Mmif(MmifObject):
410
407
  aligned_types = set()
411
408
  for ann_id in [alignment['target'], alignment['source']]:
412
409
  ann_id = cast(str, ann_id)
413
- if Mmif.id_delimiter in ann_id:
414
- view_id, ann_id = ann_id.split(Mmif.id_delimiter)
410
+ if self.id_delimiter in ann_id:
411
+ view_id, ann_id = ann_id.split(self.id_delimiter)
415
412
  aligned_type = cast(Annotation, self[view_id][ann_id]).at_type
416
413
  else:
417
414
  aligned_type = cast(Annotation, alignment_view[ann_id]).at_type
@@ -438,8 +435,8 @@ class Mmif(MmifObject):
438
435
  except StopIteration:
439
436
  # means search failed by the full doc_id string,
440
437
  # now try trimming the view_id from the string and re-do the search
441
- if Mmif.id_delimiter in doc_id:
442
- vid, did = doc_id.split(Mmif.id_delimiter)
438
+ if self.id_delimiter in doc_id:
439
+ vid, did = doc_id.split(self.id_delimiter)
443
440
  if view.id == vid:
444
441
  annotations = view.get_annotations(document=did)
445
442
  try:
@@ -450,6 +447,16 @@ class Mmif(MmifObject):
450
447
  pass
451
448
  return views
452
449
 
450
+ def get_all_views_with_error(self) -> List[View]:
451
+ """
452
+ Returns the list of all views in the MMIF that have errors.
453
+
454
+ :return: the list of views that contain errors but no annotations
455
+ """
456
+ return [v for v in self.views if v.has_error()]
457
+
458
+ get_views_with_error = get_all_views_with_error
459
+
453
460
  def get_all_views_contain(self, at_types: Union[ThingTypesBase, str, List[Union[str, ThingTypesBase]]]) -> List[View]:
454
461
  """
455
462
  Returns the list of all views in the MMIF if given types
@@ -464,11 +471,27 @@ class Mmif(MmifObject):
464
471
  else:
465
472
  return [view for view in self.views if at_types in view.metadata.contains]
466
473
 
467
- def get_views_contain(self, at_types: Union[ThingTypesBase, str, List[Union[str, ThingTypesBase]]]) -> List[View]:
474
+ get_views_contain = get_all_views_contain
475
+
476
+ def get_view_with_error(self) -> Optional[View]:
468
477
  """
469
- An alias to `get_all_views_contain` method.
478
+ Returns the last view appended that contains an error.
479
+
480
+ :return: the view, or None if no error is found
481
+ """
482
+ for view in reversed(self.views):
483
+ if view.has_error():
484
+ return view
485
+ return None
486
+
487
+ def get_last_error(self) -> Optional[str]:
488
+ """
489
+ Returns the last error message found in the views.
490
+
491
+ :return: the error message in human-readable format, or None if no error is found
470
492
  """
471
- return self.get_all_views_contain(at_types)
493
+ v = self.get_view_with_error()
494
+ return v.get_error() if v is not None else None
472
495
 
473
496
  def get_view_contains(self, at_types: Union[ThingTypesBase, str, List[Union[str, ThingTypesBase]]]) -> Optional[View]:
474
497
  """
@@ -506,8 +529,8 @@ class Mmif(MmifObject):
506
529
  elif 'targets' in props:
507
530
 
508
531
  def get_target_ann(cur_ann, target_id):
509
- if Mmif.id_delimiter not in target_id:
510
- target_id = Mmif.id_delimiter.join((cur_ann.parent, target_id))
532
+ if self.id_delimiter not in target_id:
533
+ target_id = self.id_delimiter.join((cur_ann.parent, target_id))
511
534
  return self.__getitem__(target_id)
512
535
 
513
536
  if not targets_sorted:
@@ -549,7 +572,7 @@ class Mmif(MmifObject):
549
572
  """
550
573
  if item in self._named_attributes():
551
574
  return self.__dict__[item]
552
- split_attempt = item.split(Mmif.id_delimiter)
575
+ split_attempt = item.split(self.id_delimiter)
553
576
 
554
577
  document_result = self.documents.get(split_attempt[0])
555
578
  view_result = self.views.get(split_attempt[0])
@@ -14,7 +14,7 @@ for the different components of MMIF is added in the subclasses.
14
14
 
15
15
  import json
16
16
  from datetime import datetime
17
- from typing import Union, Any, Dict, Optional, TypeVar, Generic, Generator, Iterator, Type, Set
17
+ from typing import Union, Any, Dict, Optional, TypeVar, Generic, Generator, Iterator, Type, Set, ClassVar
18
18
 
19
19
  from deepdiff import DeepDiff
20
20
 
@@ -44,41 +44,48 @@ class MmifObject(object):
44
44
 
45
45
  1. _unnamed_attributes:
46
46
  Only can be either None or an empty dictionary. If it's set to None,
47
- it means the class won't take any ``Additional Attributes`` in the JSON
48
- schema sense. If it's a dict, users can throw any k-v pairs to the
49
- class, EXCEPT for the reserved two key names.
47
+ it means the class won't take any ``Additional Attributes`` in the
48
+ JSON schema sense. If it's an empty dict, users can throw any k-v
49
+ pairs to the class, as long as the key is not a "reserved" name,
50
+ and those additional attributes will be stored in this dict while
51
+ in memory.
50
52
  2. _attribute_classes:
51
53
  This is a dict from a key name to a specific python class to use for
52
54
  deserialize the value. Note that a key name in this dict does NOT
53
55
  have to be a *named* attribute, but is recommended to be one.
54
56
  3. _required_attributes:
55
- This is a simple list of names of attributes that are required in the object.
56
- When serialize, an object will skip its *empty* (e.g. zero-length, or None)
57
- attributes unless they are in this list. Otherwise, the serialized JSON
58
- string would have empty representations (e.g. ``""``, ``[]``).
57
+ This is a simple list of names of attributes that are required in
58
+ the object. When serialize, an object will skip its *empty* (e.g.
59
+ zero-length, or None) attributes unless they are in this list.
60
+ Otherwise, the serialized JSON string would have empty
61
+ representations (e.g. ``""``, ``[]``).
59
62
  4. _exclude_from_diff:
60
- This is a simple list of names of attributes that should be excluded from
61
- the diff calculation in ``__eq__``.
63
+ This is a simple list of names of attributes that should be excluded
64
+ from the diff calculation in ``__eq__``.
62
65
 
63
66
  # TODO (krim @ 8/17/20): this dict is however, a duplicate with the type hints in the class definition.
64
- Maybe there is a better way to utilize type hints (e.g. getting them as a programmatically), but for now
65
- developers should be careful to add types to hints as well as to this dict.
67
+ Maybe there is a better way to utilize type hints (e.g. getting them
68
+ as a programmatically), but for now developers should be careful to
69
+ add types to hints as well as to this dict.
66
70
 
67
71
  Also note that those special attributes MUST be set in the __init__()
68
72
  before calling super method, otherwise deserialization will not work.
69
73
 
70
74
  And also, a subclass that has one or more *named* attributes, it must
71
- set those attributes in the __init__() before calling super method. When
72
- serializing a MmifObject, all *empty* attributes will be ignored, so for
73
- optional named attributes, you must leave the values empty (len == 0), but
74
- NOT None. Any None-valued named attributes will cause issues with current
75
- implementation.
75
+ set those attributes in the __init__() before calling super method.
76
+ When serializing a MmifObject, all *empty* attributes will be ignored,
77
+ so for optional named attributes, you must leave the values empty
78
+ (len == 0), but NOT None. Any None-valued named attributes will cause
79
+ issues with current implementation.
76
80
 
77
81
  :param mmif_obj: JSON string or `dict` to initialize an object.
78
82
  If not given, an empty object will be initialized, sometimes with
79
83
  an ID value automatically generated, based on its parent object.
80
84
  """
81
85
 
86
+ view_prefix: ClassVar[str] = 'v_'
87
+ id_delimiter: ClassVar[str] = ':'
88
+
82
89
  # these are the reserved names that cannot be used as attribute names, and
83
90
  # they won't be serialized
84
91
  reserved_names: Set[str] = {
@@ -269,8 +276,8 @@ class MmifObject(object):
269
276
  """
270
277
  Returns number of attributes that are not *empty*.
271
278
  """
272
- return sum([named in self and not self.is_empty(self[named]) for named in self._named_attributes()]) \
273
- + (len(self._unnamed_attributes) if self._unnamed_attributes else 0)
279
+ return (sum([named in self and not self.is_empty(self[named]) for named in self._named_attributes()])
280
+ + (len(self._unnamed_attributes) if self._unnamed_attributes else 0))
274
281
 
275
282
  def __setitem__(self, key, value) -> None:
276
283
  if key in self.reserved_names:
@@ -5,6 +5,7 @@ as a live Python object.
5
5
  In MMIF, views are created by apps in a pipeline that are annotating
6
6
  data that was previously present in the MMIF file.
7
7
  """
8
+ import json
8
9
  from datetime import datetime
9
10
  from typing import Dict, Union, Optional, Generator, List, cast
10
11
 
@@ -230,6 +231,23 @@ class View(MmifObject):
230
231
  def set_error(self, err_message: str, err_trace: str) -> None:
231
232
  self.metadata.set_error(err_message, err_trace)
232
233
  self.annotations.empty()
234
+
235
+ def get_error(self) -> Optional[str]:
236
+ """
237
+ Get the "text" representation of the error occurred during
238
+ processing. Text representation is supposed to be human-readable.
239
+ When ths view does not have any error, returns None.
240
+ """
241
+ if self.has_error():
242
+ return self.metadata.get_error_as_text()
243
+ else:
244
+ return None
245
+
246
+ def has_error(self) -> bool:
247
+ return self.metadata.has_error()
248
+
249
+ def has_warnings(self):
250
+ return self.metadata.has_warnings()
233
251
 
234
252
 
235
253
  class ViewMetadata(MmifObject):
@@ -267,7 +285,24 @@ class ViewMetadata(MmifObject):
267
285
  if not (self.contains.items() or self.error or self.warnings):
268
286
  serialized['contains'] = {}
269
287
  return serialized
270
-
288
+
289
+ def has_error(self) -> bool:
290
+ return len(self.error) > 0
291
+
292
+ def has_warnings(self):
293
+ return len(self.warnings) > 0
294
+
295
+ def get_error_as_text(self) -> str:
296
+ if self.has_error():
297
+ if isinstance(self.error, ErrorDict):
298
+ return str(self.error)
299
+ elif isinstance(self.error, dict):
300
+ return f"Error: {json.dumps(self.error, indent=2)}"
301
+ else:
302
+ return f"Error (unknown error format): {self.error}"
303
+ else:
304
+ raise KeyError(f"No error found")
305
+
271
306
  def new_contain(self, at_type: Union[str, ThingTypesBase], **contains_metadata) -> Optional['Contain']:
272
307
  """
273
308
  Adds a new element to the ``contains`` dictionary.
@@ -346,6 +381,9 @@ class ErrorDict(MmifObject):
346
381
  self.message: str = ''
347
382
  self.stackTrace: str = ''
348
383
  super().__init__(error_obj)
384
+
385
+ def __str__(self):
386
+ return f"({self.message})\n\n{self.stackTrace}"
349
387
 
350
388
 
351
389
  class Contain(DataDict[str, str]):
@@ -68,17 +68,17 @@ def convert(t: Union[int, float, str], in_unit: str, out_unit: str, fps: float)
68
68
  elif out_unit == 'frame':
69
69
  # ms>f
70
70
  if 'millisecond' == in_unit:
71
- return int(t / 1000 * fps)
71
+ return round(t / 1000 * fps)
72
72
  # s>f
73
73
  elif 'second' == in_unit:
74
- return int(t * fps)
74
+ return round(t * fps)
75
75
  # s>(ms or i)
76
76
  elif in_unit == 'second':
77
- return int(t * 1000) if out_unit == 'millisecond' else _second_to_isoformat(t)
77
+ return round(t * 1000) if out_unit == 'millisecond' else _second_to_isoformat(t)
78
78
  # ms>(s or i)
79
79
  elif in_unit == 'millisecond':
80
80
  return t / 1000 if out_unit == 'second' else _millisecond_to_isoformat(t)
81
81
  # f>ms, f>s
82
82
  else:
83
- return (t / fps) if out_unit == 'second' else (round(t / fps, 3) * 1000) # pytype: disable=bad-return-type
83
+ return (t / fps) if out_unit == 'second' else round(round(t / fps, 3) * 1000) # pytype: disable=bad-return-type
84
84
 
@@ -1,6 +1,7 @@
1
1
  import importlib
2
2
  import warnings
3
3
  from typing import List, Union, Tuple
4
+ import math
4
5
 
5
6
  import mmif
6
7
  from mmif import Annotation, Document, Mmif
@@ -124,22 +125,23 @@ def extract_mid_frame(mmif: Mmif, time_frame: Annotation, as_PIL: bool = False):
124
125
  return extract_frames_as_images(vd, [get_mid_framenum(mmif, time_frame)], as_PIL=as_PIL)[0]
125
126
 
126
127
 
127
- def sample_frames(start_frame: int, end_frame: int, sample_ratio: int = 1) -> List[int]:
128
+ def sample_frames(start_frame: int, end_frame: int, sample_rate: float = 1) -> List[int]:
128
129
  """
129
130
  Helper function to sample frames from a time interval.
130
- Can also be used as a "cutoff" function when used with ``start_frame==0`` and ``sample_ratio==1``.
131
+ Can also be used as a "cutoff" function when used with ``start_frame==0`` and ``sample_rate==1``.
131
132
 
132
133
  :param start_frame: start frame of the interval
133
134
  :param end_frame: end frame of the interval
134
- :param sample_ratio: sample ratio (or step) to configure how often to take a frame, default is 1, meaning all consecutive frames are sampled
135
+ :param sample_rate: sampling rate (or step) to configure how often to take a frame, default is 1, meaning all consecutive frames are sampled
135
136
 
136
137
  """
137
- sample_ratio = int(sample_ratio)
138
- if sample_ratio < 1:
139
- raise ValueError(f"Sample ratio must be greater than 1, but got {sample_ratio}")
138
+ if sample_rate < 1:
139
+ raise ValueError(f"Sample rate must be greater than 1, but got {sample_rate}")
140
140
  frame_nums: List[int] = []
141
- for i in range(start_frame, end_frame, sample_ratio):
142
- frame_nums.append(i)
141
+ cur_f = start_frame
142
+ while cur_f < end_frame:
143
+ frame_nums.append(math.ceil(cur_f))
144
+ cur_f += sample_rate
143
145
  return frame_nums
144
146
 
145
147
 
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.14"
2
+ __specver__ = "1.0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mmif-python
3
- Version: 1.0.12
3
+ Version: 1.0.14
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
@@ -1 +0,0 @@
1
- 1.0.12
@@ -1,2 +0,0 @@
1
- __version__ = "1.0.12"
2
- __specver__ = "1.0.4"
File without changes
File without changes
File without changes
File without changes
File without changes