Пример #1
0
class MediaListItem(ModelMixin, db.Model):
    """
    Database model for media list items.
    This model maps MediaLists and MediaUserStates
    """

    __tablename__ = "media_list_items"
    """
    The name of the database table
    """

    __table_args__ = (db.UniqueConstraint("media_list_id",
                                          "media_user_state_id",
                                          name="unique_media_list_item"), )
    """
    Makes sure that objects that should be unique are unique
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    media_list_id: int = db.Column(db.Integer,
                                   db.ForeignKey("media_lists.id"),
                                   nullable=False)
    """
    The ID of the media list this list item is a part of
    """

    media_list: MediaList = db.relationship("MediaList",
                                            back_populates="media_list_items")
    """
    The media list this list item is a part of
    """

    media_user_state_id: int = db.Column(db.Integer,
                                         db.ForeignKey("media_user_states.id",
                                                       ondelete="CASCADE",
                                                       onupdate="CASCADE"),
                                         nullable=False)
    """
    The ID of the media user state this list item references
    """

    media_user_state: MediaUserState = db.relationship(
        "MediaUserState",
        backref=db.backref("media_list_items", lazy=True,
                           cascade="all,delete"))
    """
    The media user state this list item references
    """

    @property
    def identifier_tuple(self) -> Tuple[int, int]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.media_list_id, self.media_user_state_id

    def update(self, new_data: "MediaListItem"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.media_list_id = new_data.media_list_id
        self.media_user_state_id = new_data.media_user_state_id
Пример #2
0
class MediaUserState(ModelMixin, db.Model):
    """
    Database model that keeps track of a user's entries on external services
    for a media item
    """

    __tablename__ = "media_user_states"
    """
    The name of the database table
    """

    __table_args__ = (db.UniqueConstraint("media_id_id",
                                          "user_id",
                                          name="unique_media_user_state"), )
    """
    Makes sure that objects that should be unique are unique
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    media_id_id: int = db.Column(db.Integer,
                                 db.ForeignKey("media_ids.id"),
                                 nullable=False)
    """
    The ID of the media ID referenced by this user state
    """

    media_id: MediaId = db.relationship("MediaId",
                                        back_populates="media_user_states")
    """
    The media ID referenced by this user state
    """

    user_id: int = db.Column(db.Integer,
                             db.ForeignKey("users.id",
                                           ondelete="CASCADE",
                                           onupdate="CASCADE"),
                             nullable=False)
    """
    The ID of the user associated with this user state
    """

    user: User = db.relationship("User",
                                 backref=db.backref("media_user_states",
                                                    lazy=True,
                                                    cascade="all,delete"))
    """
    The user associated with this user state
    """

    progress: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The user's current progress consuming the media item
    """

    volume_progress: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The user's current 'volume' progress.
    """

    score: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The user's score for the references media item
    """

    consuming_state: ConsumingState \
        = db.Column(db.Enum(ConsumingState), nullable=False)
    """
    The current consuming state of the user for this media item
    """

    media_notification: Optional["MediaNotification"] = db.relationship(
        "MediaNotification",
        uselist=False,
        back_populates="media_user_state",
        cascade="all, delete")
    """
    Notification object for this user state
    """

    @property
    def identifier_tuple(self) -> Tuple[int, int]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.media_id_id, self.user_id

    def update(self, new_data: "MediaUserState"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.media_id_id = new_data.media_id_id
        self.user_id = new_data.user_id
        self.progress = new_data.progress
        self.volume_progress = new_data.volume_progress
        self.score = new_data.score
        self.consuming_state = new_data.consuming_state
Пример #3
0
class ServiceUsername(ModelMixin, db.Model):
    """
    Database model that stores an external service username for a user
    """

    __tablename__ = "service_usernames"
    """
    The name of the database table
    """

    __table_args__ = (db.UniqueConstraint("user_id",
                                          "username",
                                          "service",
                                          name="unique_service_username"), )
    """
    Makes sure that objects that should be unique are unique
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    user_id: int = db.Column(db.Integer,
                             db.ForeignKey("users.id",
                                           ondelete="CASCADE",
                                           onupdate="CASCADE"),
                             nullable=False)
    """
    The ID of the user associated with this service username
    """

    user: User = db.relationship("User",
                                 backref=db.backref("service_usernames",
                                                    lazy=True,
                                                    cascade="all,delete"))
    """
    The user associated with this service username
    """

    username: str = db.Column(db.String(255), nullable=False)
    """
    The service username
    """

    service: ListService = db.Column(db.Enum(ListService), nullable=False)
    """
    The external service this item is a username for
    """

    @property
    def identifier_tuple(self) -> Tuple[int, str, ListService]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.user_id, self.username, self.service

    def update(self, new_data: "ServiceUsername"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.user_id = new_data.user_id
        self.username = new_data.username
        self.service = new_data.service
Пример #4
0
class MediaId(ModelMixin, db.Model):
    """
    Database model for media IDs.
    These are used to map media items to their corresponding external
    IDS on external sites.
    """

    __tablename__ = "media_ids"
    """
    The name of the database table
    """

    __table_args__ = (
        db.UniqueConstraint("media_item_id",
                            "service",
                            "media_type",
                            name="unique_media_item_service_id"),
        db.UniqueConstraint("media_type",
                            "service",
                            "service_id",
                            name="unique_service_id"),
        db.ForeignKeyConstraint(
            ["media_item_id", "media_type", "media_subtype"], [
                "media_items.id", "media_items.media_type",
                "media_items.media_subtype"
            ]),
    )
    """
    Makes sure that objects that should be unique are unique
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    media_item_id: int = db.Column(db.Integer, nullable=False)
    """
    The ID of the media item referenced by this ID
    """

    media_item: MediaItem = db.relationship("MediaItem",
                                            back_populates="media_ids")
    """
    The media item referenced by this ID
    """

    media_type: MediaType = db.Column(db.Enum(MediaType), nullable=False)
    """
    The media type of the list item
    """

    media_subtype: MediaSubType = \
        db.Column(db.Enum(MediaSubType), nullable=False)
    """
    The media subtype of the list item
    """

    service_id: str = db.Column(db.String(255), nullable=False)
    """
    The ID of the media item on the external service
    """

    service: ListService = db.Column(db.Enum(ListService), nullable=False)
    """
    The service for which this object represents an ID
    """

    media_user_states: List["MediaUserState"] = db.relationship(
        "MediaUserState", back_populates="media_id", cascade="all, delete")
    """
    Media user states associated with this media ID
    """

    chapter_guess: Optional["MangaChapterGuess"] = db.relationship(
        "MangaChapterGuess",
        uselist=False,
        back_populates="media_id",
        cascade="all, delete")
    """
    Chapter Guess for this media ID (Only applicable if this is a manga title)
    """

    @property
    def service_url(self) -> str:
        """
        :return: The URL to the series for the given service
        """
        url_format = list_service_url_formats[self.service]
        url = url_format \
            .replace("@{media_type}", f"{self.media_type.value}") \
            .replace("@{id}", self.service_id)
        return url

    @property
    def service_icon(self) -> str:
        """
        :return: The path to the service's icon file
        """
        return url_for(
            "static",
            filename=f"images/service_logos/{self.service.value}.png")

    @property
    def identifier_tuple(self) -> Tuple[MediaType, ListService, str]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.media_type, self.service, self.service_id

    def update(self, new_data: "MediaId"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.media_item_id = new_data.media_item_id
        self.media_type = new_data.media_type
        self.service = new_data.service
        self.service_id = new_data.service_id
Пример #5
0
class MediaList(ModelMixin, db.Model):
    """
    Database model for user-specific media lists.
    """

    __tablename__ = "media_lists"
    """
    The name of the database table
    """

    __table_args__ = (
        db.UniqueConstraint(
            "name",
            "user_id",
            "service",
            "media_type",
            name="unique_media_list"
        ),
    )
    """
    Makes sure that objects that should be unique are unique
    """

    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    user_id: int = db.Column(
        db.Integer,
        db.ForeignKey(
            "users.id", ondelete="CASCADE", onupdate="CASCADE"
        ),
        nullable=False
    )
    """
    The ID of the user associated with this list
    """

    user: User = db.relationship(
        "User",
        backref=db.backref("media_lists", lazy=True, cascade="all,delete")
    )
    """
    The user associated with this list
    """

    name: str = db.Column(db.Unicode(255), nullable=False)
    """
    The name of this list
    """

    service: ListService = db.Column(db.Enum(ListService), nullable=False)
    """
    The service for which this list applies to
    """

    media_type: MediaType = db.Column(db.Enum(MediaType), nullable=False)
    """
    The media type for this list
    """

    media_list_items: List["MediaListItem"] = db.relationship(
        "MediaListItem", back_populates="media_list", cascade="all, delete"
    )
    """
    Media List Items that are a part of this media list
    """

    @property
    def identifier_tuple(self) -> Tuple[str, int, ListService, MediaType]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.name, self.user_id, self.service, self.media_type

    def update(self, new_data: "MediaList"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.user_id = new_data.user_id
        self.name = new_data.name
        self.service = new_data.service
        self.media_type = new_data.media_type
Пример #6
0
class MediaItem(ModelMixin, db.Model):
    """
    Database model for media items.
    These model a generic, site-agnostic representation of a series.
    """

    __tablename__ = "media_items"
    """
    The name of the database table
    """

    __table_args__ = (
        db.UniqueConstraint("media_type",
                            "media_subtype",
                            "romaji_title",
                            name="unique_media_item_data"),
        db.UniqueConstraint("id",
                            "media_type",
                            "media_subtype",
                            name="unique_media_item"),
    )
    """
    Makes sure that objects that should be unique are unique
    """
    def __init__(self, *args, **kwargs):
        """
        Initializes the Model
        :param args: The constructor arguments
        :param kwargs: The constructor keyword arguments
        """
        super().__init__(*args, **kwargs)

    media_type: MediaType = db.Column(db.Enum(MediaType), nullable=False)
    """
    The media type of the list item
    """

    media_subtype: MediaSubType = db.Column(db.Enum(MediaSubType),
                                            nullable=False)
    """
    The subtype (for example, TV short, movie oneshot etc)
    """

    english_title: Optional[str] = db.Column(db.Unicode(255), nullable=True)
    """
    The English title of the media item
    """

    romaji_title: str = db.Column(db.Unicode(255), nullable=False)
    """
    The Japanese title of the media item written in Romaji
    """

    cover_url: str = db.Column(db.String(255), nullable=False)
    """
    An URL to a cover image of the media item
    """

    latest_release: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The latest release chapter/episode for this media item
    """

    latest_volume_release: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The latest volume for this media item
    """

    next_episode: Optional[int] = db.Column(db.Integer, nullable=True)
    """
    The next episode to air
    """

    next_episode_airing_time: Optional[int] = \
        db.Column(db.Integer, nullable=True)
    """
    The time the next episode airs
    """

    releasing_state: ReleasingState = db.Column(db.Enum(ReleasingState),
                                                nullable=False)
    """
    The current releasing state of the media item
    """

    media_ids: List["MediaId"] = db.relationship("MediaId",
                                                 back_populates="media_item",
                                                 cascade="all, delete")
    """
    Media IDs associated with this Media item
    """

    ln_releases: List["LnRelease"] = db.relationship(
        "LnRelease", back_populates="media_item", cascade="all, delete")
    """
    Light novel releases associated with this Media item
    """

    @property
    def current_release(self) -> Optional[int]:
        """
        The most current release, specifically tailored to the type of media
        :return: None
        """
        if self.next_episode is not None:
            return self.next_episode - 1
        elif self.latest_volume_release is not None:
            return self.latest_volume_release
        elif self.latest_release is not None:
            return self.latest_release
        else:
            return None

    @property
    def media_id_mapping(self) -> Dict[ListService, "MediaId"]:
        """
        :return: A dictionary mapping list services to IDs for this media item
        """
        return {x.service: x for x in self.media_ids}

    @property
    def identifier_tuple(self) -> Tuple[str, MediaType, MediaSubType]:
        """
        :return: A tuple that uniquely identifies this database entry
        """
        return self.romaji_title, self.media_type, self.media_subtype

    def update(self, new_data: "MediaItem"):
        """
        Updates the data in this record based on another object
        :param new_data: The object from which to use the new values
        :return: None
        """
        self.media_type = new_data.media_type
        self.media_subtype = new_data.media_subtype
        self.english_title = new_data.english_title
        self.romaji_title = new_data.romaji_title
        self.cover_url = new_data.cover_url
        self.latest_release = new_data.latest_release
        self.latest_volume_release = new_data.latest_volume_release
        self.releasing_state = new_data.releasing_state
        self.next_episode = new_data.next_episode
        self.next_episode_airing_time = new_data.next_episode_airing_time

    @property
    def title(self) -> str:
        """
        :return: The default title for the media item.
        """
        if self.english_title is None:
            return self.romaji_title
        else:
            return self.english_title

    @property
    def own_url(self) -> str:
        """
        :return: The URL to the item's page on the otaku-info site
        """
        return url_for("media.media", media_item_id=self.id)

    @property
    def next_episode_datetime(self) -> Optional[datetime]:
        """
        :return: The datetime for when the next episode airs
        """
        if self.next_episode_airing_time is None:
            return None
        else:
            return datetime.fromtimestamp(self.next_episode_airing_time)