Skip to content

Commit 502b2d2

Browse files
authored
Merge pull request #171 from WikiMovimentoBrasil/2025-02-small-fixes
fix: REMOVE_QUAL and REMOVE_REF throw errors && CREATE updates their entity_d
2 parents 1622fbb + 49dd227 commit 502b2d2

File tree

4 files changed

+151
-6
lines changed

4 files changed

+151
-6
lines changed

src/core/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ def __init__(self, entity_id, property_id, value):
6060
return super().__init__(message)
6161

6262

63+
class NoQualifiers(ApiException):
64+
def __init__(self):
65+
return super().__init__("There are no qualifiers with the given value")
66+
67+
68+
class NoReferenceParts(ApiException):
69+
def __init__(self):
70+
return super().__init__("There are no reference parts with the given value")
71+
72+
6373
class UserError(ApiException):
6474
def __init__(self, status, response_code, response_message, response_json):
6575
self.status = status

src/core/models.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from .exceptions import UserError
2121
from .exceptions import NoStatementsForThatProperty
2222
from .exceptions import NoStatementsWithThatValue
23+
from .exceptions import NoQualifiers
24+
from .exceptions import NoReferenceParts
2325
from .exceptions import NonexistantPropertyOrNoDataType
2426

2527
logger = logging.getLogger("qsts3")
@@ -376,6 +378,8 @@ class Error(models.TextChoices):
376378
OP_NOT_IMPLEMENTED = "op_not_implemented", _("Operation not implemented")
377379
NO_STATEMENTS_PROPERTY = "no_statements_property", _("No statements for given property")
378380
NO_STATEMENTS_VALUE = "no_statements_value", _("No statements with given value")
381+
NO_QUALIIFERS = "no_qualifiers", _("No qualifiers with given value")
382+
NO_REFERENCE_PARTS = "no_reference_parts", _("No reference parts with given value")
379383
SITELINK_INVALID = "sitelink_invalid", _("The sitelink id is invalid")
380384
COMBINING_COMMAND_FAILED = "combining_failed", _("The next command failed")
381385
API_USER_ERROR = "api_user_error", _("API returned a User error")
@@ -402,6 +406,8 @@ def start(self):
402406
def finish(self):
403407
logger.info(f"[{self}] finished")
404408
self.status = BatchCommand.STATUS_DONE
409+
if self.operation == BatchCommand.Operation.CREATE_ITEM:
410+
self.set_entity_id(self.response_id())
405411
self.save()
406412
self.propagate_status_to_previous_commands()
407413

@@ -449,10 +455,9 @@ def entity_id(self):
449455
def set_entity_id(self, value):
450456
if self.json.get("item", None):
451457
self.json["item"] = value
452-
elif self.json.get("entity", {}).get("id", None):
453-
self.json["entity"]["id"] = value
454458
else:
455-
raise ValueError("This command has no entity to update its id.")
459+
self.json.setdefault("entity", {})
460+
self.json["entity"]["id"] = value
456461

457462
def entity_url(self):
458463
entity_id = self.entity_id()
@@ -703,6 +708,10 @@ def run(self, client: Client):
703708
self.error_with_value(self.Error.NO_STATEMENTS_PROPERTY)
704709
except NoStatementsWithThatValue:
705710
self.error_with_value(self.Error.NO_STATEMENTS_VALUE)
711+
except NoQualifiers:
712+
self.error_with_value(self.Error.NO_QUALIIFERS)
713+
except NoReferenceParts:
714+
self.error_with_value(self.Error.NO_REFERENCE_PARTS)
706715
except UserError as e:
707716
if e.response_message == "Invalid path parameter: 'site_id'":
708717
self.error_with_value(self.Error.SITELINK_INVALID)
@@ -896,15 +905,22 @@ def _remove_qualifier_or_reference(self, entity: dict):
896905
Removes a qualifier or a reference from the entity.
897906
"""
898907
statement = self._get_statement(entity)
899-
if statement is None:
900-
return
908+
statement = statement if statement else {}
909+
found_qualifier = False
910+
found_ref_part = False
901911
for i, qual in enumerate(statement.get("qualifiers", [])):
902912
if self.is_in_qualifiers(qual):
903913
statement["qualifiers"].pop(i)
914+
found_qualifier = True
904915
for i, ref in enumerate(statement.get("references", [])):
905916
for j, part in enumerate(ref["parts"]):
906917
if self.is_part_in_references(part):
907918
statement["references"][i]["parts"].pop(j)
919+
found_ref_part = True
920+
if not found_qualifier and len(self.qualifiers_for_api()) > 0:
921+
raise NoQualifiers()
922+
if not found_ref_part and len(self.references_for_api()) > 0:
923+
raise NoReferenceParts()
908924

909925
def _remove_entity_statement(self, entity: dict):
910926
"""

