Source code for wikibaseintegrator.entities.mediainfo
"""
MediaInfo entity, used by Structured Data on Commons (SDC).
.. warning::
Beta: Structured Data on Commons has no stable interface policy (https://phabricator.wikimedia.org/T149410) and its
API is not versioned, so it can change without notice. This module follows these changes and may change in a minor
release, without a deprecation period.
"""
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Any, Self
from wikibaseintegrator.entities.baseentity import BaseEntity, TermsEntity
from wikibaseintegrator.models.aliases import Aliases
from wikibaseintegrator.models.claims import Claims
from wikibaseintegrator.models.descriptions import Descriptions
from wikibaseintegrator.models.labels import Labels
from wikibaseintegrator.wbi_exceptions import MissingEntityException
from wikibaseintegrator.wbi_helpers import mediawiki_api_call_helper
if TYPE_CHECKING:
from wikibaseintegrator.wbi_login import _Login
[docs]
class MediaInfoEntity(TermsEntity):
"""
A MediaInfo entity (M-ID), holding the structured data of a media file hosted on Wikimedia Commons.
.. warning::
Beta: Structured Data on Commons has no stable interface policy, see the module documentation.
"""
ETYPE = 'mediainfo'
def __init__(self, labels: Labels | None = None, descriptions: Descriptions | None = None, aliases: Aliases | None = None, **kwargs: Any) -> None:
"""
:param api:
:param labels:
:param descriptions:
:param aliases:
:param sitelinks:
:param kwargs:
"""
super().__init__(labels=labels, descriptions=descriptions, aliases=aliases, **kwargs)
@BaseEntity.id.setter # type: ignore
def id(self, value: None | str | int) -> None:
if isinstance(value, str):
pattern = re.compile(r'^M?([0-9]+)$')
matches = pattern.match(value)
if not matches:
raise ValueError(f"Invalid MediaInfo ID ({value}), format must be 'M[0-9]+'")
value = f'M{matches.group(1)}'
elif isinstance(value, int):
value = f'M{value}'
elif value is None:
pass
else:
raise ValueError(f"Invalid MediaInfo ID ({value}), format must be 'M[0-9]+'")
BaseEntity.id.fset(self, value) # type: ignore
[docs]
def new(self, **kwargs: Any) -> MediaInfoEntity:
return MediaInfoEntity(api=self.api, **kwargs)
[docs]
def get(self, entity_id: str | int | None = None, **kwargs: Any) -> MediaInfoEntity:
"""
Request the MediaWiki API to get data for the entity specified in argument.
:param entity_id: The ID of the entity to load, starting with 'M'. By default, the ID of this entity, to reload it.
:param kwargs:
:return: a MediaInfoEntity instance
"""
if entity_id is None and self.id is not None:
entity_id = self.id
elif entity_id is None:
raise ValueError("You must provide an entity_id")
if isinstance(entity_id, str):
pattern = re.compile(r'^M?([0-9]+)$')
matches = pattern.match(entity_id)
if not matches:
raise ValueError(f"Invalid MediaInfo ID ({entity_id}), format must be 'M[0-9]+'")
entity_id = int(matches.group(1))
if entity_id < 1:
raise ValueError("MediaInfo ID must be greater than 0")
entity_id = f'M{entity_id}'
json_data = super()._get(entity_id=entity_id, **kwargs)
return MediaInfoEntity(api=self.api).from_json(json_data=json_data['entities'][entity_id])
[docs]
def get_by_title(self, titles: list[str] | str, sites: str = 'commonswiki', login: _Login | None = None, allow_anonymous: bool = True, is_bot: bool | None = None,
**kwargs: Any) -> MediaInfoEntity:
"""
Request the MediaWiki API to get the MediaInfo entity of a file, from the title of its page.
:param titles: The title of the file page, e.g. 'File:Example.jpg'
:param sites: The site of the page. Wikimedia Commons ('commonswiki') by default.
:param login: A login instance. The login of the WikibaseIntegrator instance by default.
:param allow_anonymous: Allow an anonymous request. Enabled by default.
:param is_bot: Add the bot flag to the request. The is_bot of the WikibaseIntegrator instance by default.
:param kwargs: More arguments for mediawiki_api_call_helper()
:return: a MediaInfoEntity instance
:exception MissingEntityException: If the page doesn't exist.
:exception ValueError: If the titles match more than one entity.
"""
if isinstance(titles, list):
titles = '|'.join(titles)
params = {
'action': 'wbgetentities',
'sites': sites,
'titles': titles,
'format': 'json'
}
login = login or self.api.login
is_bot = is_bot if is_bot is not None else self.api.is_bot
json_data = mediawiki_api_call_helper(data=params, login=login, allow_anonymous=allow_anonymous, is_bot=is_bot, **kwargs)
entities = json_data['entities']
# The API answers {'-1': {'title': ..., 'missing': ''}} for a page that doesn't exist
missing = [entity.get('title', entity_id) for entity_id, entity in entities.items() if 'missing' in entity]
if len(entities) == 0 or missing:
raise MissingEntityException(f"Title not found: {', '.join(missing) or titles}")
if len(entities) > 1:
raise ValueError('More than one element for this title')
return MediaInfoEntity(api=self.api).from_json(json_data=next(iter(entities.values())))
[docs]
def get_json(self) -> dict[str, str | dict]:
json_data = {
'labels': self.labels.get_json(),
'descriptions': self.descriptions.get_json(),
**super().get_json()
}
# MediaInfo calls the claims 'statements', and their main snaks have no datatype
statements = json_data.pop('claims')
assert isinstance(statements, dict)
for property_statements in statements.values():
for statement in property_statements:
statement['mainsnak'].pop('datatype', None)
json_data['statements'] = statements
return json_data
[docs]
def from_json(self, json_data: dict[str, Any]) -> Self:
super().from_json(json_data=json_data)
super()._terms_from_json(json_data=json_data)
if 'statements' in json_data:
self.claims = Claims().from_json(json_data['statements'])
return self
[docs]
def write(self, **kwargs: Any) -> MediaInfoEntity:
"""
Write the MediaInfoEntity data to the Wikibase instance and return the MediaInfoEntity object returned by the instance.
:param data: The serialized object that is used as the data source. A newly created entity will be assigned an 'id'.
:param summary: A summary of the edit
:param login: A login instance
:param allow_anonymous: Force a check if the query can be anonymous or not
:param clear: Clear the existing entity before updating
:param is_bot: Add the bot flag to the query
:param kwargs: More arguments for Python requests
:return: an MediaInfoEntity of the response from the instance
"""
json_data = super()._write(data=self.get_json(), **kwargs)
return self.from_json(json_data=json_data)