Skip to content

Commit 1cda0f1

Browse files
authored
fix: REMOVE_QUAL and REMOVE_REF should check all statements (#408)
fixes #406 Now REMOVE_QUAL and REMOVE_REF check all available statements with given value, instead of picking up the first. I suspect this behaviour could be useful in other commands.
1 parent 76ec8eb commit 1cda0f1

File tree

2 files changed

+153
-11
lines changed

2 files changed

+153
-11
lines changed

src/core/models.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,19 @@ def _get_statement(self, entity: dict) -> Optional[dict]:
16671667
return statement
16681668
return None
16691669

1670+
def _get_statements(self, entity: dict) -> List[dict]:
1671+
"""
1672+
Returns the statements that matches the command's value.
1673+
1674+
Returns an empty list if there is no matching statement.
1675+
"""
1676+
statements = entity["statements"].setdefault(self.prop, [])
1677+
matching = []
1678+
for i, statement in enumerate(statements):
1679+
if statement["value"] == self.statement_api_value:
1680+
matching.append(statement)
1681+
return matching
1682+
16701683
def _update_entity_statements(self, entity: dict):
16711684
"""
16721685
Modifies the entity json statements in-place.
@@ -1688,21 +1701,28 @@ def _remove_qualifier_or_reference(self, entity: dict):
16881701
"""
16891702
Removes a qualifier or a reference from the entity.
16901703
"""
1691-
statement = self._get_statement(entity)
1692-
statement = statement if statement else {}
1704+
statements = self._get_statements(entity)
16931705
found_qualifier = False
16941706
found_ref_part = False
1695-
for i, qual in enumerate(statement.get("qualifiers", [])):
1696-
if self.is_in_qualifiers(qual):
1697-
statement["qualifiers"].pop(i)
1698-
found_qualifier = True
1707+
for statement in statements:
1708+
for i, qual in enumerate(statement.get("qualifiers", [])):
1709+
if self.is_in_qualifiers(qual):
1710+
statement["qualifiers"].pop(i)
1711+
found_qualifier = True
1712+
break
1713+
if found_qualifier:
16991714
break
1700-
for i, ref in enumerate(statement.get("references", [])):
1701-
for j, part in enumerate(ref["parts"]):
1702-
if self.is_part_in_references(part):
1703-
statement["references"][i]["parts"].pop(j)
1704-
found_ref_part = True
1715+
for statement in statements:
1716+
for i, ref in enumerate(statement.get("references", [])):
1717+
for j, part in enumerate(ref["parts"]):
1718+
if self.is_part_in_references(part):
1719+
statement["references"][i]["parts"].pop(j)
1720+
found_ref_part = True
1721+
break
1722+
if found_ref_part:
17051723
break
1724+
# any better way to do this? :P
1725+
# (without refactoring into a different function...)
17061726
if found_ref_part:
17071727
break
17081728
if not found_qualifier and len(self.qualifiers_for_api()) > 0:

src/core/tests/test_entity_patching.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ def assertQualCount(self, entity: dict, property_id: str, length: int, i: int =
148148

149149
def assertRefCount(self, entity: dict, property_id: str, length: int, i: int = 0):
150150
refs = entity["statements"][property_id][i].get("references", [])
151+
# FIXME: remove empty refs for now, original code should be doing that
152+
refs = [r for r in refs if len(r["parts"]) > 0]
151153
self.assertEqual(len(refs), length)
152154

153155
def assertRefPartsCount(
@@ -244,6 +246,126 @@ def test_remove_qualifier(self):
244246
remove_nothing.update_entity_json(entity)
245247
self.assertQualCount(entity, "P65", 1)
246248

249+
def test_remove_from_available_statement(self):
250+
"""
251+
this will create a new statement, which will be in the second position
252+
then, it will try to remove the qualifier and the reference
253+
from that new stamement,
254+
both of which do not exist on the first
255+
"""
256+
text = """
257+
REMOVE_QUAL|Q12345678|P65|42|P84267|123
258+
REMOVE_REF|Q12345678|P65|42|S93|"https://pt.wikipedia.org/"
259+
260+
+Q12345678|P65|42|P84267|123|S93|"https://pt.wikipedia.org/"
261+
262+
REMOVE_QUAL|Q12345678|P65|42|P84267|123
263+
REMOVE_REF|Q12345678|P65|42|S93|"https://pt.wikipedia.org/"
264+
"""
265+
batch = self.parse(text)
266+
entity = copy.deepcopy(self.INITIAL)
267+
# -----
268+
remove_qual = batch.commands()[0]
269+
with self.assertRaises(NoQualifiers):
270+
remove_qual.update_entity_json(entity)
271+
# -----
272+
remove_ref_part = batch.commands()[1]
273+
with self.assertRaises(NoReferenceParts):
274+
remove_ref_part.update_entity_json(entity)
275+
# -----
276+
create_statement = batch.commands()[2]
277+
self.assertStmtnCount(entity, "P65", 1)
278+
create_statement.update_entity_json(entity)
279+
self.assertStmtnCount(entity, "P65", 2)
280+
# -----
281+
remove_qual = batch.commands()[3]
282+
self.assertQualCount(entity, "P65", 2, i=0)
283+
self.assertQualCount(entity, "P65", 1, i=1)
284+
# now it should try to remove from the first, then the second
285+
remove_qual.update_entity_json(entity)
286+
self.assertQualCount(entity, "P65", 2, i=0)
287+
self.assertQualCount(entity, "P65", 0, i=1)
288+
# -----
289+
remove_ref_part = batch.commands()[4]
290+
self.assertRefCount(entity, "P65", 0, i=0)
291+
self.assertRefCount(entity, "P65", 1, i=1)
292+
# now it should try to remove from the first, then the second
293+
remove_ref_part.update_entity_json(entity)
294+
self.assertRefCount(entity, "P65", 0, i=0)
295+
self.assertRefCount(entity, "P65", 0, i=1)
296+
297+
def test_remove_qualifier_from_first_statement(self):
298+
"""
299+
this will create a new statement, which will be in the second position
300+
then, it will try to remove the qualifier and the reference
301+
that is equal on both
302+
it will remove from the first, then the second, then error
303+
"""
304+
text = """
305+
+Q12345678|P31|somevalue|P18|+2025-01-15T00:00:00Z/11|S93|"https://kernel.org/"
306+
REMOVE_QUAL|Q12345678|P31|somevalue|P18|+2025-01-15T00:00:00Z/11
307+
REMOVE_QUAL|Q12345678|P31|somevalue|P18|+2025-01-15T00:00:00Z/11
308+
REMOVE_QUAL|Q12345678|P31|somevalue|P18|+2025-01-15T00:00:00Z/11
309+
REMOVE_REF|Q12345678|P31|somevalue|S93|"https://kernel.org/"
310+
REMOVE_REF|Q12345678|P31|somevalue|S93|"https://kernel.org/"
311+
REMOVE_REF|Q12345678|P31|somevalue|S93|"https://kernel.org/"
312+
"""
313+
batch = self.parse(text)
314+
entity = copy.deepcopy(self.INITIAL)
315+
create_statement = batch.commands()[0]
316+
self.assertStmtnCount(entity, "P31", 1)
317+
create_statement.update_entity_json(entity)
318+
self.assertStmtnCount(entity, "P31", 2)
319+
# -----
320+
remove_qual = batch.commands()[1]
321+
self.assertQualCount(entity, "P31", 1, i=0)
322+
self.assertQualCount(entity, "P31", 1, i=1)
323+
remove_qual.update_entity_json(entity)
324+
self.assertQualCount(entity, "P31", 0, i=0)
325+
self.assertQualCount(entity, "P31", 1, i=1)
326+
# -----
327+
remove_qual = batch.commands()[2]
328+
self.assertQualCount(entity, "P31", 0, i=0)
329+
self.assertQualCount(entity, "P31", 1, i=1)
330+
remove_qual.update_entity_json(entity)
331+
self.assertQualCount(entity, "P31", 0, i=0)
332+
self.assertQualCount(entity, "P31", 0, i=1)
333+
# -----
334+
remove_qual = batch.commands()[3]
335+
with self.assertRaises(NoQualifiers):
336+
remove_qual.update_entity_json(entity)
337+
# -----
338+
remove_ref_part = batch.commands()[4]
339+
self.assertRefCount(entity, "P31", 2)
340+
self.assertRefPartsCount(entity, "P31", 2, ipart=0)
341+
self.assertRefPartsCount(entity, "P31", 3, ipart=1)
342+
self.assertRefCount(entity, "P31", 1, i=1)
343+
self.assertRefPartsCount(entity, "P31", 1, i=1, ipart=0)
344+
remove_ref_part.update_entity_json(entity)
345+
self.assertRefCount(entity, "P31", 2)
346+
self.assertRefPartsCount(entity, "P31", 1, ipart=0)
347+
self.assertRefPartsCount(entity, "P31", 3, ipart=1)
348+
self.assertRefCount(entity, "P31", 1, i=1)
349+
self.assertRefPartsCount(entity, "P31", 1, i=1, ipart=0)
350+
# -----
351+
remove_ref_part = batch.commands()[5]
352+
self.assertRefCount(entity, "P31", 2)
353+
self.assertRefPartsCount(entity, "P31", 1, ipart=0)
354+
self.assertRefPartsCount(entity, "P31", 3, ipart=1)
355+
self.assertRefCount(entity, "P31", 1, i=1)
356+
self.assertRefPartsCount(entity, "P31", 1, i=1, ipart=0)
357+
remove_ref_part.update_entity_json(entity)
358+
self.assertRefCount(entity, "P31", 2)
359+
self.assertRefPartsCount(entity, "P31", 1, ipart=0)
360+
self.assertRefPartsCount(entity, "P31", 3, ipart=1)
361+
self.assertRefCount(entity, "P31", 0, i=1)
362+
self.assertRefPartsCount(entity, "P31", 0, i=1, ipart=0)
363+
# -----
364+
remove_ref_part = batch.commands()[6]
365+
with self.assertRaises(NoReferenceParts):
366+
remove_ref_part.update_entity_json(entity)
367+
368+
247369
def test_remove_reference(self):
248370
text = """
249371
REMOVE_REF|Q12345678|P65|42|S31|somevalue

0 commit comments

Comments
 (0)