src/core/tests/test_batch_processing.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,112 @@ def test_all_errors(self, mocker):
490490
self.assertEqual(len(commands), 7)
491491
for command in commands:
492492
self.assertEqual(command.status, BatchCommand.STATUS_ERROR)
493+
494+
@requests_mock.Mocker()
495+
def test_get_label(self, mocker):
496+
ApiMocker.wikidata_property_data_types(mocker)
497+
ApiMocker.is_autoconfirmed(mocker)
498+
ApiMocker.create_item(mocker, "Q1")
499+
ApiMocker.item_empty(mocker, "Q1")
500+
ApiMocker.item_empty(mocker, "Q2")
501+
ApiMocker.property_data_type(mocker, "P1", "quantity")
502+
ApiMocker.patch_item_successful(mocker, "Q1", {})
503+
ApiMocker.patch_item_successful(mocker, "Q2", {})
504+
batch = self.parse("""
505+
CREATE
506+
LAST|P1|12
507+
Q2|P1|15
508+
""")
509+
batch.combine_commands = True
510+
commands = batch.commands()
511+
client = ApiClient.from_username(batch.user)
512+
ApiMocker.labels(mocker, client, "Q1", {"pt": "pt1", "en": "en1"})
513+
ApiMocker.labels(mocker, client, "Q2", {"pt": "pt2", "en": "en2"})
514+
self.assertEqual(commands[0].get_label(client, "pt"), None)
515+
self.assertEqual(commands[1].get_label(client, "pt"), None)
516+
self.assertEqual(commands[2].get_label(client, "pt"), "pt2")
517+
self.assertEqual(commands[0].get_label(client, "de"), None)
518+
self.assertEqual(commands[1].get_label(client, "de"), None)
519+
self.assertEqual(commands[2].get_label(client, "de"), "en2")
520+
self.assertEqual(commands[0].get_label(client, "en"), None)
521+
self.assertEqual(commands[1].get_label(client, "en"), None)
522+
self.assertEqual(commands[2].get_label(client, "en"), "en2")
523+
batch.run() # -> load Q1 into commands[0] and commands[1]
524+
self.assertEqual(commands[0].get_label(client, "pt"), "pt1")
525+
self.assertEqual(commands[1].get_label(client, "pt"), "pt1")
526+
self.assertEqual(commands[2].get_label(client, "pt"), "pt2")
527+
self.assertEqual(commands[0].get_label(client, "de"), "en1")
528+
self.assertEqual(commands[1].get_label(client, "de"), "en1")
529+
self.assertEqual(commands[2].get_label(client, "de"), "en2")
530+
self.assertEqual(commands[0].get_label(client, "en"), "en1")
531+
self.assertEqual(commands[1].get_label(client, "en"), "en1")
532+
self.assertEqual(commands[2].get_label(client, "en"), "en2")
533+
534+
@requests_mock.Mocker()
535+
def test_remove_qual_or_ref_errors(self, mocker):
536+
ApiMocker.item(
537+
mocker,
538+
"Q1",
539+
{
540+
"statements": {
541+
"P5": [
542+
{
543+
"id": "Q1234$abcdefgh-uijkl",
544+
"value": {
545+
"type": "value",
546+
"content": "Q12",
547+
},
548+
"qualifiers": [
549+
{
550+
"property": {"id": "P65", "data_type": "quantity"},
551+
"value": {
552+
"type": "value",
553+
"content": {"amount": "+84", "unit": "1"},
554+
},
555+
},
556+
],
557+
"references": [
558+
{
559+
"hash": "i_am_ahash",
560+
"parts": [
561+
{
562+
"property": {"id": "P93", "data_type": "url"},
563+
"value": {
564+
"type": "value",
565+
"content": "https://kernel.org/",
566+
},
567+
},
568+
],
569+
},
570+
],
571+
},
572+
],
573+
},
574+
},
575+
)
576+
ApiMocker.wikidata_property_data_types(mocker)
577+
ApiMocker.is_autoconfirmed(mocker)
578+
ApiMocker.property_data_type(mocker, "P5", "wikibase-item")
579+
ApiMocker.patch_item_successful(mocker, "Q1", {})
580+
batch = self.parse("""
581+
REMOVE_QUAL|Q1|P5|Q12|P123|123
582+
REMOVE_QUAL|Q1|P5|Q999|P65|84
583+
REMOVE_QUAL|Q1|P5|Q12|P65|84
584+
REMOVE_REF|Q1|P5|Q12|S93|"https://kernel.xyz"
585+
REMOVE_REF|Q1|P5|Q999|S93|"https://kernel.org/"
586+
REMOVE_REF|Q1|P5|Q12|S93|"https://kernel.org/"
587+
""")
588+
commands = batch.commands()
589+
batch.run()
590+
# qualifiers
591+
self.assertEqual(commands[0].status, BatchCommand.STATUS_ERROR)
592+
self.assertEqual(commands[0].error, BatchCommand.Error.NO_QUALIIFERS)
593+
self.assertEqual(commands[1].status, BatchCommand.STATUS_ERROR)
594+
self.assertEqual(commands[1].error, BatchCommand.Error.NO_QUALIIFERS)
595+
self.assertEqual(commands[2].status, BatchCommand.STATUS_DONE)
596+
# references
597+
self.assertEqual(commands[3].status, BatchCommand.STATUS_ERROR)
598+
self.assertEqual(commands[3].error, BatchCommand.Error.NO_REFERENCE_PARTS)
599+
self.assertEqual(commands[4].status, BatchCommand.STATUS_ERROR)
600+
self.assertEqual(commands[4].error, BatchCommand.Error.NO_REFERENCE_PARTS)
601+
self.assertEqual(commands[5].status, BatchCommand.STATUS_DONE)

