# ----------
# TODO
# whenever acessing WD, get the valid snak for, it there are multiple snaks with different start & end dates
# Setting an end date seems not to work, if there is already a source set (different from the one passing)
# WA: retrieve the source and use it again to simulate identical values
# change the Kfz when moving municipalities to different districts
# is using quick_statements smart enough?
# ----------
    
# This script creates the imput for https://tools.wmflabs.org/wikidata-todo/quick_statements.php
# to change administrative structure as defined by legal changes in Austria
#
# the script creates commands for municipalites and districts to reflect the legal change in WD
# * for each municipality:
# ** assigns new GKZs (Gemeindekennziffer, P964) by due date and terminates the old GKZ
# ** assigns municipality objects a new parent administrative unit, i.e. district (P131) with the due date
#    and terminates the old municipality assignment
# ** change the Kfz-Kennzeichen from the old district's to the new district's
# ** for the old district terminate the municipality assignment by due date -1 (P150)
# ** for the new district create a municipality assignment by due date (P150)
#

# preconditions:
# * the target district must exist in WD (create manually if the district is new)
# * the source district, if it ceases to exist, has to be terminated manually
# * municipalities are identified by their articles on the German WP
# * define the data reference for the changes as a WD object manually. This is the source for all the changes needed
#   see https://www.wikidata.org/wiki/Q27906384 as an example
# 
dataRef = "Q27906384"

import pywikibot
import re
import math
import time
import csv
from datetime import date, datetime, timedelta
from enum import Enum
from pywikibot import pagegenerators, data

# define the data
# due date
dueDate = datetime(2017,1,1)
termDate = dueDate-timedelta(days=1)

# input data as tab separated csv entries with heading in first row
# see data/WU-change.csv as the primary example
# heading:
# municipality	WP article	GKZ old	GKZ new	district old	district new
requestedChangesInputCSV = "data/WU-change.csv"

dewiki   = pywikibot.Site('de')
wikidata = pywikibot.Site('wikidata', fam='wikidata')
repo     = wikidata.data_repository()

# WD properties
P_Gemeindekennziffer = "P964" # Gemeindekennziffer in Österreich
P_Untereinheit = "P150" # Untereinheit (administrative Einheit)
P_Verwaltungseinheit = "P131" # liegt in der Verwaltungseinheit
P_KfzKennzeichen = "P395" # Kfz-Kennzeichen; data not complete, skip this
P_Startzeitpunkt = "P580"# Qualifier Startzeitpunkt
P_Endezeitpunkt = "P582" # Qualifier Endezeitpunkt
S_nachgewiesenIn = "S248" # nachgewiesen in

# the main data structure, read from csv file and augmented from various sources
requestedChanges = []
with open(requestedChangesInputCSV, 'r') as csvfile:
    reader = csv.DictReader(csvfile, delimiter='\t', quotechar='"')
    for row in reader:
        requestedChanges.append(row)

# as a performance optimization extract all the discticts and get information only once
districts = {} # a set of k=name, v={Q id, Kfz}
for r in requestedChanges:
    districts[r['district old']]={}
    districts[r['district new']]={}
#print (districts)

allObjectsFound = True # we will not continue otherwise
# get the WD Q ids for all the districts involved
for pageName in districts.keys():
    page = pywikibot.Page(dewiki, pageName) # get page
    
    if page.exists():
        if page.isDisambig():
            print ("*** %s is disambiguation" % page.title())
            allObjectsFound = False
            continue
            
        if page.isRedirectPage():
            print (">>> %s is redirect" % page.title())
            page = page.getRedirectTarget()
        
        item = pywikibot.ItemPage.fromPage(page)
        print (">>> %s: %s" % (page.title(),item.title()))
        districts[pageName]={'Qid':item.title()}
        item.get()
        if item.claims and P_KfzKennzeichen in item.claims: 
            for v in item.claims[P_KfzKennzeichen]:
                districts[pageName]['Kfz']=v.target
    else:
        print ("*** %s not found" % page.title())
        allObjectsFound = False

