http://www.mediawiki.org/wiki/Special:Code/pywikipedia/11182
Revision: 11182 Author: legoktm Date: 2013-03-06 13:30:42 +0000 (Wed, 06 Mar 2013) Log Message: ----------- Add initial implementation of WikibasePage (more detailed post to ML will folow)
*This creates a base WikibasePage, ItemPage, PropertyPage, QueryPage, and Claim classes. *Currently only read access is availible, write access will follow. *Existing methods using DataSite class are not being removed, but will be phased out in the future.
Modified Paths: -------------- branches/rewrite/pywikibot/__init__.py branches/rewrite/pywikibot/page.py
Modified: branches/rewrite/pywikibot/__init__.py =================================================================== --- branches/rewrite/pywikibot/__init__.py 2013-03-06 08:13:25 UTC (rev 11181) +++ branches/rewrite/pywikibot/__init__.py 2013-03-06 13:30:42 UTC (rev 11182) @@ -178,7 +178,7 @@ getSite = Site # alias for backwards-compability
-from page import Page, ImagePage, Category, Link, User +from page import Page, ImagePage, Category, Link, User, ItemPage, PropertyPage, Claim from page import html2unicode, url2unicode
Modified: branches/rewrite/pywikibot/page.py =================================================================== --- branches/rewrite/pywikibot/page.py 2013-03-06 08:13:25 UTC (rev 11181) +++ branches/rewrite/pywikibot/page.py 2013-03-06 13:30:42 UTC (rev 11182) @@ -2184,6 +2184,271 @@ yield ImagePage(self.site, item.title().title()), \ unicode(item.timestamp()), item.comment(), item.pageid() > 0
+class WikibasePage(Page): + """ + The base page for the Wikibase extension. + There really should be no need to call this directly + """ + def __init__(self, site, title=u""): + Page.__init__(self, site, title) + if isinstance(self.site, pywikibot.site.DataSite): + self.repo = self.site + else: + self.repo = self.site.data_repository() + + def __defined_by(self): + """ + returns the parameters needed by the API + to identify an item. + Once an item's "p/q##" is looked up, that + will be used for all future requests. + """ + params = {} + #id overrides all + if hasattr(self, 'id'): + params['ids'] = self.id + return params + + #the rest only applies to ItemPages, but is still needed here. + + if isinstance(self.site, pywikibot.site.DataSite): + params['ids'] = self.title(withNamespace=False) + elif isinstance(self.site, pywikibot.site.BaseSite): + params['sites'] = self.site.dbName() + params['titles'] = self.title() + else: + raise pywikibot.exceptions.BadTitle + return params + + def get(self, force=False, *args): + """ + Fetches all page data, and caches it + force will override caching + args can be used to specify custom props. + """ + if force or not hasattr(self, '_content'): + params = dict(**self.__defined_by()) + params['action'] = 'wbgetentities' + if args: + params['props'] = '|'.join(args) + #print params + req = pywikibot.data.api.Request(site=self.repo, **params) + data = req.submit() + if not 'success' in data: + raise pywikibot.data.api.APIError, data['errors'] + self.id = data['entities'].keys()[0] + self._content = data['entities'][self.id] + #aliases + self.aliases = {} + if 'aliases' in self._content: + for lang in self._content['aliases']: + self.aliases[lang] = list() + for value in self._content['aliases'][lang]: + self.aliases[lang].append(value['value']) + + #labels + self.labels = {} + if 'labels' in self._content: + for lang in self._content['labels']: + self.labels[lang] = self._content['labels'][lang]['value'] + + #descriptions + self.descriptions = {} + if 'descriptions' in self._content: + for lang in self._content['descriptions']: + self.descriptions[lang] = self._content['descriptions'][lang]['value'] + + return {'aliases':self.aliases, + 'labels':self.labels, + 'descriptions':self.descriptions, + } + + + + def save(self, summary, **kwargs): + """ + Save whatever we added/removed/etc. + """ + raise NotImplementedError + + +class ItemPage(WikibasePage): + def __init__(self, site, title=None): + """ + defined by qid XOR site AND title + options: + site=pywikibot.DataSite & title=Q42 + site=pywikibot.Site & title=Main Page + """ + WikibasePage.__init__(self, site, title) + + @staticmethod + def fromPage(page): + """ + Get the ItemPage based on a Page that links to it + """ + return ItemPage(page.site, page.title()) + + def __make_site(self, dbname): + """ + Converts a Site.dbName() into a Site object. + Rather hackish method that only works for WMF sites + """ + lang = dbname.replace('wiki','') + lang = lang.replace('_','-') + return pywikibot.Site(lang, 'wikipedia') + + def get(self, force=False, *args): + """ + Fetches all page data, and caches it + force will override caching + args are the values of props + """ + if force or not hasattr(self, '_content'): + WikibasePage.get(self, force=force, *args) + + #claims + self.claims = {} + if 'claims' in self._content: + for pid in self._content['claims']: + self.claims[pid] = list() + for claim in self._content['claims'][pid]: + self.claims[pid].append(Claim.fromJSON(self.repo, claim)) + + #sitelinks + self.sitelinks = {} + if 'sitelinks' in self._content: + for dbname in self._content['sitelinks']: + #Due to issues with locked/obsolete sites + #this part is commented out + #site = self.__make_site(dbname) + #self.sitelinks[site] = pywikibot.Page(site, self._content['sitelinks'][dbname]['title']) + self.sitelinks[dbname] = self._content['sitelinks'][dbname]['title'] + + return {'aliases': self.aliases, + 'labels': self.labels, + 'descriptions': self.descriptions, + 'sitelinks': self.sitelinks, + 'claims': self.claims + } + + def get_sitelink(self, site, force=False): + """ + Returns a page object for the specific site + site is a pywikibot.Site + force will override caching + If the item doesn't have that language, raise NoPage + """ + if force or not hasattr(self, '_content'): + self.get(force=force) + dbname = site.dbName() + if not dbname in self.sitelinks: + raise pywikibot.NoPage + else: + return self.sitelinks[dbname] + + +class PropertyPage(WikibasePage): + """ + Any page in the property namespace + Should be created as: + PropertyPage(DataSite, 'Property:P21') + """ + def __init__(self, source, title=u""): + WikibasePage.__init__(self, source, title) + self.id = self.title(withNamespace=False).lower() + if not self.id.startswith(u'p'): + raise ValueError(u"'%s' is not a property page!" % self.title()) + + def get_type(self): + """ + Returns the type that this item uses + Examples: item, commons media file, StringValue, NumericalValue + """ + raise NotImplementedError + +class QueryPage(WikibasePage): + """ + For future usage, not implemented yet + """ + def __init__(self, site, title): + WikibasePage.__init__(self, site, title) + raise NotImplementedError + + +class Claim(PropertyPage): + """ + Claims are standard claims as well as references. + """ + def __init__(self, site, pid, snak=None, isReference=False): + """ + Defined by the "snak" value, supplemented by site + pid + """ + PropertyPage.__init__(self, site, 'Property:'+pid) + self.snak = snak + self.isReference = isReference + self.sources = [] + self.target = None + + @staticmethod + def fromJSON(site, data): + """ + Creates the claim object from JSON returned + in the API call. + """ + claim = Claim(site, data['mainsnak']['property']) + if 'id' in data: + claim.snak = data['id'] + else: + claim.isReference = True + if data['mainsnak']['datavalue']['type'] == 'wikibase-entityid': + claim.target = ItemPage(site, 'Q' + + str(data['mainsnak']['datavalue']['value']['numeric-id'])) + else: + claim.target = data['mainsnak']['datavalue']['value'] + if 'references' in data: + for source in data['references']: + claim.sources.append(Claim.referenceFromJSON(site, source['snaks'].values()[0][0])) + return claim + + @staticmethod + def referenceFromJSON(site, data): + """ + This is a simple hack since reference objects + aren't wrapped in a mainsnak object. + """ + wrap = {'mainsnak': data} + return Claim.fromJSON(site, wrap) + + def set_target(self, value): + """ + Sets the target to the passed value. + There should be checks to ensure type compliance + """ + self.target = value + + def get_target(self): + """ + Returns object that the property is associated with. + None is returned if no target is set + """ + return self.target + + def get_sources(self): + """ + Returns a list of Claims + """ + return self.sources + + def add_source(self, source): + """ + source is a Claim. + adds it as a reference. + """ + raise NotImplementedError + + + class Revision(object): """A structure holding information about a single revision of a Page.""" def __init__(self, revid, timestamp, user, anon=False, comment=u"",