|
24 | 24 | __all__ = ['Mmif']
|
25 | 25 |
|
26 | 26 |
|
| 27 | +class MmifMetadata(MmifObject): |
| 28 | + """ |
| 29 | + Basic MmifObject class to contain the top-level metadata of a MMIF file. |
| 30 | +
|
| 31 | + :param metadata_obj: the JSON data |
| 32 | + """ |
| 33 | + |
| 34 | + def __init__(self, metadata_obj: Optional[Union[bytes, str, dict]] = None) -> None: |
| 35 | + # TODO (krim @ 10/7/20): there could be a better name and a better way to give a value to this |
| 36 | + self.mmif: str = f"http://mmif.clams.ai/{mmif.__specver__}" |
| 37 | + self._required_attributes = ["mmif"] |
| 38 | + super().__init__(metadata_obj) |
| 39 | + |
| 40 | + |
| 41 | +class DocumentsList(DataList[Document]): |
| 42 | + """ |
| 43 | + DocumentsList object that implements :class:`mmif.serialize.model.DataList` |
| 44 | + for :class:`mmif.serialize.document.Document`. |
| 45 | + """ |
| 46 | + _items: Dict[str, Document] |
| 47 | + |
| 48 | + def _deserialize(self, input_list: list) -> None: # pytype: disable=signature-mismatch |
| 49 | + """ |
| 50 | + Extends base ``_deserialize`` method to initialize ``items`` as a dict from |
| 51 | + document IDs to :class:`mmif.serialize.document.Document` objects. |
| 52 | +
|
| 53 | + :param input_list: the JSON data that defines the list of documents |
| 54 | + :return: None |
| 55 | + """ |
| 56 | + self._items = {item['properties']['id']: Document(item) for item in input_list} |
| 57 | + |
| 58 | + def append(self, value: Document, overwrite=False) -> None: |
| 59 | + """ |
| 60 | + Appends a document to the list. |
| 61 | +
|
| 62 | + Fails if there is already a document with the same ID |
| 63 | + in the list, unless ``overwrite`` is set to True. |
| 64 | +
|
| 65 | + :param value: the :class:`mmif.serialize.document.Document` |
| 66 | + object to add |
| 67 | + :param overwrite: if set to True, will overwrite an |
| 68 | + existing document with the same ID |
| 69 | + :raises KeyError: if ``overwrite`` is set to False and |
| 70 | + a document with the same ID exists |
| 71 | + in the list |
| 72 | + :return: None |
| 73 | + """ |
| 74 | + super()._append_with_key(value.id, value, overwrite) |
| 75 | + |
| 76 | + |
| 77 | +class ViewsList(DataList[View]): |
| 78 | + """ |
| 79 | + ViewsList object that implements :class:`mmif.serialize.model.DataList` |
| 80 | + for :class:`mmif.serialize.view.View`. |
| 81 | + """ |
| 82 | + _items: Dict[str, View] |
| 83 | + |
| 84 | + def __init__(self, mmif_obj: Optional[Union[bytes, str, list]] = None): |
| 85 | + super().__init__(mmif_obj) |
| 86 | + |
| 87 | + def _deserialize(self, input_list: list) -> None: # pytype: disable=signature-mismatch |
| 88 | + """ |
| 89 | + Extends base ``_deserialize`` method to initialize ``items`` as a dict from |
| 90 | + view IDs to :class:`mmif.serialize.view.View` objects. |
| 91 | +
|
| 92 | + :param input_list: the JSON data that defines the list of views |
| 93 | + :return: None |
| 94 | + """ |
| 95 | + if input_list: |
| 96 | + self._items = {item['id']: View(item) for item in input_list} |
| 97 | + |
| 98 | + def append(self, value: View, overwrite=False) -> None: |
| 99 | + """ |
| 100 | + Appends a view to the list. |
| 101 | +
|
| 102 | + Fails if there is already a view with the same ID |
| 103 | + in the list, unless ``overwrite`` is set to True. |
| 104 | +
|
| 105 | + :param value: the :class:`mmif.serialize.view.View` |
| 106 | + object to add |
| 107 | + :param overwrite: if set to True, will overwrite an |
| 108 | + existing view with the same ID |
| 109 | + :raises KeyError: if ``overwrite`` is set to False and |
| 110 | + a view with the same ID exists |
| 111 | + in the list |
| 112 | + :return: None |
| 113 | + """ |
| 114 | + super()._append_with_key(value.id, value, overwrite) |
| 115 | + |
| 116 | + def get_last(self) -> Optional[View]: |
| 117 | + """ |
| 118 | + Returns the last view appended to the list. |
| 119 | + """ |
| 120 | + for view in reversed(self._items.values()): |
| 121 | + if 'error' not in view.metadata and 'warning' not in view.metadata: |
| 122 | + return view |
| 123 | + |
| 124 | + |
27 | 125 | class Mmif(MmifObject):
|
28 | 126 | """
|
29 | 127 | MmifObject that represents a full MMIF file.
|
@@ -560,131 +658,41 @@ def get_end(self, annotation: Annotation) -> Union[int, float]:
|
560 | 658 | """
|
561 | 659 | return self._get_linear_anchor_point(annotation, start=False)
|
562 | 660 |
|
563 |
| - # pytype: disable=bad-return-type |
564 |
| - def __getitem__(self, item: str) -> Union[Document, View, Annotation]: |
| 661 | + def __getitem__(self, item: str) \ |
| 662 | + -> Union[Document, View, Annotation, MmifMetadata, DocumentsList, ViewsList]: |
565 | 663 | """
|
566 |
| - getitem implementation for Mmif. When nothing is found, this will raise an error |
567 |
| - rather than returning a None (although pytype doesn't think so...) |
| 664 | + getitem implementation for Mmif. This will try to find any object, given an identifier or an immediate |
| 665 | + attribute name. When nothing is found, this will raise an error rather than returning a None |
568 | 666 |
|
569 | 667 | :raises KeyError: if the item is not found or if the search results are ambiguous
|
570 |
| - :param item: the search string, a document ID, a view ID, or a view-scoped annotation ID |
| 668 | + :param item: an attribute name or an object identifier (a document ID, a view ID, or an annotation ID). When |
| 669 | + annotation ID is given as a "short" ID (without view ID prefix), the method will try to find a |
| 670 | + match from the first view, and return immediately if found. |
571 | 671 | :return: the object searched for
|
| 672 | + :raise KeyError: if the item is not found or multiple objects are found with the same ID |
572 | 673 | """
|
573 | 674 | if item in self._named_attributes():
|
574 | 675 | return self.__dict__[item]
|
575 | 676 | split_attempt = item.split(self.id_delimiter)
|
576 | 677 |
|
577 |
| - document_result = self.documents.get(split_attempt[0]) |
578 |
| - view_result = self.views.get(split_attempt[0]) |
| 678 | + found = [] |
579 | 679 |
|
580 | 680 | if len(split_attempt) == 1:
|
581 |
| - anno_result = None |
582 |
| - elif view_result: |
583 |
| - anno_result = view_result[split_attempt[1]] |
| 681 | + found.append(self.documents.get(split_attempt[0])) |
| 682 | + found.append(self.views.get(split_attempt[0])) |
| 683 | + for view in self.views: |
| 684 | + found.append(view.annotations.get(split_attempt[0])) |
| 685 | + elif len(split_attempt) == 2: |
| 686 | + v = self.get_view_by_id(split_attempt[0]) |
| 687 | + if v is not None: |
| 688 | + found.append(v.annotations.get(split_attempt[1])) |
584 | 689 | else:
|
585 | 690 | raise KeyError("Tried to subscript into a view that doesn't exist")
|
| 691 | + found = [x for x in found if x is not None] |
586 | 692 |
|
587 |
| - if view_result and document_result: |
| 693 | + if len(found) > 1: |
588 | 694 | raise KeyError("Ambiguous ID search result")
|
589 |
| - if not (view_result or document_result): |
| 695 | + elif len(found) == 0: |
590 | 696 | raise KeyError("ID not found: %s" % item)
|
591 |
| - return anno_result or view_result or document_result |
592 |
| - # pytype: enable=bad-return-type |
593 |
| - |
594 |
| - |
595 |
| -class MmifMetadata(MmifObject): |
596 |
| - """ |
597 |
| - Basic MmifObject class to contain the top-level metadata of a MMIF file. |
598 |
| -
|
599 |
| - :param metadata_obj: the JSON data |
600 |
| - """ |
601 |
| - |
602 |
| - def __init__(self, metadata_obj: Optional[Union[bytes, str, dict]] = None) -> None: |
603 |
| - # TODO (krim @ 10/7/20): there could be a better name and a better way to give a value to this |
604 |
| - self.mmif: str = f"http://mmif.clams.ai/{mmif.__specver__}" |
605 |
| - self._required_attributes = ["mmif"] |
606 |
| - super().__init__(metadata_obj) |
607 |
| - |
608 |
| - |
609 |
| -class DocumentsList(DataList[Document]): |
610 |
| - """ |
611 |
| - DocumentsList object that implements :class:`mmif.serialize.model.DataList` |
612 |
| - for :class:`mmif.serialize.document.Document`. |
613 |
| - """ |
614 |
| - _items: Dict[str, Document] |
615 |
| - |
616 |
| - def _deserialize(self, input_list: list) -> None: # pytype: disable=signature-mismatch |
617 |
| - """ |
618 |
| - Extends base ``_deserialize`` method to initialize ``items`` as a dict from |
619 |
| - document IDs to :class:`mmif.serialize.document.Document` objects. |
620 |
| -
|
621 |
| - :param input_list: the JSON data that defines the list of documents |
622 |
| - :return: None |
623 |
| - """ |
624 |
| - self._items = {item['properties']['id']: Document(item) for item in input_list} |
625 |
| - |
626 |
| - def append(self, value: Document, overwrite=False) -> None: |
627 |
| - """ |
628 |
| - Appends a document to the list. |
629 |
| -
|
630 |
| - Fails if there is already a document with the same ID |
631 |
| - in the list, unless ``overwrite`` is set to True. |
632 |
| -
|
633 |
| - :param value: the :class:`mmif.serialize.document.Document` |
634 |
| - object to add |
635 |
| - :param overwrite: if set to True, will overwrite an |
636 |
| - existing document with the same ID |
637 |
| - :raises KeyError: if ``overwrite`` is set to False and |
638 |
| - a document with the same ID exists |
639 |
| - in the list |
640 |
| - :return: None |
641 |
| - """ |
642 |
| - super()._append_with_key(value.id, value, overwrite) |
643 |
| - |
644 |
| - |
645 |
| -class ViewsList(DataList[View]): |
646 |
| - """ |
647 |
| - ViewsList object that implements :class:`mmif.serialize.model.DataList` |
648 |
| - for :class:`mmif.serialize.view.View`. |
649 |
| - """ |
650 |
| - _items: Dict[str, View] |
651 |
| - |
652 |
| - def __init__(self, mmif_obj: Optional[Union[bytes, str, list]] = None): |
653 |
| - super().__init__(mmif_obj) |
654 |
| - |
655 |
| - def _deserialize(self, input_list: list) -> None: # pytype: disable=signature-mismatch |
656 |
| - """ |
657 |
| - Extends base ``_deserialize`` method to initialize ``items`` as a dict from |
658 |
| - view IDs to :class:`mmif.serialize.view.View` objects. |
659 |
| -
|
660 |
| - :param input_list: the JSON data that defines the list of views |
661 |
| - :return: None |
662 |
| - """ |
663 |
| - if input_list: |
664 |
| - self._items = {item['id']: View(item) for item in input_list} |
665 |
| - |
666 |
| - def append(self, value: View, overwrite=False) -> None: |
667 |
| - """ |
668 |
| - Appends a view to the list. |
669 |
| -
|
670 |
| - Fails if there is already a view with the same ID |
671 |
| - in the list, unless ``overwrite`` is set to True. |
672 |
| -
|
673 |
| - :param value: the :class:`mmif.serialize.view.View` |
674 |
| - object to add |
675 |
| - :param overwrite: if set to True, will overwrite an |
676 |
| - existing view with the same ID |
677 |
| - :raises KeyError: if ``overwrite`` is set to False and |
678 |
| - a view with the same ID exists |
679 |
| - in the list |
680 |
| - :return: None |
681 |
| - """ |
682 |
| - super()._append_with_key(value.id, value, overwrite) |
683 |
| - |
684 |
| - def get_last(self) -> Optional[View]: |
685 |
| - """ |
686 |
| - Returns the last view appended to the list. |
687 |
| - """ |
688 |
| - for view in reversed(self._items.values()): |
689 |
| - if 'error' not in view.metadata and 'warning' not in view.metadata: |
690 |
| - return view |
| 697 | + else: |
| 698 | + return found[-1] |
0 commit comments