|
4 | 4 | """
|
5 | 5 |
|
6 | 6 | import copy
|
| 7 | +import datetime |
7 | 8 | import logging
|
8 | 9 | from functools import lru_cache
|
9 | 10 | from pathlib import Path
|
|
13 | 14 | import numpy
|
14 | 15 | from packaging.version import InvalidVersion, Version
|
15 | 16 |
|
| 17 | +import imaspy |
16 | 18 | from imaspy.dd_zip import parse_dd_version
|
17 | 19 | from imaspy.ids_base import IDSBase
|
18 | 20 | from imaspy.ids_data_type import IDSDataType
|
@@ -397,6 +399,7 @@ def convert_ids(
|
397 | 399 | version: Optional[str],
|
398 | 400 | *,
|
399 | 401 | deepcopy: bool = False,
|
| 402 | + provenance_origin_uri: str = "", |
400 | 403 | xml_path: Optional[str] = None,
|
401 | 404 | factory: Optional[IDSFactory] = None,
|
402 | 405 | target: Optional[IDSToplevel] = None,
|
@@ -427,6 +430,9 @@ def convert_ids(
|
427 | 430 | deepcopy: When True, performs a deep copy of all data. When False (default),
|
428 | 431 | numpy arrays are not copied and the converted IDS shares the same underlying
|
429 | 432 | data buffers.
|
| 433 | + provenance_origin_uri: When nonempty, add an entry in the provenance data in |
| 434 | + ``ids_properties`` to indicate that this IDS has been converted, and it was |
| 435 | + originally stored at the given uri. |
430 | 436 | xml_path: Path to a data dictionary XML file that should be used instead of the
|
431 | 437 | released data dictionary version specified by ``version``.
|
432 | 438 | factory: Existing IDSFactory to use for as target version.
|
@@ -468,9 +474,49 @@ def convert_ids(
|
468 | 474 |
|
469 | 475 | _copy_structure(toplevel, target_ids, deepcopy, source_is_new, version_map)
|
470 | 476 | logger.info("Conversion of IDS %s finished.", ids_name)
|
| 477 | + if provenance_origin_uri: |
| 478 | + _add_provenance_entry(target_ids, toplevel._version, provenance_origin_uri) |
471 | 479 | return target_ids
|
472 | 480 |
|
473 | 481 |
|
| 482 | +def _add_provenance_entry( |
| 483 | + target_ids: IDSToplevel, source_version: str, provenance_origin_uri: str |
| 484 | +) -> None: |
| 485 | + # provenance node was added in DD 3.34.0 |
| 486 | + if not hasattr(target_ids.ids_properties, "provenance"): |
| 487 | + logger.warning( |
| 488 | + "Cannot add provenance entry for DD conversion: " |
| 489 | + "target IDS does not have a provenance property." |
| 490 | + ) |
| 491 | + return |
| 492 | + |
| 493 | + # Find the node corresponding to the whole IDS, or create one if there is none |
| 494 | + for node in target_ids.ids_properties.provenance.node: |
| 495 | + if node.path == "": |
| 496 | + break |
| 497 | + else: |
| 498 | + # No node found for the whole IDS, create a new one: |
| 499 | + curlen = len(target_ids.ids_properties.provenance.node) |
| 500 | + target_ids.ids_properties.provenance.node.resize(curlen + 1, keep=True) |
| 501 | + node = target_ids.ids_properties.provenance.node[-1] |
| 502 | + |
| 503 | + # Populate the node |
| 504 | + source_txt = ( |
| 505 | + f"{provenance_origin_uri}; " |
| 506 | + f"This IDS has been converted from DD {source_version} to " |
| 507 | + f"DD {target_ids._dd_version} by IMASPy {imaspy.__version__}." |
| 508 | + ) |
| 509 | + if hasattr(node, "reference"): |
| 510 | + # DD version after IMAS-5304 |
| 511 | + node.reference.resize(len(node.reference) + 1, keep=True) |
| 512 | + node.reference[-1].name = source_txt |
| 513 | + timestamp = datetime.datetime.now(datetime.UTC).isoformat(timespec="seconds") |
| 514 | + node.reference[-1].time = timestamp.replace("+00:00", "Z") |
| 515 | + else: |
| 516 | + # DD before IMAS-5304 (between 3.34.0 and 3.41.0) |
| 517 | + node.sources.append(source_txt) # sources is a STR_1D (=list of strings) |
| 518 | + |
| 519 | + |
474 | 520 | def _copy_structure(
|
475 | 521 | source: IDSStructure,
|
476 | 522 | target: IDSStructure,
|
|
0 commit comments