#print (districts)

# get the WD Q ids for all the municipalities involved
for r in requestedChanges:
    pageName = r['municipality']
    page = pywikibot.Page(dewiki, pageName) # get page
    if page.exists():
        if page.isDisambig():
            print ("*** %s is disambiguation" % page.title())
            allObjectsFound = False
            continue

        if page.isRedirectPage():
            print (">>> %s is redirect" % page.title())
            page = page.getRedirectTarget()

        item = pywikibot.ItemPage.fromPage(page)
        print (">>> %s: %s" % (page.title(),item.title()))
        r['Qid'] = item.title()
    else:
        print ("*** %s not found" % page.title())
        allObjectsFound = False

#print (requestedChanges)

assert (allObjectsFound)

# +1967-01-17T00:00:00Z/11 # WD format, precision 1 day
dueDateFormatted = dueDate.strftime(r"+%Y-%m-%dT00:00:00Z/11")
termDateFormatted = termDate.strftime(r"+%Y-%m-%dT00:00:00Z/11")

# create data for quick_statements
print ("\nCommands for https://tools.wmflabs.org/wikidata-todo/quick_statements.php\n\n")
# Q4115189 TAB P31 TAB Q1 TAB P580 TAB +1840-01-01T00:00:00Z/09 TAB S143 TAB Q48183
for r in requestedChanges:
    # new GKZ
    print ("%s\t%s\t\"%s\"\t%s\t%s\t%s\t%s" % (r['Qid'], P_Gemeindekennziffer, r['GKZ new'],
                                               P_Startzeitpunkt, dueDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))
    # terminate old GKZ
    print ("%s\t%s\t\"%s\"\t%s\t%s\t%s\t%s" % (r['Qid'], P_Gemeindekennziffer, r['GKZ old'],
                                               P_Endezeitpunkt, termDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))

# ** assigns municipality objects a new parent administrative unit, i.e. district (P131) with the due date
#    and terminates the old municipality assignment
    print ("%s\t%s\t%s\t%s\t%s\t%s\t%s" % (r['Qid'], P_Verwaltungseinheit, districts[r['district old']]['Qid'],
                                               P_Endezeitpunkt, termDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))
    print ("%s\t%s\t%s\t%s\t%s\t%s\t%s" % (r['Qid'], P_Verwaltungseinheit, districts[r['district new']]['Qid'],
                                               P_Startzeitpunkt, dueDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))
    print ("%s\t%s\t%s\t%s\t%s\t%s\t%s" % (districts[r['district old']]['Qid'], P_Untereinheit, r['Qid'], 
                                               P_Endezeitpunkt, termDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))
    print ("%s\t%s\t%s\t%s\t%s\t%s\t%s" % (districts[r['district new']]['Qid'], P_Untereinheit, r['Qid'], 
                                               P_Startzeitpunkt, dueDateFormatted,
                                               S_nachgewiesenIn, dataRef
                                              ))


    print ("\n")
    
print ("\nfinished")
>>> Bezirk Tulln: Q694428
>>> Bezirk St. Pölten-Land: Q694393
>>> Bezirk Bruck an der Leitha: Q584543
>>> Bezirk Wien-Umgebung: Q521869
>>> Bezirk Korneuburg: Q694372
>>> Ebergassing: Q674130
>>> Fischamend: Q594276
>>> Gablitz: Q665086
>>> Gerasdorf bei Wien: Q664293
>>> Gramatneusiedl: Q673374
>>> Himberg: Q673406
>>> Klein-Neusiedl: Q673535
>>> Klosterneuburg: Q487522
>>> Lanzendorf (Niederösterreich): Q691554
>>> Leopoldsdorf (Bezirk Wien-Umgebung) is redirect
>>> Leopoldsdorf (Bezirk Bruck an der Leitha): Q259258
>>> Maria-Lanzendorf: Q675952
>>> Mauerbach: Q700542
>>> Moosbrunn: Q672316
>>> Pressbaum: Q666577
>>> Purkersdorf: Q590620
>>> Rauchenwarth: Q672168
>>> Schwadorf: Q673507
>>> Schwechat: Q489972
>>> Tullnerbach: Q672331
>>> Wolfsgraben: Q673520
>>> Zwölfaxing: Q245210