src/core/tests/test_entity_patching.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from django.test import TestCase
33

44
from core.parsers.v1 import V1CommandParser
5+
from core.exceptions import NoQualifiers
6+
from core.exceptions import NoReferenceParts
57

68

79
class RemoveQualRefTests(TestCase):
@@ -180,6 +182,7 @@ def test_remove_qualifier(self):
180182
text = """
181183
REMOVE_QUAL|Q12345678|P65|42|P84267|-5
182184
REMOVE_QUAL|Q12345678|P31|somevalue|P18|+2025-01-15T00:00:00Z/11
185+
REMOVE_QUAL|Q12345678|P65|42|P84267|999
183186
"""
184187
batch = self.parse(text)
185188
entity = copy.deepcopy(self.INITIAL)
@@ -196,6 +199,12 @@ def test_remove_qualifier(self):
196199
self.assertQualCount(entity, "P31", 1)
197200
remove_p31_qual.update_entity_json(entity)
198201
self.assertQualCount(entity, "P31", 0)
202+
# -----
203+
remove_nothing = batch.commands()[2]
204+
self.assertQualCount(entity, "P65", 1)
205+
with self.assertRaises(NoQualifiers):
206+
remove_nothing.update_entity_json(entity)
207+
self.assertQualCount(entity, "P65", 1)
199208

200209
def test_remove_reference(self):
201210
text = """
@@ -207,7 +216,8 @@ def test_remove_reference(self):
207216
# -----
208217
remove_nothing = batch.commands()[0]
209218
self.assertRefCount(entity, "P65", 0)
210-
remove_nothing.update_entity_json(entity)
219+
with self.assertRaises(NoReferenceParts):
220+
remove_nothing.update_entity_json(entity)
211221
self.assertRefCount(entity, "P65", 0)
212222
# -----
213223
remove_part_mediawiki = batch.commands()[1]

0 commit comments

Comments
 (0)