byceps/services/board/dbmodels/topic.py
"""
byceps.services.board.dbmodels.topic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:Copyright: 2014-2024 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""
from datetime import datetime
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import Mapped, mapped_column, relationship
from byceps.database import db
from byceps.services.board.models import BoardCategoryID, TopicID
from byceps.services.user.dbmodels.user import DbUser
from byceps.services.user.models.user import UserID
from byceps.util.instances import ReprBuilder
from .category import DbBoardCategory
class DbTopic(db.Model):
"""A topic."""
__tablename__ = 'board_topics'
id: Mapped[TopicID] = mapped_column(db.Uuid, primary_key=True)
category_id: Mapped[BoardCategoryID] = mapped_column(
db.Uuid,
db.ForeignKey('board_categories.id'),
index=True,
nullable=False,
)
category: Mapped[DbBoardCategory] = relationship(DbBoardCategory)
created_at: Mapped[datetime] = mapped_column(
db.DateTime, default=datetime.utcnow
)
creator_id: Mapped[UserID] = mapped_column(
db.Uuid, db.ForeignKey('users.id')
)
title: Mapped[str] = mapped_column(db.UnicodeText)
posting_count: Mapped[int] = mapped_column(default=0)
last_updated_at: Mapped[datetime | None] = mapped_column(
default=datetime.utcnow
)
last_updated_by_id: Mapped[UserID | None] = mapped_column(
db.Uuid, db.ForeignKey('users.id')
)
last_updated_by: Mapped[DbUser | None] = relationship(
DbUser, foreign_keys=[last_updated_by_id]
)
hidden: Mapped[bool] = mapped_column(default=False)
hidden_at: Mapped[datetime | None]
hidden_by_id: Mapped[UserID | None] = mapped_column(
db.Uuid, db.ForeignKey('users.id')
)
hidden_by: Mapped[DbUser | None] = relationship(
DbUser, foreign_keys=[hidden_by_id]
)
locked: Mapped[bool] = mapped_column(default=False)
locked_at: Mapped[datetime | None]
locked_by_id: Mapped[UserID | None] = mapped_column(
db.Uuid, db.ForeignKey('users.id')
)
locked_by: Mapped[DbUser | None] = relationship(
DbUser, foreign_keys=[locked_by_id]
)
pinned: Mapped[bool] = mapped_column(default=False)
pinned_at: Mapped[datetime | None]
pinned_by_id: Mapped[UserID | None] = mapped_column(
db.Uuid, db.ForeignKey('users.id')
)
pinned_by: Mapped[DbUser | None] = relationship(
DbUser, foreign_keys=[pinned_by_id]
)
initial_posting = association_proxy(
'initial_topic_posting_association', 'posting'
)
posting_limited_to_moderators: Mapped[bool] = mapped_column(default=False)
muted: Mapped[bool] = mapped_column(default=False)
def __init__(
self,
topic_id: TopicID,
category_id: BoardCategoryID,
creator_id: UserID,
title: str,
) -> None:
self.id = topic_id
self.category_id = category_id
self.creator_id = creator_id
self.title = title
@property
def reply_count(self) -> int:
return self.posting_count - 1
def count_pages(self, postings_per_page: int) -> int:
"""Return the number of pages this topic spans."""
full_page_count, remaining_postings = divmod(
self.posting_count, postings_per_page
)
if remaining_postings > 0:
return full_page_count + 1
else:
return full_page_count
def __eq__(self, other) -> bool:
return self.id == other.id
def __repr__(self) -> str:
builder = (
ReprBuilder(self)
.add_with_lookup('id')
.add('category', self.category.title)
.add_with_lookup('title')
)
if self.hidden_by:
builder.add_custom(f'hidden by {self.hidden_by.screen_name}')
if self.locked_by:
builder.add_custom(f'locked by {self.locked_by.screen_name}')
if self.pinned_by:
builder.add_custom(f'pinned by {self.pinned_by.screen_name}')
return builder.build()