| @@ -1,10 +1,13 @@ | |||
| from __future__ import annotations | |||
| # pylint: disable = missing-class-docstring | |||
| # pylint: disable = missing-function-docstring | |||
| # pylint: disable = missing-module-docstring | |||
| # pylint: disable = unused-argument | |||
| from typing import Any, Generic, Type, TypeVar, overload | |||
| from __future__ import annotations | |||
| from eloquent.orm.relations.dynamic_property import DynamicProperty | |||
| from typing import Any, Generic, Self, Type, TypeVar, overload | |||
| _TModel = TypeVar ('_TModel', bound = 'Model') | |||
| _ModelT = TypeVar ('_ModelT', bound = 'Model') | |||
| class Connection: | |||
| @@ -48,66 +51,78 @@ class DatabaseManager: | |||
| class Model: | |||
| id: int | |||
| def has_one ( | |||
| self, | |||
| related_model: Type[Model], | |||
| foreign_key: str | None = None, | |||
| ) -> DynamicProperty: ... | |||
| related_model: Type[_ModelT], | |||
| foreign_key: str | None = None, | |||
| ) -> _ModelT: ... | |||
| def has_many ( | |||
| self, | |||
| related_model: Type[Model], | |||
| foreign_key: str | None = None, | |||
| ) -> DynamicProperty: ... | |||
| related_model: Type[_ModelT], | |||
| foreign_key: str | None = None, | |||
| ) -> _ModelT: ... | |||
| def belongs_to ( | |||
| self, | |||
| related_model: Type[Model], | |||
| foreign_key: str | None = None, | |||
| ) -> DynamicProperty: ... | |||
| related_model: Type[_ModelT], | |||
| foreign_key: str | None = None, | |||
| ) -> _ModelT: ... | |||
| def belongs_to_many ( | |||
| self, | |||
| related_model: Type[Model], | |||
| foreign_key: str | None = None, | |||
| ) -> DynamicProperty: ... | |||
| related_model: Type[_ModelT], | |||
| foreign_key: str | None = None, | |||
| ) -> _ModelT: ... | |||
| def save (self) -> None: ... | |||
| def delete (self) -> None: ... | |||
| @classmethod | |||
| def find (cls, id: int) -> Model | None: ... | |||
| def find (cls, id_: int) -> Self | None: ... | |||
| @overload | |||
| @classmethod | |||
| def where (cls, field: str, operator: str, value: Any) -> QueryBuilder: ... | |||
| def where ( | |||
| cls, | |||
| field: str, | |||
| operator: str, | |||
| value: Any, | |||
| ) -> QueryBuilder[Self]: ... | |||
| @overload | |||
| @classmethod | |||
| def where (cls, field: str, value: Any) -> QueryBuilder: ... | |||
| def where (cls, field: str, value: Any) -> QueryBuilder[Self]: ... | |||
| @classmethod | |||
| def where_not_in (cls, column: str, values: list[Any] | tuple) -> QueryBuilder: ... | |||
| def where_not_in ( | |||
| cls, | |||
| column: str, | |||
| values: list[Any] | tuple | |||
| ) -> QueryBuilder[Self]: ... | |||
| @classmethod | |||
| def where_not_null (cls, field: str) -> QueryBuilder: ... | |||
| def where_not_null (cls, field: str) -> QueryBuilder[Self]: ... | |||
| @classmethod | |||
| def set_connection_resolver (cls, resolver: DatabaseManager) -> None: ... | |||
| class QueryBuilder (Generic[_TModel]): | |||
| def first (self) -> _TModel | None: ... | |||
| class QueryBuilder (Generic[_ModelT]): | |||
| def first (self) -> _ModelT | None: ... | |||
| def get (self) -> list[_TModel]: ... | |||
| def get (self) -> list[_ModelT]: ... | |||
| @overload | |||
| def where (self, field: str, operator: str, value: Any) -> QueryBuilder: ... | |||
| def where ( | |||
| self, | |||
| field: str, | |||
| operator: str, | |||
| value: Any, | |||
| ) -> QueryBuilder[_ModelT]: ... | |||
| @overload | |||
| def where (self, field: str, value: Any) -> QueryBuilder: ... | |||
| def where (self, field: str, value: Any) -> QueryBuilder[_ModelT]: ... | |||
| def where_null (self, field: str) -> QueryBuilder: ... | |||
| def where_null (self, field: str) -> QueryBuilder[_ModelT]: ... | |||
| @@ -1,4 +0,0 @@ | |||
| from eloquent import Model | |||
| class DynamicProperty (Model): ... | |||
| @@ -1,3 +1,6 @@ | |||
| # pylint: disable = missing-class-docstring | |||
| # pylint: disable = missing-function-docstring | |||
| """ | |||
| 日次で実行し,ぼざクリ DB を最新に更新する. | |||
| """ | |||
| @@ -10,13 +13,11 @@ import random | |||
| import string | |||
| import time | |||
| import unicodedata | |||
| from dataclasses import dataclass | |||
| from datetime import date, datetime, timedelta | |||
| from typing import Any, Type, TypedDict, cast | |||
| from typing import Any, TypedDict, cast | |||
| import requests | |||
| from eloquent import DatabaseManager, Model | |||
| from eloquent.orm.relations.dynamic_property import DynamicProperty | |||
| config: dict[str, DbConfig] = { 'mysql': { 'driver': 'mysql', | |||
| 'host': 'localhost', | |||
| @@ -58,6 +59,8 @@ def update_tables ( | |||
| video_history.views_count = datum['viewCounter'] | |||
| video_history.save () | |||
| video_tags = video.video_tags.where_not_null ('untagged_at').get () | |||
| tag: Tag | None | |||
| video_tag: VideoTag | None | |||
| for video_tag in video_tags: | |||
| tag = video_tag.tag | |||
| if (tag is not None | |||
| @@ -70,10 +73,10 @@ def update_tables ( | |||
| tag = Tag () | |||
| tag.name = tag_name | |||
| tag.save () | |||
| video_tag = (Video.where ('video_id', video.id) | |||
| .where ('tag_id', tag.id) | |||
| .where_null ('untagged_at') | |||
| .first ()) | |||
| video_tag = (VideoTag.where ('video_id', video.id) | |||
| .where ('tag_id', tag.id) | |||
| .where_null ('untagged_at') | |||
| .first ()) | |||
| if video_tag is None: | |||
| video_tag = VideoTag () | |||
| video_tag.video_id = video.id | |||
| @@ -173,12 +176,15 @@ def search_nico_by_tags ( | |||
| while to <= today: | |||
| time.sleep (1.2) | |||
| until = to + timedelta (days = 14) | |||
| # pylint: disable = consider-using-f-string | |||
| query_filter = json.dumps ({ 'type': 'or', | |||
| 'filters': [ | |||
| { 'type': 'range', | |||
| 'field': 'startTime', | |||
| 'from': '%04d-%02d-%02dT00:00:00+09:00' % (to.year, to.month, to.day), | |||
| 'to': '%04d-%02d-%02dT23:59:59+09:00' % (until.year, until.month, until.day), | |||
| 'from': ('%04d-%02d-%02dT00:00:00+09:00' | |||
| % (to.year, to.month, to.day)), | |||
| 'to': ('%04d-%02d-%02dT23:59:59+09:00' | |||
| % (until.year, until.month, until.day)), | |||
| 'include_lower': True }] }) | |||
| params: VideoSearchParam = { 'q': ' OR '.join (tags), | |||
| 'targets': 'tagsExact', | |||
| @@ -202,8 +208,9 @@ def search_nico_by_tags ( | |||
| class Comment (Model): | |||
| __timestamps__ = False | |||
| # pylint: disable = too-many-instance-attributes | |||
| id: int | |||
| video_id: int | |||
| comment_no: int | |||
| user_id: int | |||
| @@ -212,16 +219,18 @@ class Comment (Model): | |||
| nico_count: int | |||
| vpos_ms: int | |||
| __timestamps__ = False | |||
| @property | |||
| def video ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Video: | |||
| return self.belongs_to (Video) | |||
| @property | |||
| def user ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> User: | |||
| return self.belongs_to (User) | |||
| def upsert ( | |||
| @@ -232,58 +241,62 @@ class Comment (Model): | |||
| .first ()) | |||
| if row is not None: | |||
| self.id = row.id | |||
| self.__exists = True # pylint: disable = unused-private-member | |||
| self.save () | |||
| class Tag (Model): | |||
| __timestamps__ = False | |||
| id: int | |||
| name: str | |||
| __timestamps__ = False | |||
| @property | |||
| def video_tags ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> VideoTag: | |||
| return self.has_many (VideoTag) | |||
| class User (Model): | |||
| __timestamps__ = False | |||
| id: int | |||
| code: str | |||
| __timestamps__ = False | |||
| @property | |||
| def comments ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Comment: | |||
| return self.has_many (Comment) | |||
| class Video (Model): | |||
| __timestamps__ = False | |||
| id: int | |||
| code: str | |||
| title: str | |||
| description: str | |||
| uploaded_at: datetime | |||
| deleted_at: datetime | None | |||
| __timestamps__ = False | |||
| @property | |||
| def video_histories ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> VideoHistory: | |||
| return self.has_many (VideoHistory) | |||
| @property | |||
| def video_tags ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> VideoTag: | |||
| return self.has_many (VideoTag) | |||
| @property | |||
| def comments ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Comment: | |||
| return self.has_many (Comment) | |||
| def upsert ( | |||
| @@ -292,20 +305,22 @@ class Video (Model): | |||
| row = Video.where ('code', self.code).first () | |||
| if row is not None: | |||
| self.id = row.id | |||
| self.__exists = True # pylint: disable = unused-private-member | |||
| self.save () | |||
| class VideoHistory (Model): | |||
| __timestamps__ = False | |||
| id: int | |||
| video_id: int | |||
| fetched_at: date | |||
| views_count: int | |||
| __timestamps__ = False | |||
| @property | |||
| def video ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Video: | |||
| return self.belongs_to (Video) | |||
| def upsert ( | |||
| @@ -316,27 +331,29 @@ class VideoHistory (Model): | |||
| .first ()) | |||
| if row is not None: | |||
| self.id = row.id | |||
| self.__exists = True # pylint: disable = unused-private-member | |||
| self.save () | |||
| class VideoTag (Model): | |||
| __timestamps__ = False | |||
| id: int | |||
| video_id: int | |||
| tag_id: int | |||
| tagged_at: date | |||
| untagged_at: date | None | |||
| __timestamps__ = False | |||
| @property | |||
| def video ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Video: | |||
| return self.belongs_to (Video) | |||
| @property | |||
| def tag ( | |||
| self, | |||
| ) -> DynamicProperty: | |||
| ) -> Tag: | |||
| return self.belongs_to (Tag) | |||
| def upsert ( | |||
| @@ -347,6 +364,7 @@ class VideoTag (Model): | |||
| .first ()) | |||
| if row is not None: | |||
| self.id = row.id | |||
| self.__exists = True # pylint: disable = unused-private-member | |||
| self.save () | |||