pymetadataeditor 0.3.0__tar.gz → 0.3.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymetadataeditor
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Metadata Editor client for python
5
5
  License: MIT
6
6
  Author: Gordon Blackadder
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Requires-Dist: markitdown (>=0.0.1a3,<0.0.2)
14
- Requires-Dist: metadataschemas (>=0.1.2,<0.2.0)
14
+ Requires-Dist: metadataschemas (>=0.1.3,<0.2.0)
15
15
  Requires-Dist: openai (>=1.59.4,<2.0.0)
16
16
  Requires-Dist: pandas (>=2.2.2,<3.0.0)
17
17
  Requires-Dist: pydantic (>=2.7.4,<3.0.0)
@@ -427,12 +427,21 @@ class MetadataEditor:
427
427
  else:
428
428
  metadata_type = self._templates[template_uid]["metadata_type"]
429
429
  if "class" not in self._templates[template_uid] or apply_template_rules is False:
430
- parent_schema = self._mm.metadata_class_from_name(metadata_type)
430
+ try:
431
+ parent_schema = self._mm.metadata_class_from_name(metadata_type)
432
+ except ValueError:
433
+ warnings.warn(
434
+ f"Could not find a metadata class for template type '{metadata_type}', UID '{template_uid}'. "
435
+ f"Building a class from the template without a parent schema."
436
+ )
437
+ parent_schema = SchemaBaseModel
438
+
431
439
  klass = pydantic_from_template(
432
440
  temp.template,
433
441
  parent_schema=parent_schema,
434
442
  uid=template_uid,
435
443
  name=temp.name,
444
+ metadata_type=metadata_type,
436
445
  apply_rules=apply_template_rules,
437
446
  )
438
447
  if apply_template_rules:
@@ -1043,7 +1052,7 @@ class MetadataEditor:
1043
1052
  document, geospatial, image, indicator, indicators_db, microdata, resource, script, table, video
1044
1053
  In this case we will use the default template for that metadata type.
1045
1054
  Alternatively you can pass in the UID of a template. This is required if the metadata is a dictionary
1046
- otherwise ignored.
1055
+ otherwise the UID associated with the pydantic model or the Excel file will be used.
1047
1056
 
1048
1057
  Returns:
1049
1058
  int: The ID of the newly created document metadata
@@ -1063,6 +1072,37 @@ class MetadataEditor:
1063
1072
  )
1064
1073
  return ret["id"]
1065
1074
 
1075
+ # def log_project_admin_metadata(
1076
+ # self, id: int, metadata: Union[BaseModel, Dict, str], metadata_type_or_template_uid: Optional[str] = None
1077
+ # ) -> int:
1078
+ # """Validates and logs admin metadata which can be a dictionary, a pydantic model or a path to an Excel file.
1079
+
1080
+ # Args:
1081
+ # id (int): The ID of the project to associate the metadata with.
1082
+ # metadata (dictionary or BaseModel or str): If str, it's assumed this is a path to an appropriately
1083
+ # formatted Excel file.
1084
+ # admin_metadata_template_uid (str, optional): The UID of the admin metadata template to use.
1085
+ # otherwise the UID associated with the pydantic model or the Excel file will be used.
1086
+
1087
+ # Returns:
1088
+ # int: The ID of the newly created admin metadata
1089
+ # """
1090
+ # metadata, metadata_type, uid = self._process_metadata_input(metadata, metadata_type_or_template_uid)
1091
+ # post_request_pth = "admin-metadata/data/"
1092
+ # post_json = {
1093
+ # "project_id": id,
1094
+ # "template_uid": uid,
1095
+ # "metadata": remove_empty_from_dict(metadata.model_dump(mode="json",
1096
+ # exclude_none=True,
1097
+ # exclude_unset=True)),
1098
+ # }
1099
+ # ret = self._apinterface.post_request(
1100
+ # pth=post_request_pth,
1101
+ # json=post_json,
1102
+ # id=id,
1103
+ # )
1104
+ # return ret
1105
+
1066
1106
  def update_project_log_by_id(self, id: int, new_metadata: Union[BaseModel, Dict, str]):
