|
12 | 12 | from rdflib import Graph, Namespace, URIRef |
13 | 13 | from structlog._config import BoundLoggerLazyProxy |
14 | 14 |
|
15 | | -from datetime import datetime |
| 15 | +import uuid |
16 | 16 |
|
17 | 17 | from bam_masterdata.metadata._maps import ( |
18 | 18 | COLLECTION_TYPE_MAP, |
@@ -570,6 +570,45 @@ def create_type(openbis: "Openbis", defs: ObjectTypeDef): |
570 | 570 | ) |
571 | 571 |
|
572 | 572 |
|
| 573 | +UUID_SUFFIX_LENGTH = 8 |
| 574 | + |
| 575 | + |
| 576 | +def generate_object_id(object_type: ObjectType) -> str: |
| 577 | + """ |
| 578 | + Generate a unique identifier for an object type based on its definition. |
| 579 | +
|
| 580 | + Args: |
| 581 | + object_type (ObjectType): The object type for which to generate the identifier. |
| 582 | +
|
| 583 | + Returns: |
| 584 | + str: A unique identifier string for the object type, combining a prefix from the definition |
| 585 | + and a random 8-characters-long UUID suffix. |
| 586 | + """ |
| 587 | + try: |
| 588 | + prefix = object_type.defs.generated_code_prefix # string prefix |
| 589 | + # UUID suffix with a defined length |
| 590 | + suffix = uuid.uuid4().hex[:UUID_SUFFIX_LENGTH] |
| 591 | + except AttributeError: |
| 592 | + # fallback if the object type does not have a generated_code_prefix |
| 593 | + prefix = "" |
| 594 | + suffix = str(uuid.uuid4()) |
| 595 | + return f"{prefix}{suffix}" |
| 596 | + |
| 597 | + |
| 598 | +def generate_object_relationship_id(parent_id: str, child_id: str) -> str: |
| 599 | + """ |
| 600 | + Generate a unique identifier for a relationship between two object types as their IDs concatenated. |
| 601 | +
|
| 602 | + Args: |
| 603 | + parent_id (str): The unique identifier of the parent object type. |
| 604 | + child_id (str): The unique identifier of the child object type. |
| 605 | +
|
| 606 | + Returns: |
| 607 | + str: A unique identifier string for the relationship, combining the parent and child IDs. |
| 608 | + """ |
| 609 | + return f"{parent_id}>>{child_id}" |
| 610 | + |
| 611 | + |
573 | 612 | class VocabularyType(BaseEntity): |
574 | 613 | """ |
575 | 614 | Base class used to define vocabulary types. All vocabulary types must inherit from this class. The |
@@ -642,6 +681,34 @@ def create_type(openbis: "Openbis", defs: VocabularyTypeDef, terms: list): |
642 | 681 |
|
643 | 682 |
|
644 | 683 | class CollectionType(ObjectType): |
| 684 | + model_config = ConfigDict( |
| 685 | + ignored_types=( |
| 686 | + ObjectTypeDef, |
| 687 | + ObjectType, |
| 688 | + CollectionTypeDef, |
| 689 | + PropertyTypeAssignment, |
| 690 | + ) |
| 691 | + ) |
| 692 | + |
| 693 | + attached_objects: dict[str, ObjectType] = Field( |
| 694 | + default={}, |
| 695 | + exclude=True, |
| 696 | + description=""" |
| 697 | + Dictionary containing the object types attached to the collection type. |
| 698 | + The keys are object unique identifiers and the values are the ObjectType instances. |
| 699 | + """, |
| 700 | + ) |
| 701 | + |
| 702 | + relationships: dict[str, tuple[str, str]] = Field( |
| 703 | + default={}, |
| 704 | + exclude=True, |
| 705 | + description=""" |
| 706 | + Dictionary containing the relationships between the objects attached to the collection type. |
| 707 | + The keys are relationships unique identifiers, the values are the object unique identifiers as a |
| 708 | + tuple, and the order is always (parent_id, child_id). |
| 709 | + """, |
| 710 | + ) |
| 711 | + |
645 | 712 | @property |
646 | 713 | def cls_name(self) -> str: |
647 | 714 | """ |
@@ -684,6 +751,85 @@ def create_type(openbis: "Openbis", defs: CollectionTypeDef): |
684 | 751 | create_type=create_type, |
685 | 752 | ) |
686 | 753 |
|
| 754 | + def add(self, object_type: ObjectType) -> str: |
| 755 | + """ |
| 756 | + Add an object type to the collection type. |
| 757 | +
|
| 758 | + Args: |
| 759 | + object_type (ObjectType): The object type to add to the collection type. |
| 760 | +
|
| 761 | + Returns: |
| 762 | + str: The unique identifier of the object type assigned in openBIS. |
| 763 | + """ |
| 764 | + if not isinstance(object_type, ObjectType): |
| 765 | + raise TypeError( |
| 766 | + f"Expected an ObjectType instance, got `{type(object_type).__name__}`" |
| 767 | + ) |
| 768 | + object_id = generate_object_id(object_type) |
| 769 | + self.attached_objects[object_id] = object_type |
| 770 | + return object_id |
| 771 | + |
| 772 | + def remove(self, object_id: str = "") -> None: |
| 773 | + """ |
| 774 | + Remove an object type from the collection type by its unique identifier. |
| 775 | +
|
| 776 | + Args: |
| 777 | + object_id (str, optional): The ID of the object type to be removed from the collection. |
| 778 | + """ |
| 779 | + if not object_id: |
| 780 | + raise ValueError( |
| 781 | + "You must provide an `object_id` to remove the object type from the collection." |
| 782 | + ) |
| 783 | + if object_id not in self.attached_objects.keys(): |
| 784 | + raise ValueError( |
| 785 | + f"Object with ID '{object_id}' does not exist in the collection." |
| 786 | + ) |
| 787 | + del self.attached_objects[object_id] |
| 788 | + |
| 789 | + def add_relationship(self, parent_id: str, child_id: str) -> str: |
| 790 | + """ |
| 791 | + Add a relationship between two object types in the collection type. |
| 792 | +
|
| 793 | + Args: |
| 794 | + parent_id (str): The unique identifier of the parent object type. |
| 795 | + child_id (str): The unique identifier of the child object type. |
| 796 | +
|
| 797 | + Returns: |
| 798 | + str: The unique identifier of the relationship created, which is a concatenation of the parent |
| 799 | + and child IDs. |
| 800 | + """ |
| 801 | + if not parent_id or not child_id: |
| 802 | + raise ValueError( |
| 803 | + "Both `parent_id` and `child_id` must be provided to add a relationship." |
| 804 | + ) |
| 805 | + if ( |
| 806 | + parent_id not in self.attached_objects.keys() |
| 807 | + or child_id not in self.attached_objects.keys() |
| 808 | + ): |
| 809 | + raise ValueError( |
| 810 | + "Both `parent_id` and `child_id` must be assigned to objects attached to the collection." |
| 811 | + ) |
| 812 | + relationship_id = generate_object_relationship_id(parent_id, child_id) |
| 813 | + self.relationships[relationship_id] = (parent_id, child_id) |
| 814 | + return relationship_id |
| 815 | + |
| 816 | + def remove_relationship(self, relationship_id: str) -> None: |
| 817 | + """ |
| 818 | + Remove a relationship from the collection type. |
| 819 | +
|
| 820 | + Args: |
| 821 | + relationship_id (str): The unique identifier of the relationship to remove. |
| 822 | + """ |
| 823 | + if not relationship_id: |
| 824 | + raise ValueError( |
| 825 | + "You must provide a `relationship_id` to remove the relationship from the collection type." |
| 826 | + ) |
| 827 | + if relationship_id not in self.relationships.keys(): |
| 828 | + raise ValueError( |
| 829 | + f"Relationship with ID '{relationship_id}' does not exist in the collection type." |
| 830 | + ) |
| 831 | + del self.relationships[relationship_id] |
| 832 | + |
687 | 833 |
|
688 | 834 | class DatasetType(ObjectType): |
689 | 835 | @property |
|
0 commit comments