Wikidata:Pywikibot - Python 3 Tutorial/Quantities and Units
In this example we will load an item, check if the claim exists, and otherwise add it to the item. We will set a quantity, uncertainty, an unit and a source.
Danger Zone edit
Make sure before running the examples that the bot is editing test.wikidata.org
.
Check your user-config.py
family = 'wikidata'
mylang = 'test'
usernames['wikidata']['test'] = u'YOUR_BOT_OR_USER_NAME'
Check your login (note the wikidata:test
):
$ python3 pwb.py login
Logged in on wikidata:test as YOUR_BOT_OR_USER_NAME.
Then make sure that each script calls the test-site:
site = pywikibot.Site("test", "wikidata")
All examples use the Wikidata Sandbox (Q4115189) item to further prevent accidental vandalism to Wikidata-proper by people copy-pasting code. Because the item does not exist on test.wikidata you can just create a new item (https://test.wikidata.org/wiki/Special:NewItem) for your practice edits. If you copy-paste the examples and run them, two things might happen: You will edit Wikidata Sandbox (Q4115189) or you will see an error message of test.wikidata that tells you that the item does not exist on that site.
pywikibot.data.api.APIError: no-such-entity: Could not find such an entity (Can't access entity Q4115189, revision may have been deleted.) [help:See https://test.wikidata.org/w/api.php for API usage; messages:[{'parameters': [], 'html': {'*': 'Could not find such an entity'}, 'name': 'wikibase-api-no-such-entity'}]]
ID's of properties and values do differ between test and live, so unexpected errors (like: ValueError: wikidata:test:Q101352 is not type <class 'str'>.) may arise if you use property and qualifier ID's from live.
Example edit
The main item I created was https://test.wikidata.org/wiki/Q1746 (uranium-240). As seen in en:Isotopes of uranium uranium-240 has a half-life of 14.1(1) h.
Remember that test.wikidata has other item and property numbers than wikidata-proper. If you run this example on wikidata-proper you will have to correct the numbers and make sure you know the rules and your code!
import pywikibot
from pywikibot.data import api
import pprint
# FIXME Hardcoded for test.wikidata
# Define properties and data
p_stated_in = "P149"
p_half_life = "P525"
p_ref_url = "P93"
precision = 10 ** -10
# data = [quantity, uncertainty, unit (Q1748 = hours)]
# source = [stated in item, ref url]
half_life_data = {"uranium-240": {"data": ["14.1", "0.1", "Q1748"],
"source": ["Q1751", "http://www.nndc.bnl.gov/chart/reCenter.jsp?z=92&n=148"]}
}
site = pywikibot.Site("test", "wikidata")
repo = site.data_repository()
def get_items(site, item_title):
"""
Requires a site and search term (item_title) and returns the results.
"""
params = {"action": "wbsearchentities",
"format": "json",
"language": "en",
"type": "item",
"search": item_title}
request = api.Request(site=site, **params)
return request.submit()
def check_claim_and_uncert(item, property, data):
"""
Requires a property, value, uncertainty and unit and returns boolean.
Returns the claim that fits into the defined precision or None.
"""
item_dict = item.get()
value, uncert, unit = data
value, uncert = float(value), float(uncert)
try:
claims = item_dict["claims"][property]
except:
return None
try:
claim_exists = False
uncert_set = False
for claim in claims:
wb_quant = claim.getTarget()
delta_amount = wb_quant.amount - value
if abs(delta_amount) < precision:
claim_exists = True
delta_lower = wb_quant.amount - wb_quant.lowerBound
delta_upper = wb_quant.upperBound - wb_quant.amount
check_lower = abs(uncert - delta_lower) < precision
check_upper = abs(delta_upper - uncert) < precision
if check_upper and check_lower:
uncert_set = True
if claim_exists and uncert_set:
return claim
except:
return None
def check_source_set(claim, property, data):
source_claims = claim.getSources()
if len(source_claims) == 0:
return False
for source in source_claims:
try:
stated_in_claim = source[p_stated_in]
except:
return False
for claim in stated_in_claim:
trgt = claim.target
if trgt.id == data[0]:
return True
def set_claim(item, property, data):
value, uncert, unit = data
value, uncert = float(value), float(uncert)
claim = pywikibot.Claim(repo, property)
unit_item = pywikibot.ItemPage(repo, unit)
entity_helper_string = "http://test.wikidata.org/entity/Q1748".format()
wb_quant = pywikibot.WbQuantity(value, entity_helper_string, uncert)
claim.setTarget(wb_quant)
item.addClaim(claim, bot=False, summary="Adding half-life claim from NNDC.")
return claim
def create_source_claim(claim, source_data):
trgt_item, ref_url = source_data
trgt_itempage = pywikibot.ItemPage(repo, trgt_item)
source_claim = pywikibot.Claim(repo, p_stated_in, isReference=True)
source_claim.setTarget(trgt_itempage)
claim.addSources([source_claim])
return True
for key in half_life_data:
search_results = get_items(site, key)
if len(search_results["search"]) == 1:
item = pywikibot.ItemPage(repo, search_results["search"][0]["id"])
data = half_life_data[key]["data"]
source_data = half_life_data[key]["source"]
claim = check_claim_and_uncert(item, p_half_life, data)
if claim:
source = check_source_set(claim, key, source_data)
if source:
pass
else:
create_source_claim(claim, source_data)
else:
claim = set_claim(item, p_half_life, data)
create_source_claim(claim, source_data)
else:
print("No result or too many found for {}.", key)
References edit
* http://stackoverflow.com/questions/27452656/wikidata-entity-value-from-name - I modified parts of the code by Nonsense.