Commands for https://tools.wmflabs.org/wikidata-todo/quick_statements.php


Q674130	P964	"30729"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q674130	P964	"32401"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q674130	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q674130	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q674130	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q674130	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q594276	P964	"30730"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q594276	P964	"32402"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q594276	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q594276	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q594276	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q594276	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q665086	P964	"31949"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q665086	P964	"32403"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q665086	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q665086	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q665086	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q665086	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q664293	P964	"31235"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q664293	P964	"32404"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q664293	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q664293	P131	Q694372	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q664293	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694372	P150	Q664293	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q673374	P964	"30731"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q673374	P964	"32405"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673374	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673374	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q673374	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q673374	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q673406	P964	"30732"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q673406	P964	"32406"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673406	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673406	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q673406	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q673406	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q673535	P964	"30733"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q673535	P964	"32407"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673535	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673535	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q673535	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q673535	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q487522	P964	"32144"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q487522	P964	"32408"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q487522	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q487522	P131	Q694428	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q487522	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694428	P150	Q487522	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q691554	P964	"30734"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q691554	P964	"32409"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q691554	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q691554	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q691554	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q691554	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q259258	P964	"30735"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q259258	P964	"32410"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q259258	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q259258	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q259258	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q259258	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q675952	P964	"30736"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q675952	P964	"32411"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q675952	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q675952	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q675952	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q675952	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q700542	P964	"31950"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q700542	P964	"32412"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q700542	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q700542	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q700542	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q700542	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q672316	P964	"30737"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q672316	P964	"32413"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672316	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672316	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q672316	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q672316	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q666577	P964	"31951"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q666577	P964	"32415"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q666577	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q666577	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q666577	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q666577	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q590620	P964	"31952"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q590620	P964	"32416"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q590620	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q590620	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q590620	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q590620	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q672168	P964	"30738"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q672168	P964	"32417"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672168	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672168	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q672168	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q672168	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q673507	P964	"30739"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q673507	P964	"32418"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673507	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673507	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q673507	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q673507	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q489972	P964	"30740"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q489972	P964	"32419"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q489972	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q489972	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q489972	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q489972	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q672331	P964	"31953"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q672331	P964	"32421"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672331	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q672331	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q672331	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q672331	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q673520	P964	"31954"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q673520	P964	"32423"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673520	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q673520	P131	Q694393	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q673520	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q694393	P150	Q673520	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384


Q245210	P964	"30741"	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q245210	P964	"32424"	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q245210	P131	Q521869	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q245210	P131	Q584543	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384
Q521869	P150	Q245210	P582	+2016-12-31T00:00:00Z/11	S248	Q27906384
Q584543	P150	Q245210	P580	+2017-01-01T00:00:00Z/11	S248	Q27906384



finished
from pywikibot import pagegenerators, data
pywikibot.WbTime(2016,10,31)
item.between(P_KfzKennzeichen, begin=pywikibot.WbTime(2016,10,31))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-58-4c289702ed38> in <module>()
      1 from pywikibot import pagegenerators, data
      2 pywikibot.WbTime(2016,10,31)
----> 3 item.between(P_KfzKennzeichen, begin=pywikibot.WbTime(2016,10,31))

/srv/paws/lib/python3.4/site-packages/pywikibot/page.py in __getattribute__(self, name)
   3419                 'WikibasePage.lastrevid', 'latest_revision_id', 2)
   3420             name = '_revid'
-> 3421         return super(WikibasePage, self).__getattribute__(name)
   3422 
   3423     def __setattr__(self, attr, value):

AttributeError: 'ItemPage' object has no attribute 'between'