# SPDX-License-Identifier: AGPL-3.0-or-later """ Typification of the *answer* results. Results of this type are rendered in the :origin:`answers.html ` template. ---- .. autoclass:: BaseAnswer :members: :show-inheritance: .. autoclass:: Answer :members: :show-inheritance: .. autoclass:: Translations :members: :show-inheritance: .. autoclass:: Weather :members: :show-inheritance: .. autoclass:: AnswerSet :members: :show-inheritance: """ # pylint: disable=too-few-public-methods from __future__ import annotations __all__ = ["AnswerSet", "Answer", "Translations", "Weather"] import msgspec from ._base import Result class BaseAnswer(Result, kw_only=True): """Base class of all answer types. It is not intended to build instances of this class (aka *abstract*).""" class AnswerSet: """Aggregator for :py:obj:`BaseAnswer` items in a result container.""" def __init__(self): self._answerlist = [] def __len__(self): return len(self._answerlist) def __bool__(self): return bool(self._answerlist) def add(self, answer: BaseAnswer) -> None: a_hash = hash(answer) for i in self._answerlist: if hash(i) == a_hash: return self._answerlist.append(answer) def __iter__(self): """Sort items in this set and iterate over the items.""" self._answerlist.sort(key=lambda answer: answer.template) yield from self._answerlist def __contains__(self, answer: BaseAnswer) -> bool: a_hash = hash(answer) for i in self._answerlist: if hash(i) == a_hash: return True return False class Answer(BaseAnswer, kw_only=True): """Simple answer type where the *answer* is a simple string with an optional :py:obj:`url field ` field to link a resource (article, map, ..) related to the answer.""" template: str = "answer/legacy.html" answer: str """Text of the answer.""" def __hash__(self): """The hash value of field *answer* is the hash value of the :py:obj:`Answer` object. :py:obj:`Answer ` objects are equal, when the hash values of both objects are equal.""" return hash(self.answer) class Translations(BaseAnswer, kw_only=True): """Answer type with a list of translations. The items in the list of :py:obj:`Translations.translations` are of type :py:obj:`Translations.Item`: .. code:: python def response(resp): results = [] ... foo_1 = Translations.Item( text="foobar", synonyms=["bar", "foo"], examples=["foo and bar are placeholders"], ) foo_url="https://www.deepl.com/de/translator#en/de/foo" ... Translations(results=results, translations=[foo], url=foo_url) """ template: str = "answer/translations.html" """The template in :origin:`answer/translations.html `""" translations: list[Translations.Item] """List of translations.""" def __post_init__(self): if not self.translations: raise ValueError("Translation does not have an item in the list translations") class Item(msgspec.Struct, kw_only=True): """A single element of the translations / a translation. A translation consists of at least a mandatory ``text`` property (the translation) , optional properties such as *definitions*, *synonyms* and *examples* are possible.""" text: str """Translated text.""" transliteration: str = "" """Transliteration_ of the requested translation. .. _Transliteration: https://en.wikipedia.org/wiki/Transliteration """ examples: list[str] = [] """List of examples for the requested translation.""" definitions: list[str] = [] """List of definitions for the requested translation.""" synonyms: list[str] = [] """List of synonyms for the requested translation.""" class Weather(BaseAnswer, kw_only=True): """Answer type for weather data.""" template: str = "answer/weather.html" """The template is located at :origin:`answer/weather.html `""" location: str """The geo-location the weather data is from (e.g. `Berlin, Germany`).""" current: Weather.DataItem """Current weather at ``location``.""" forecasts: list[Weather.DataItem] = [] """Weather forecasts for ``location``.""" def __post_init__(self): if not self.location: raise ValueError("Weather answer is missing a location") class DataItem(msgspec.Struct, kw_only=True): """A container for weather data such as temperature, humidity, ...""" time: str | None = None """Time of the forecast - not needed for the current weather.""" condition: str """Weather condition, e.g. `cloudy`, `rainy`, `sunny` ...""" temperature: str """Temperature string, e.g. `17°C`""" feelsLike: str | None = None """Felt temperature string, should be formatted like ``temperature``""" humidity: str | None = None """Humidity percentage string, e.g. `60%`""" pressure: str | None = None """Pressure string, e.g. `1030hPa`""" wind: str | None = None """Information about the wind, e.g. `W, 231°, 10 m/s`""" attributes: dict[str] = [] """Key-Value dict of additional weather attributes that are not available above"""