Wikidata:Pywikibot - Python 3 Tutorial/Quantities and Units

From Wikidata
Jump to navigation Jump to search

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.