Skip to content

Commit d89c180

Browse files
authored
Merge pull request #65 from gdcc/fix-multiple-compounds-update
Fix multiple compounds update
2 parents ff52a42 + bae06b7 commit d89c180

File tree

6 files changed

+85
-8
lines changed

6 files changed

+85
-8
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,9 @@ poetry.lock
170170

171171
# Zed editor
172172
pyrightconfig.json
173+
174+
# Files generated by tests
175+
minimal_upload_other_license.json
176+
minimal_upload.json
177+
test_file.txt
178+
toydataset.schema.json

easyDataverse/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class DataverseBase(BaseModel):
2828

2929
# ! Overloads
3030
def __setattr__(self, name: str, value: Any) -> None:
31-
if name in self.model_fields:
31+
if name in self.__class__.model_fields:
3232
self._changed.add(name)
3333

3434
return super().__setattr__(name, value)
@@ -124,7 +124,7 @@ def dataverse_dict(self) -> Dict:
124124
# Get properties and init json_obj
125125
json_obj = {}
126126

127-
for attr, field in self.model_fields.items():
127+
for attr, field in self.__class__.model_fields.items():
128128
if any(name in attr for name in ["add_", "_metadatablock_name"]):
129129
# Only necessary for blind fetch
130130
continue
@@ -189,7 +189,7 @@ def extract_changed(self) -> List[Dict]:
189189
changed_fields = []
190190

191191
for name in self._changed:
192-
field = self.model_fields[name]
192+
field = self.__class__.model_fields[name]
193193

194194
if self._is_compound(field) and self._is_multiple(field):
195195
value = self._process_multiple_compound(getattr(self, name))
@@ -206,7 +206,7 @@ def extract_changed(self) -> List[Dict]:
206206
def _add_changed_multiples(self):
207207
"""Checks whether a compound has multiple changed fields"""
208208

209-
for name, field in self.model_fields.items():
209+
for name, field in self.__class__.model_fields.items():
210210
if not self._is_compound(field):
211211
continue
212212
if not self._is_multiple(field):

easyDataverse/classgen.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,10 @@ def generate_add_function(subclass, attribute, name):
214214
"""
215215

216216
def add_fun_template(self, **kwargs):
217-
getattr(self, attribute).append(subclass(**kwargs))
217+
self._changed.add(attribute)
218+
obj = subclass(**kwargs)
219+
obj._changed.update(kwargs.keys())
220+
getattr(self, attribute).append(obj)
218221

219222
signature = create_function_signature(subclass)
220223
new_func = forge.sign(forge.self, *signature)(
@@ -245,6 +248,8 @@ def create_function_signature(subclass) -> List:
245248
"""
246249
signature = []
247250
for name, dtype in subclass.__annotations__.items():
251+
if name == "_changed":
252+
continue
248253
sig_params = {"name": name, "type": dtype, "interface_name": name}
249254

250255
default = subclass.model_fields[name].default

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "easyDataverse"
3-
version = "0.4.4"
3+
version = "0.4.5"
44
description = "Lightweight Dataverse interface in Python to upload, download and update datasets found in Dataverse instances."
55
authors = ["Jan Range <jan.range@simtech.uni-stuttgart.de>"]
66
license = "MIT License"

tests/integration/test_dataset_update.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,69 @@ def test_dataset_update(
6363
"The updated dataset title does not match the expected title."
6464
)
6565

66+
@pytest.mark.integration
67+
def test_dataset_update_with_multiple_fields(
68+
self,
69+
credentials,
70+
):
71+
# Arrange
72+
base_url, api_token = credentials
73+
dataverse = Dataverse(
74+
server_url=base_url,
75+
api_token=api_token,
76+
)
77+
78+
# Create a dataset
79+
dataset = dataverse.create_dataset()
80+
dataset.citation.title = "My dataset"
81+
dataset.citation.subject = ["Other"]
82+
dataset.citation.add_author(name="John Doe")
83+
dataset.citation.add_ds_description(
84+
value="This is a description of the dataset",
85+
date="2024",
86+
)
87+
dataset.citation.add_dataset_contact(
88+
name="John Doe",
89+
email="john@doe.com",
90+
)
91+
92+
pid = dataset.upload("Root")
93+
94+
# Act
95+
# Re-fetch the dataset and add other ID
96+
dataset = dataverse.load_dataset(pid)
97+
dataset.citation.add_other_id(agency="DOI", value="10.5072/easy-dataverse")
98+
dataset.update()
99+
100+
# Re-fetch the dataset to verify the update
101+
url = (
102+
f"{base_url}/api/datasets/:persistentId/versions/:draft?persistentId={pid}"
103+
)
104+
105+
response = httpx.get(
106+
url,
107+
headers={"X-Dataverse-key": api_token},
108+
)
109+
110+
response.raise_for_status()
111+
updated_dataset = response.json()
112+
other_id_field = next(
113+
filter(
114+
lambda x: x["typeName"] == "otherId",
115+
updated_dataset["data"]["metadataBlocks"]["citation"]["fields"],
116+
),
117+
None,
118+
)
119+
120+
# Assert
121+
assert other_id_field is not None, "Other ID field should be present"
122+
assert len(other_id_field["value"]) > 0, "Other ID field should have values"
123+
assert any(
124+
item["otherIdAgency"]["value"] == "DOI"
125+
and item["otherIdValue"]["value"] == "10.5072/easy-dataverse"
126+
for item in other_id_field["value"]
127+
), "The DOI other ID should be present in the updated dataset"
128+
66129
@staticmethod
67130
def sort_citation(dataset: Dict):
68131
citation = dataset["datasetVersion"]["metadataBlocks"]["citation"]

tests/unit/test_connect.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from enum import Enum
2-
from typing import List, Optional, Union, get_args
2+
from typing import List, Optional, Set, Union, get_args
33

44
import pytest
5-
from pydantic import BaseModel, Field
5+
from pydantic import BaseModel, Field, PrivateAttr
66

77
from easyDataverse.classgen import (
88
camel_to_snake,
@@ -451,9 +451,11 @@ class TestClass(BaseModel):
451451
name: str
452452
value: int = 42
453453
optional: Optional[str] = None
454+
_changed: Set = PrivateAttr(default_factory=set)
454455

455456
class ParentClass(BaseModel):
456457
to_add_to: List[TestClass] = []
458+
_changed: Set = PrivateAttr(default_factory=set)
457459

458460
# Act
459461
result = generate_add_function(
@@ -480,6 +482,7 @@ class ParentClass(BaseModel):
480482
"name": str,
481483
"value": int,
482484
"optional": Optional[str],
485+
"_changed": Set,
483486
}
484487

485488
expected_object = TestClass(

0 commit comments

Comments
 (0)