# -*- coding: utf-8 -*-
import abc
import arrow
[docs]class ResultSet(list):
"""A list like object that holds results from an Annict API query."""
def __init__(self, total_count, prev_page=None, next_page=None):
super().__init__()
self.total_count = total_count
self.prev_page = prev_page
self.next_page = next_page
[docs]class Model(metaclass=abc.ABCMeta):
"""Abstract class of each models."""
def __init__(self, api=None):
self._api = api
self._children = []
[docs] @classmethod
@abc.abstractmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance."""
raise NotImplementedError
[docs] @classmethod
def parse_list(cls, api, json, payload_type):
"""Parse JSON objects into list of model instances.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:param str payload_type: Type of payload.
:return: list of Model objects.
:rtype: ResultSet
"""
results = ResultSet(
total_count=json["total_count"],
prev_page=json["prev_page"],
next_page=json["next_page"],
)
results._json = json
if payload_type == "activity":
pluralized_payload_name = "activities"
elif payload_type == "person":
pluralized_payload_name = "people"
else:
pluralized_payload_name = "{}s".format(payload_type)
for obj in json[pluralized_payload_name]:
if obj:
results.append(cls.parse(api, obj))
return results
[docs]class User(Model):
"""User information model"""
def __repr__(self):
return f"<User:{self.id}:{self.name}:@{self.username}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`User <User>` object
:rtype: User
"""
user = cls(api)
user._json = json
for k, v in json.items():
if k == "created_at":
setattr(user, k, arrow.get(v).datetime)
else:
setattr(user, k, v)
return user
[docs] def following(self, fields=None, page=None, per_page=None, sort_id=None):
"""Get following information of this user.
:param fields: (optional) Narrow down the fields of data contained in the response body.
:type fields: list of str
:param int page: (optional) Specify the number of pages.
:param int per_page: (optional) Specify how many items to acquire per page.
:param str sort_id: (optional) Sort the results by their ID. You can specify `asc` or `desc`.
:return: list of :class:`User <annict.models.User>` objects.
:rtype: annict.models.ResultSet
"""
return self._api.following(
fields=fields,
filter_user_id=self.id,
page=page,
per_page=per_page,
sort_id=sort_id,
)
[docs] def followers(self, fields=None, page=None, per_page=None, sort_id=None):
"""Get following information of this user.
:param fields: (optional) Narrow down the fields of data contained in the response body.
:type fields: list of str
:param int page: (optional) Specify the number of pages.
:param int per_page: (optional) Specify how many items to acquire per page.
:param str sort_id: (optional) Sort the results by their ID. You can specify `asc` or `desc`.
:return: list of :class:`User <annict.models.User>` objects.
:rtype: annict.models.ResultSet
"""
return self._api.followers(
fields=fields,
filter_user_id=self.id,
page=page,
per_page=per_page,
sort_id=sort_id,
)
[docs]class Work(Model):
"""Work information model"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._episodes = None
def __repr__(self):
return f"<Work:{self.id}:{self.title}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Work <Work>` object
:rtype: Work
"""
work = cls(api)
work._json = json
for k, v in json.items():
if k == "released_on":
if v:
date = arrow.get(v).date()
else:
date = None
setattr(work, k, date)
else:
setattr(work, k, v)
return work
[docs] def set_status(self, kind):
"""Set the status of the work.
:param str kind: Types of status.
You can specify `wanna_watch`, `watching`, `watched`, `on_hold`, `stop_watching`, or `no_select`.
:return: Returns `True` if deletion succeeded.
"""
return self._api.set_status(self.id, kind)
[docs] def episodes(
self,
fields=None,
filter_ids=None,
page=None,
per_page=None,
sort_id=None,
sort_sort_number=None,
):
"""Get episodes information
:reference: https://docs.annict.com/ja/api/v1/episodes.html
:param fields: (optional) Narrow down the fields of data contained in the response body.
:type fields: list of str
:param filter_ids: (optional) Filter results by IDs.
:type filter_ids: list of int
:param int page: (optional) Specify the number of pages.
:param int per_page: (optional) Specify how many items to acquire per page.
:param str sort_id: (optional) Sort the results by their ID. You can specify `asc` or `desc`.
:param str sort_sort_number: (optional) Sort by number for sorting. You can specify `asc` or `desc`.
:return: list of :class:`Episode <annict.models.Episode>` objects.
:rtype: annict.models.ResultSet
"""
return self._api.episodes(
fields=fields,
filter_ids=filter_ids,
filter_work_id=self.id,
page=page,
per_page=per_page,
sort_id=sort_id,
sort_sort_number=sort_sort_number,
)
[docs] def select_episodes(self, *numbers):
"""Select multiple episodes
:param numbers: Episode number.
:return: list of :class:`Episode <Episode>`
:rtype: list of :class:`Episode <Episode>`
"""
if not self._episodes:
self._episodes = self.episodes(sort_sort_number="asc")
if not numbers:
return self._episodes
return [self._episodes[n - 1] for n in numbers]
[docs] def get_episode(self, number):
"""Get Episode object
:param number: Episode number
:return: :class:`Episode <Episode>` object
:rtype: Episode
"""
return self.select_episodes(number)[0]
[docs]class Episode(Model):
"""Episode information model"""
def __repr__(self):
return f"<Episode:{self.id}:{self.number_text}:{self.title}:{self.work.title}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Episode <Episode>` object
:rtype: Episode
"""
episode = cls(api)
episode._json = json
for k, v in json.items():
if k == "episode":
setattr(episode, "episode_id", v)
elif k == "work":
work = Work.parse(api, v)
setattr(episode, k, work)
elif k == "prev_episode" and v:
prev_episode = cls.parse(api, v)
setattr(episode, k, prev_episode)
elif k == "next_episode" and v:
next_episode = cls.parse(api, v)
setattr(episode, k, next_episode)
else:
setattr(episode, k, v)
return episode
[docs] def create_record(
self, comment=None, rating=None, share_twitter=False, share_facebook=False
):
"""Create a record for this episode.
:param str comment: (optional) Comment.
:param float rating: (optional) Rating.
:param bool share_twitter: (optional) Whether to share the record on Twitter.
You can enter `True` or `False`.
:param bool share_facebook: (optional) Whether to share the record on Facebook.
You can enter `True` or `False`.
:return: :class:`Record <annict.models.Record>` object.
"""
return self._api.create_record(
self.id, comment, rating, share_twitter, share_facebook
)
[docs]class Record(Model):
"""Record information model"""
def __repr__(self):
return f"<Record:{self.id}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Record <Record>` object
:rtype: Record
"""
record = cls(api)
record._json = json
for k, v in json.items():
if k == "created_at":
setattr(record, k, arrow.get(v).datetime)
elif k == "user":
user = User.parse(api, v)
setattr(record, k, user)
elif k == "work":
work = Work.parse(api, v)
setattr(record, k, work)
elif k == "episode":
episode = Episode.parse(api, v)
setattr(record, k, episode)
else:
setattr(record, k, v)
return record
[docs] def edit(
self, comment=None, rating=None, share_twitter=False, share_facebook=False
):
"""Edit the created record.
:param str comment: (optional) Comment.
:param float rating: (optional) Rating.
:param bool share_twitter: (optional) Whether to share the record on Twitter.
You can enter `True` or `False`.
:param bool share_facebook: (optional) Whether to share the record on Facebook.
You can enter `True` or `False`.
:return: :class:`Record <annict.models.Record>` object after edit.
"""
return self._api.edit_record(
self.id, comment, rating, share_twitter, share_facebook
)
[docs] def delete(self):
"""Delete the created record.
:return: Returns `True` if deletion succeeded.
"""
return self._api.delete_record(self.id)
[docs]class Program(Model):
"""Program information model"""
def __repr__(self):
return f"<Program:{self.id}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Program <Program>` object
:rtype: Program
"""
program = cls(api)
program._json = json
for k, v in json.items():
if k == "started_at":
setattr(program, k, arrow.get(v).datetime)
elif k == "work":
work = Work.parse(api, v)
setattr(program, k, work)
elif k == "episode":
episode = Episode.parse(api, v)
setattr(program, k, episode)
else:
setattr(program, k, v)
return program
[docs]class Activity(Model):
"""Activity information model"""
def __repr__(self):
return f"<Activity:{self.action}:@{self.user.username}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Activity <Activity>` object
:rtype: Activity
"""
activity = cls(api)
activity._json = json
for k, v in json.items():
if k == "created_at":
setattr(activity, k, arrow.get(v).datetime)
elif k == "user":
user = User.parse(api, v)
setattr(activity, k, user)
elif k == "work":
work = Work.parse(api, v)
setattr(activity, k, work)
elif k == "episode":
episode = Episode.parse(api, v)
setattr(activity, k, episode)
else:
setattr(activity, k, v)
return activity
[docs]class Person(Model):
"""Person information model such as cast and staff"""
def __repr__(self):
return f"<Person:{self.id}:{self.name}>"
[docs] @classmethod
def parse(cls, api, json):
"""Parse a JSON object into a model instance.
:param api: instance of :class:`API <annict.api.API>` .
:type api: annict.api.API
:param dict json: JSON from Annict API.
:return: :class:`Person <Person>` object
:rtype: Person
"""
person = cls(api)
person._json = json
for k, v in json.items():
if k == "birthday":
if v:
date = arrow.get(v).date()
else:
date = None
setattr(person, k, date)
else:
setattr(person, k, v)
return person
MODEL_MAPPING = {
"user": User,
"work": Work,
"episode": Episode,
"record": Record,
"program": Program,
"activity": Activity,
"person": Person,
}