Examples#

More sample code (adding an entity, a lexeme, etc.) is in the Jupyter notebooks of the repository.

In order to create a minimal bot based on WikibaseIntegrator, two things are required:

  • A datatype object containing a value.

  • An entity object (Item/Property/Lexeme/…) which takes the data, does the checks and performs write.

An optional Login object can be used to be authenticated on the Wikibase instance.

Create a new Item#

from wikibaseintegrator import wbi_login, WikibaseIntegrator
from wikibaseintegrator.datatypes import ExternalID
from wikibaseintegrator.wbi_config import config as wbi_config

wbi_config['USER_AGENT'] = 'MyWikibaseBot/1.0 (https://www.wikidata.org/wiki/User:MyUsername)'

# login object
login_instance = wbi_login.OAuth2(consumer_token='<consumer_token>', consumer_secret='<consumer_secret>')

wbi = WikibaseIntegrator(login=login_instance)

# data type object, e.g. for a NCBI gene entrez ID
entrez_gene_id = ExternalID(value='<some_entrez_id>', prop_nr='P351')

# data goes into a list, because many data objects can be provided to
data = [entrez_gene_id]

# Create a new item
item = wbi.item.new()

# Set an english label
item.labels.set(language='en', value='Newly created item')

# Set a French description
item.descriptions.set(language='fr', value='Une description un peu longue')

item.claims.add(data)
item.write()

Modify an existing item#

from wikibaseintegrator import wbi_login, WikibaseIntegrator
from wikibaseintegrator.datatypes import ExternalID
from wikibaseintegrator.wbi_enums import ActionIfExists
from wikibaseintegrator.wbi_config import config as wbi_config

wbi_config['USER_AGENT'] = 'MyWikibaseBot/1.0 (https://www.wikidata.org/wiki/User:MyUsername)'

# login object
login_instance = wbi_login.OAuth2(consumer_token='<consumer_token>', consumer_secret='<consumer_secret>')

wbi = WikibaseIntegrator(login=login_instance)

# data type object, e.g. for a NCBI gene entrez ID
entrez_gene_id = ExternalID(value='<some_entrez_id>', prop_nr='P351')

# data goes into a list, because many data objects can be provided to
data = [entrez_gene_id]

# Search and then edit an Item
item = wbi.item.get(entity_id='Q141806')

# Set an english label but don't modify it if there is already an entry
item.labels.set(language='en', value='An updated item', action_if_exists=ActionIfExists.KEEP)

# Set a French description and replace the existing one
item.descriptions.set(language='fr', value='Une description un peu longue', action_if_exists=ActionIfExists.REPLACE_ALL)

# Add the claims, the existing claims with the same property are kept
item.claims.add(data)

# To replace all the existing claims with the same property instead, use ActionIfExists.REPLACE_ALL
# item.claims.add(data, action_if_exists=ActionIfExists.REPLACE_ALL)

item.write()

A bot for Mass Import#

An enhanced example of the previous bot puts the creation of the data and the entity into a ‘for loop’ and so allows mass creation or modification of items.

from wikibaseintegrator import WikibaseIntegrator, wbi_login
from wikibaseintegrator.datatypes import ExternalID, Item, String, Time
from wikibaseintegrator.wbi_config import config as wbi_config
from wikibaseintegrator.wbi_enums import WikibaseTimePrecision

wbi_config['USER_AGENT'] = 'MyWikibaseBot/1.0 (https://www.wikidata.org/wiki/User:MyUsername)'

# login object
login_instance = wbi_login.OAuth2(consumer_token='<consumer_token>', consumer_secret='<consumer_secret>')

# We have raw data, which should be written to Wikidata, namely two human NCBI entrez gene IDs mapped to two Ensembl Gene IDs
raw_data = {
    '50943': 'ENST00000376197',
    '1029': 'ENST00000498124'
}

wbi = WikibaseIntegrator(login=login_instance)

