Skip to content

Commit 370376b

Browse files
authored
Merge pull request #399 from wikimediabrasil/create-property
feat: CREATE_PROPERTY && remove wikibase from docker-compose
2 parents 2b43b65 + fbcc002 commit 370376b

File tree

4 files changed

+89
-91
lines changed

4 files changed

+89
-91
lines changed

docker-compose.yml

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ x-application-service: &application-service
2525
- ./translations:/root/www/python/translations
2626
- qs-static-data:/root/www/python/static
2727

28-
x-wikibase-volumes: &wikibase-volumes
29-
volumes:
30-
- wikibase-config-data:/config
31-
- wikibase-static-data:/var/www/html/images
32-
3328
services:
3429
mariadb:
3530
image: mariadb:11.7.2
@@ -79,54 +74,6 @@ services:
7974
app:
8075
condition: service_healthy
8176

82-
wikibase:
83-
<<: *wikibase-volumes
84-
image: wikibase/wikibase:4.0
85-
ports:
86-
- 8888:80
87-
environment:
88-
MW_ADMIN_NAME: "${WIKIBASE_ADMIN_USERNAME:-admin}"
89-
MW_ADMIN_PASS: "${WIKIBASE_ADMIN_PASSWORD:-wikibase_admin_pass}"
90-
MW_ADMIN_EMAIL: "${WIKIBASE_ADMIN_EMAIL:-admin@wikibase.example}"
91-
MW_WG_SERVER: http://localhost:8888
92-
DB_SERVER: wikibase-db:3306
93-
DB_NAME: wikibase
94-
DB_USER: wikibase
95-
DB_PASS: wikibase_pass
96-
healthcheck:
97-
test: curl --silent --fail localhost/wiki/Main_Page
98-
interval: 10s
99-
start_period: 5m
100-
depends_on:
101-
wikibase-db:
102-
condition: service_healthy
103-
104-
wikibase-jobrunner:
105-
<<: *wikibase-volumes
106-
image: wikibase/wikibase:4.0
107-
command: /jobrunner-entrypoint.sh
108-
depends_on:
109-
wikibase:
110-
condition: service_healthy
111-
112-
wikibase-db:
113-
image: mariadb:10.11
114-
environment:
115-
MARIADB_DATABASE: wikibase
116-
MARIADB_USER: wikibase
117-
MARIADB_PASSWORD: wikibase_pass
118-
MARIADB_RANDOM_ROOT_PASSWORD: "true"
119-
healthcheck:
120-
test: healthcheck.sh --connect --innodb_initialized
121-
start_period: 1m
122-
interval: 20s
123-
timeout: 5s
124-
volumes:
125-
- wikibase-db-data:/var/lib/mysql
126-
12777
volumes:
12878
qs-mariadb-data:
12979
qs-static-data:
130-
wikibase-config-data:
131-
wikibase-static-data:
132-
wikibase-db-data:

src/core/models.py

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import copy
2+
import traceback
23
import csv
34
import logging
45
import math
@@ -214,13 +215,16 @@ def get_property_value_type(self, property_id):
214215
Uses a dictionary attribute for caching.
215216
"""
216217
endpoint = f"/entities/properties/{property_id}"
217-
url = self.wikibase_url(endpoint)
218218

219219
try:
220-
res = self.session.get(url).json()
220+
res = self.wikibase_request_wrapper("get", endpoint, {})
221221
data_type = res["data_type"]
222222
except (KeyError, UserError):
223223
raise NonexistantPropertyOrNoDataType(property_id)
224+
except Exception as e:
225+
logger.error(f"Error while trying to get data type: {e}")
226+
traceback.print_exc()
227+
raise NonexistantPropertyOrNoDataType(property_id)
224228

225229
try:
226230
value_type = self.data_type_to_value_type(data_type)
@@ -285,6 +289,8 @@ def wikibase_entity_endpoint(entity_id, entity_endpoint=""):
285289
base = "/entities/items"
286290
elif entity_id.startswith("P"):
287291
base = "/entities/properties"
292+
elif entity_id.upper() == "LAST":
293+
raise LastCouldNotBeEvaluated()
288294
else:
289295
raise EntityTypeNotImplemented(entity_id)
290296

@@ -985,7 +991,7 @@ def start(self):
985991
def finish(self):
986992
logger.info(f"[{self}] finished")
987993
self.status = BatchCommand.STATUS_DONE
988-
if self.is_id_last_or_create_item():
994+
if self.is_entity_creation():
989995
self.set_entity_id(self.response_id)
990996
self.save()
991997
self.propagate_to_previous_commands()
@@ -999,6 +1005,7 @@ def error_with_value(self, value: Error, message: str = None):
9991005

10001006
def error_with_exception(self, exception: Exception):
10011007
message = getattr(exception, "message", str(exception))
1008+
traceback.print_exc()
10021009
self.error_with_message(message)
10031010

10041011
def error_with_message(self, message):
@@ -1015,7 +1022,7 @@ def propagate_to_previous_commands(self):
10151022
if self.is_error_status():
10161023
cmd.error = self.Error.COMBINING_COMMAND_FAILED
10171024
cmd.message = cmd.error.label
1018-
elif cmd.is_id_last_or_create_item():
1025+
elif cmd.is_entity_creation():
10191026
cmd.set_entity_id(self.entity_id)
10201027
cmd.save()
10211028

@@ -1084,6 +1091,10 @@ def sitelink(self):
10841091
def what(self):
10851092
return self.json.get("what", "").upper()
10861093

1094+
@property
1095+
def property_data_type(self):
1096+
return self.json.get("data", "").lower()
1097+
10871098
@property
10881099
def related_identifiers_set(self):
10891100
"""
@@ -1337,11 +1348,27 @@ def is_sitelink_command(self):
13371348
def is_error_status(self):
13381349
return self.status == BatchCommand.STATUS_ERROR
13391350

