From fe46e3ad9b45ca94cfab6b16268c0c71078ce405 Mon Sep 17 00:00:00 2001 From: glrs <5999366+glrs@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:26:50 +0100 Subject: [PATCH 1/2] Add user_info attribute and set_user_info method to YggdrasilDocument --- lib/couchdb/document.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/couchdb/document.py b/lib/couchdb/document.py index 136b1ca..6d4dace 100644 --- a/lib/couchdb/document.py +++ b/lib/couchdb/document.py @@ -45,6 +45,9 @@ def from_dict(cls, data: Dict[str, Any]) -> "YggdrasilDocument": # Samples instance.samples = data.get("samples", []) + # User info + instance.user_info = data.get("user_info", {}) + # Delivery info instance.delivery_info = data.get("delivery_info", {}) if "delivery_results" not in instance.delivery_info: @@ -78,6 +81,7 @@ def __init__(self, project_id: str, projects_reference: str, method: str) -> Non self.samples: List[Dict[str, Any]] = [] self.delivery_info: Dict[str, Any] = {"delivery_results": []} self.ngi_report: List[Dict[str, Any]] = [] + self.user_info: Dict[str, Dict[str, Optional[str]]] = {} def to_dict(self) -> Dict[str, Any]: """Converts the YggdrasilDocument to a dictionary. @@ -96,8 +100,37 @@ def to_dict(self) -> Dict[str, Any]: "samples": self.samples, "delivery_info": self.delivery_info, "ngi_report": self.ngi_report, + "user_info": self.user_info, } + # --------------------------- + # USER INFO + # --------------------------- + + def set_user_info(self, updated_info: Dict[str, Dict[str, Optional[str]]]) -> None: + """ + Updates self.user_info with the nested dictionary provided. + + Example updated_info: + { + "owner": { + "email": "owner@host.org", + "name": "Owner Name" + }, + "pi": { + "email": "pi@host.org", + "name": "PI Name" + } + } + """ + for role, sub_dict in updated_info.items(): + if role not in self.user_info: + # If the doc didn't have that role yet, create a blank dict + self.user_info[role] = {} + # Copy keys like "email", "name" + for key, val in sub_dict.items(): + self.user_info[role][key] = val or "" + # ------------------------------------------------------------------------ # SAMPLES # ------------------------------------------------------------------------ From 6f9f3e487acc52eb1b318d96e41045f59807a2fb Mon Sep 17 00:00:00 2001 From: glrs <5999366+glrs@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:28:13 +0100 Subject: [PATCH 2/2] Add `user_info` and `sensitive` parameters to create_project method | Implement `sync_project_metadata` method for updating project doc --- lib/couchdb/manager.py | 48 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/couchdb/manager.py b/lib/couchdb/manager.py index dae883e..49d79b5 100644 --- a/lib/couchdb/manager.py +++ b/lib/couchdb/manager.py @@ -203,7 +203,12 @@ def __init__(self) -> None: super().__init__("yggdrasil") def create_project( - self, project_id: str, projects_reference: str, method: str + self, + project_id: str, + projects_reference: str, + method: str, + user_info: Optional[Dict[str, Dict[str, Optional[str]]]] = None, + sensitive: Optional[bool] = True, ) -> YggdrasilDocument: """Creates a new project document in the database. @@ -211,6 +216,9 @@ def create_project( project_id (str): The project ID. projects_reference (str): Reference to the original project document. method (str): The library construction method. + user_info (Optional[Dict[str, Dict[str, str]]]): Nested dict of user info, + e.g. {"owner": {"email": "...", "name": "..."}, ...}. + sensitive (bool): True if data is sensitive. Defaults to True. Returns: YggdrasilDocument: The newly created project document. @@ -218,10 +226,48 @@ def create_project( new_document = YggdrasilDocument( project_id=project_id, projects_reference=projects_reference, method=method ) + + # If we have user info, populate it into new_document.user_info + # TODO: Make sure the PI and the owner are always set | Either here or upon delivery + if user_info: + new_document.user_info = user_info + + # Set sensitive flag to True by default (better safe than sorry) + new_document.delivery_info["sensitive"] = sensitive + self.save_document(new_document) logging.info(f"New project with ID '{project_id}' created successfully.") return new_document + def sync_project_metadata( + self, + project_id: str, + user_info: Dict[str, Dict[str, Optional[str]]], + is_sensitive: bool, + ) -> None: + """ + Fetches the project doc from YggdrasilDB, updates user_info & sensitive, + then saves it back. + """ + doc_dict = self.get_document_by_project_id(project_id) + if not doc_dict: + logging.warning(f"No project '{project_id}' found to update.") + return + + ygg_doc = YggdrasilDocument.from_dict(doc_dict) + + # Update user_info + ygg_doc.set_user_info(user_info) + + # Update sensitive + ygg_doc.delivery_info["sensitive"] = is_sensitive + + # Save + self.save_document(ygg_doc) + logging.info( + f"Synced project '{project_id}' with necessary metadata from projectsDB." + ) + def save_document(self, document: YggdrasilDocument) -> None: try: existing_doc = self.db.get(document._id)