1067
1107
  """Updates the record of the metadata.
1068
1108
 
@@ -1252,7 +1292,6 @@ class MetadataEditor:
1252
1292
  new_index = templates.index.astype(int)
1253
1293
  except ValueError:
1254
1294
  pass
1255
- else:
1256
1295
  templates.index = new_index
1257
1296
  return templates
1258
1297
 
@@ -1272,10 +1311,23 @@ class MetadataEditor:
1272
1311
  raise KeyError(f"No result returned.\nResponse: {response}")
1273
1312
 
1274
1313
  temp = pd.Series(response["result"], name=response["result"]["name"])
1275
- self._templates[uid] = {"template": temp, "metadata_type": self._mm.standardize_metadata_name(temp.data_type)}
1276
1314
  try:
1277
- parent_schema = self._mm.metadata_class_from_name(temp.data_type)
1278
- klass = pydantic_from_template(temp.template, parent_schema=parent_schema, uid=uid, name=temp.name)
1315
+ standard_type_name = self._mm.standardize_metadata_name(temp.data_type)
1316
+ except ValueError:
1317
+ standard_type_name = temp.data_type
1318
+ self._templates[uid] = {"template": temp, "metadata_type": standard_type_name}
1319
+ try:
1320
+ try:
1321
+ parent_schema = self._mm.metadata_class_from_name(temp.data_type)
1322
+ except ValueError:
1323
+ warnings.warn(
1324
+ f"Could not find a metadata class for template type '{standard_type_name}', UID '{uid}'. "
1325
+ f"Building a class from the template without a parent schema."
1326
+ )
1327
+ parent_schema = SchemaBaseModel
1328
+ klass = pydantic_from_template(
1329
+ temp.template, parent_schema=parent_schema, uid=uid, name=temp.name, metadata_type=standard_type_name
1330
+ )
1279
1331
  self._templates[uid]["class"] = klass
1280
1332
  finally:
1281
1333
  return temp
@@ -1474,6 +1526,26 @@ class MetadataEditor:
1474
1526
  metadata["description"] = description
1475
1527
  self._apinterface.post_request("collections/update/{}", id=id, json=metadata)
1476
1528
 
1529
+ def copy_collection(self, source_id: int, target_id: int):
1530
+ """Copy projects and users from one collection to another.
1531
+
1532
+ Args:
1533
+ source_id (int): The ID of the source collection.
1534
+ target_id (int): The ID of the target collection.
1535
+
1536
+ """
1537
+ self._apinterface.post_request("collections/copy/", json={"source_id": source_id, "target_id": target_id})
1538
+
1539
+ def move_collection(self, source_id, target_id):
1540
+ """Move source collection to be a sub-collection of the target collection.
1541
+
1542
+ Args:
1543
+ source_id (int): The ID of the source collection.
1544
+ target_id (int): The ID of the target collection.
1545
+
1546
+ """
1547
+ self._apinterface.post_request("collections/move/", json={"source_id": source_id, "target_id": target_id})
1548
+
1477
1549
  def count_projects_in_collection(self, collection: int) -> int:
1478
1550
  """Count the number of projects you have access to.
1479
1551
 
@@ -617,7 +617,12 @@ def append_variables_and_data_files(model_elements, parent_schema, apply_rules:
617
617
 
618
618
 
619
619
  def pydantic_from_template(
620
- template: Dict, parent_schema: Type[SchemaBaseModel], uid: str, name: Optional[str] = None, apply_rules: bool = True
620
+ template: Dict,
621
+ parent_schema: Type[SchemaBaseModel],
622
+ uid: str,
623
+ name: Optional[str] = None,
624
+ metadata_type: Optional[str] = None,
625
+ apply_rules: bool = True,
621
626
  ) -> Type[BaseModel]:
622
627
  """Generate a Pydantic model from a given template.
623
628
 
@@ -626,6 +631,8 @@ def pydantic_from_template(
626
631
  parent_schema (Type[SchemaBaseModel]): The parent schema model to base the new model on.
627
632
  uid (str): A unique identifier for the new model.
628
633
  name (Optional[str], optional): The name of the new model. Defaults to None.
634
+ metadata_type (Optional[str], optional): The metadata type for the new model used if the parent_schema is
635
+ SchemaBaseModel. Otherwise the metadata_type of the parent_schema is used. Defaults to None.
629
636
  apply_rules (bool, optional): Whether to apply rules to the model elements. Defaults to True.
630
637
 
631
638
  Returns:
@@ -659,4 +666,12 @@ def pydantic_from_template(
659
666
  hasattr(parent_schema._metadata_type__, "default") and parent_schema._metadata_type__.default == "microdata"
660
667
  ):
661
668
  model_elements = append_variables_and_data_files(model_elements, parent_schema, apply_rules=apply_rules)
662
- return create_model_for_template(model_elements, parent_schema, name, uid=uid)
669
+ new_model = create_model_for_template(model_elements, parent_schema, name, uid=uid)
670
+ if parent_schema is SchemaBaseModel:
671
+ if metadata_type is None:
672
+ raise ValueError("metadata_type must be provided if parent_schema is SchemaBaseModel. ")
673
+ new_model._template_name__ = f"{name.replace('-', '_').replace(' ', '_')}"
674
+ new_model._template_uid__ = uid
675
+ new_model._metadata_type__ = metadata_type.replace("-", "_").replace(" ", "_")
676
+ new_model._metadata_type_version__ = "unknown"
677
+ return new_model
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pymetadataeditor"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Metadata Editor client for python"
5
5
  authors = ["Gordon Blackadder <gblackadder@worldbank.org>"]
6
6
  readme = "README.md"
@@ -15,7 +15,7 @@ pydantic = "^2.7.4"
15
15
  openai = "^1.59.4"
16
16
  tiktoken = "^0.8.0"
17
17
  markitdown = "^0.0.1a3"
18
- metadataschemas = "^0.1.2"
18
+ metadataschemas = "^0.1.3"
19
19
 
20
20
  [tool.poetry.group.dev.dependencies]
21
21
  pytest = "^8.2.2"