1340-
def is_not_create_item(self):
1341-
return self.operation != self.Operation.CREATE_ITEM
1351+
def is_not_create_entity(self):
1352+
return self.operation not in [self.Operation.CREATE_ITEM, self.Operation.CREATE_PROPERTY]
13421353

1343-
def is_id_last_or_create_item(self):
1344-
return self.entity_id == "LAST" or self.operation == self.Operation.CREATE_ITEM
1354+
def get_first_command_operation(self):
1355+
first_command = self
1356+
previous = getattr(self, "previous_commands", [])
1357+
if len(previous) > 0:
1358+
first_command = previous[-1]
1359+
return first_command.operation
1360+
1361+
def is_item_creation(self):
1362+
operation = self.get_first_command_operation()
1363+
return operation == self.Operation.CREATE_ITEM
1364+
1365+
def is_property_creation(self):
1366+
operation = self.get_first_command_operation()
1367+
return operation == self.Operation.CREATE_PROPERTY
1368+
1369+
def is_entity_creation(self):
1370+
operation = self.get_first_command_operation()
1371+
return operation in [self.Operation.CREATE_ITEM, self.Operation.CREATE_PROPERTY]
13451372

13461373
# # -----------------
13471374
# # LAST related methods
@@ -1447,6 +1474,7 @@ def operation_is_combinable(self):
14471474
self.Operation.CREATE_ITEM,
14481475
self.Operation.SET_STATEMENT,
14491476
self.Operation.CREATE_STATEMENT,
1477+
self.Operation.CREATE_PROPERTY,
14501478
self.Operation.REMOVE_STATEMENT_BY_VALUE,
14511479
self.Operation.REMOVE_QUALIFIER,
14521480
self.Operation.REMOVE_REFERENCE,
@@ -1480,18 +1508,19 @@ def check_combination(self, state: CombiningState, next: Optional["BatchCommand"
14801508
and self.operation_is_combinable()
14811509
and next is not None
14821510
and next.operation_is_combinable()
1483-
and next.is_not_create_item()
1511+
and next.is_not_create_entity()
14841512
and self.has_combinable_id_with(next)
14851513
)
14861514
self.previous_entity_json = state.entity
14871515
self.previous_commands = state.commands
14881516

14891517
def has_combinable_id_with(self, next: "BatchCommand"):
14901518
"""
1491-
Returns True if `self` is CREATE_ITEM and `next`
1519+
Returns True if `self` is CREATE_ITEM or CREATE_PROPERTY and `next`
14921520
has LAST as entity id, or if both have the same entity id.
14931521
"""
1494-
return (self.operation == self.Operation.CREATE_ITEM and next.entity_id == "LAST") or (
1522+
create_operations = [self.Operation.CREATE_ITEM, self.Operation.CREATE_PROPERTY]
1523+
return (self.operation in create_operations and next.entity_id == "LAST") or (
14951524
self.entity_id == next.entity_id
14961525
)
14971526

@@ -1528,10 +1557,11 @@ def get_original_entity_json(self, client: Client):
15281557
self.previous_entity_json = copy.deepcopy(entity)
15291558
return entity
15301559

1531-
def get_entity_or_empty_item(self, client: Client):
1560+
def get_entity_or_empty_entity(self, client: Client):
15321561
"""
15331562
Calls the API to get the entity json or returns
1534-
an empty item if this command is a CREATE_ITEM.
1563+
an empty entity. Items for CREATE_ITEM and
1564+
properties for CREATE_PROPERTY.
15351565
"""
15361566
if self.operation == self.Operation.CREATE_ITEM:
15371567
return {
@@ -1543,6 +1573,16 @@ def get_entity_or_empty_item(self, client: Client):
15431573
"sitelinks": {},
15441574
"id": None,
15451575
}
1576+
elif self.operation == self.Operation.CREATE_PROPERTY:
1577+
return {
1578+
"type": "property",
1579+
"data_type": self.property_data_type,
1580+
"labels": {},
1581+
"descriptions": {},
1582+
"aliases": {},
1583+
"statements": {},
1584+
"id": None,
1585+
}
15461586
else:
15471587
if self.entity_id == "LAST":
15481588
raise LastCouldNotBeEvaluated()
@@ -1556,7 +1596,7 @@ def get_previous_entity_json(self, client: Client):
15561596
to the next command.
15571597
"""
15581598
cached = getattr(self, "previous_entity_json", None)
1559-
entity = cached if cached else self.get_entity_or_empty_item(client)
1599+
entity = cached if cached else self.get_entity_or_empty_entity(client)
15601600
return entity
15611601

15621602
def get_final_entity_json(self, client: Client) -> dict:
@@ -1712,9 +1752,13 @@ def api_payload(self, client: Client):
17121752
"""
17131753
if self.operation == self.Operation.CREATE_ITEM:
17141754
return {"item": {}}
1755+
if self.operation == self.Operation.CREATE_PROPERTY:
1756+
return {"property": {"data_type": self.property_data_type}}
17151757
if self.operation_is_combinable():
1716-
if self.is_id_last_or_create_item():
1758+
if self.is_item_creation():
17171759
return {"item": self.get_final_entity_json(client)}
1760+
elif self.is_property_creation():
1761+
return {"property": self.get_final_entity_json(client)}
17181762
else:
17191763
return {"patch": self.entity_patch(client)}
17201764
return {}
@@ -1739,8 +1783,6 @@ def send_to_api(self, client: Client) -> dict:
17391783
- `NotImplementedError` if the operation
17401784
is not implemented.
17411785
"""
1742-
if self.operation == self.Operation.CREATE_PROPERTY:
1743-
raise NotImplementedError()
17441786
method, endpoint = self.operation_method_and_endpoint(client)
17451787
body = self.api_body(client)
17461788
return client.wikibase_request_wrapper(method, endpoint, body)
@@ -1755,8 +1797,10 @@ def operation_method_and_endpoint(self, client: Client):
17551797
necessary for the operation.
17561798
"""
17571799
if self.operation_is_combinable():
1758-
if self.is_id_last_or_create_item():
1800+
if self.is_item_creation():
17591801
return ("POST", "/entities/items")
1802+
elif self.is_property_creation():
1803+
return ("POST", "/entities/properties")
17601804
else:
17611805
return ("PATCH", Client.wikibase_entity_endpoint(self.entity_id))
17621806
match self.operation:

src/core/tests/test_api.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ def create_item(self, mocker, item_id):
280280
status_code=200,
281281
)
282282

283+
def create_property(self, mocker, property_id):
284+
mocker.post(
285+
self.wikibase_url("/entities/properties"),
286+
json={"id": property_id},
287+
status_code=200,
288+
)
289+
283290
def property_data_types(self, mocker, mapper):
284291
mocker.get(
285292
self.wikibase_url("/property-data-types"),
@@ -666,15 +673,18 @@ def test_send_create_item(self, mocker):
666673
@requests_mock.Mocker()
667674
def test_send_create_property(self, mocker):
668675
self.api_mocker.is_autoconfirmed(mocker)
676+
self.api_mocker.create_property(mocker, "P5")
669677
parser = V1CommandParser()
670678
batch = BatchFactory.load_from_parser(
671679
parser, "b", "u", "CREATE_PROPERTY|wikibase-item||LAST|P1|Q1"
672680
)
673681
cmd: BatchCommand = batch.commands()[0]
674682
cmd.run(self.api_client)
675683
self.assertEqual(cmd.operation, BatchCommand.Operation.CREATE_PROPERTY)
676-
self.assertEqual(cmd.status, BatchCommand.STATUS_ERROR)
677-
self.assertEqual(cmd.error, BatchCommand.Error.OP_NOT_IMPLEMENTED)
678-
self.assertIsNone(cmd.response_id)
679-
with self.assertRaises(NotImplementedError):
680-
cmd.send_to_api(self.api_client)
684+
self.assertEqual(cmd.status, BatchCommand.STATUS_DONE)
685+
self.assertIsNone(cmd.error)
686+
self.assertEqual(
687+
cmd.api_payload(self.api_client),
688+
{"property": {"data_type": "wikibase-item"}},
689+
)
690+
self.assertEqual(cmd.response_id, "P5")

src/core/tests/test_batch_processing.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,6 @@ def test_all_errors(self, mocker):
500500
)
501501
batch = self.parse(
502502
"""
503-
CREATE_PROPERTY|url
504503
Q1234|P5|12
505504
Q1234|Sptwikix|"Cool article"
506505
Q5|P5|123
@@ -513,25 +512,23 @@ def test_all_errors(self, mocker):
513512
batch.run()
514513
self.assertEqual(batch.status, Batch.STATUS_DONE)
515514
commands = batch.commands()
516-
self.assertEqual(commands[0].operation, BatchCommand.Operation.CREATE_PROPERTY)
517-
self.assertEqual(commands[0].error, BatchCommand.Error.OP_NOT_IMPLEMENTED)
518-
self.assertEqual(commands[1].operation, BatchCommand.Operation.SET_STATEMENT)
519-
self.assertEqual(commands[1].error, BatchCommand.Error.COMBINING_COMMAND_FAILED)
520-
self.assertEqual(commands[2].operation, BatchCommand.Operation.SET_SITELINK)
521-
self.assertEqual(commands[2].error, BatchCommand.Error.SITELINK_INVALID)
515+
self.assertEqual(commands[0].operation, BatchCommand.Operation.SET_STATEMENT)
516+
self.assertEqual(commands[0].error, BatchCommand.Error.COMBINING_COMMAND_FAILED)
517+
self.assertEqual(commands[1].operation, BatchCommand.Operation.SET_SITELINK)
518+
self.assertEqual(commands[1].error, BatchCommand.Error.SITELINK_INVALID)
519+
self.assertEqual(commands[2].operation, BatchCommand.Operation.SET_STATEMENT)
520+
self.assertEqual(commands[2].error, BatchCommand.Error.API_USER_ERROR)
522521
self.assertEqual(commands[3].operation, BatchCommand.Operation.SET_STATEMENT)
523-
self.assertEqual(commands[3].error, BatchCommand.Error.API_USER_ERROR)
524-
self.assertEqual(commands[4].operation, BatchCommand.Operation.SET_STATEMENT)
525-
self.assertEqual(commands[4].error, BatchCommand.Error.API_SERVER_ERROR)
522+
self.assertEqual(commands[3].error, BatchCommand.Error.API_SERVER_ERROR)
526523
self.assertEqual(
527-
commands[5].operation, BatchCommand.Operation.REMOVE_STATEMENT_BY_VALUE
524+
commands[4].operation, BatchCommand.Operation.REMOVE_STATEMENT_BY_VALUE
528525
)
529-
self.assertEqual(commands[5].error, BatchCommand.Error.NO_STATEMENTS_PROPERTY)
526+
self.assertEqual(commands[4].error, BatchCommand.Error.NO_STATEMENTS_PROPERTY)
530527
self.assertEqual(
531-
commands[6].operation, BatchCommand.Operation.REMOVE_STATEMENT_BY_VALUE
528+
commands[5].operation, BatchCommand.Operation.REMOVE_STATEMENT_BY_VALUE
532529
)
533-
self.assertEqual(commands[6].error, BatchCommand.Error.NO_STATEMENTS_VALUE)
534-
self.assertEqual(len(commands), 7)
530+
self.assertEqual(commands[5].error, BatchCommand.Error.NO_STATEMENTS_VALUE)
531+
self.assertEqual(len(commands), 6)
535532
for command in commands:
536533
self.assertEqual(command.status, BatchCommand.STATUS_ERROR)
537534

0 commit comments

Comments
 (0)