for entrez_id, ensembl in raw_data.items():
    # add some references
    references = [
        [
            Item(value='Q20641742', prop_nr='P248'),
            Time(time='+2020-02-08T00:00:00Z', prop_nr='P813', precision=WikibaseTimePrecision.DAY),
            ExternalID(value='1017', prop_nr='P351')
        ]
    ]

    # data type object
    entrez_gene_id = String(value=entrez_id, prop_nr='P351', references=references)
    ensembl_transcript_id = String(value=ensembl, prop_nr='P704', references=references)

    # data goes into a list, because many data objects can be provided to
    data = [entrez_gene_id, ensembl_transcript_id]

    # Search for and then edit/create new item
    item = wbi.item.new()
    item.claims.add(data)
    item.write()

Replace a property by another#

This bot moves every statement using a property (OLD_PROPERTY) to another property with the same datatype (NEW_PROPERTY), on all the entities of a Wikibase instance. Each statement is copied with its value, qualifiers, references and rank to the new property, then the original statement is removed, in a single edit per entity.

from copy import deepcopy

from wikibaseintegrator import WikibaseIntegrator, wbi_login
from wikibaseintegrator.wbi_config import config as wbi_config
from wikibaseintegrator.wbi_helpers import execute_sparql_query, generate_entity_instances

wbi_config['USER_AGENT'] = 'MyWikibaseBot/1.0 (https://www.wikidata.org/wiki/User:MyUsername)'

OLD_PROPERTY = 'P1234'  # The property to replace
NEW_PROPERTY = 'P5678'  # The property to use instead
DRY_RUN = True  # Set to False to write the changes

# login object
login_instance = wbi_login.OAuth2(consumer_token='<consumer_token>', consumer_secret='<consumer_secret>')

wbi = WikibaseIntegrator(login=login_instance)

# The values are copied as they are, so both properties must use the same datatype
old_datatype = wbi.property.get(OLD_PROPERTY).datatype
new_datatype = wbi.property.get(NEW_PROPERTY).datatype
if old_datatype != new_datatype:
    raise ValueError(f'{OLD_PROPERTY} ({old_datatype}) and {NEW_PROPERTY} ({new_datatype}) use different datatypes')

# Find the entities holding at least one statement with the old property
query = f'SELECT DISTINCT ?entity WHERE {{ ?entity <{wbi_config["WIKIBASE_URL"]}/prop/{OLD_PROPERTY}> ?statement . }}'
results = execute_sparql_query(query)
entity_ids = [result['entity']['value'].rsplit('/', 1)[-1] for result in results['results']['bindings']]
# The statements of the forms and senses of a lexeme (L1-F1, L1-S1) are not handled by this example
entity_ids = [entity_id for entity_id in entity_ids if '-' not in entity_id]
print(f'{len(entity_ids)} entities use {OLD_PROPERTY}')

# Retrieve the entities 50 by 50, the maximum of the wbgetentities API action
for start in range(0, len(entity_ids), 50):
    for entity_id, entity in generate_entity_instances(entities=entity_ids[start:start + 50], login=login_instance):
        for claim in entity.claims.get(OLD_PROPERTY):
            # Copy the statement to the new property, as a new statement
            new_claim = deepcopy(claim)
            new_claim.reset_id()
            new_claim.mainsnak.property_number = NEW_PROPERTY
            # Not added twice if the entity already holds the same value with the new property
            entity.claims.add(new_claim)

            # Remove the original statement
            claim.remove()

        if DRY_RUN:
            print(f'{entity_id}: {len(entity.claims.get(OLD_PROPERTY))} statement(s) to move')
        else:
            entity.write(summary=f'Replace {OLD_PROPERTY} by {NEW_PROPERTY}', is_bot=True)

[!NOTE] The SPARQL endpoint can lag behind the live data: run the bot again until no entity is found. This example only moves the main statements: the qualifiers and references using the old property are left untouched.