本日作業分
このコミットが含まれているのは:
+116
-48
@@ -5,31 +5,72 @@ import string
|
|||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import TypedDict, cast
|
||||||
|
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
DbNull = (None,)
|
||||||
|
DbNullType = tuple[None]
|
||||||
|
|
||||||
|
|
||||||
|
class VideoResult (TypedDict):
|
||||||
|
contentId: str
|
||||||
|
title: str
|
||||||
|
tags: list[str]
|
||||||
|
description: str
|
||||||
|
viewCounter: int
|
||||||
|
startTime: str
|
||||||
|
|
||||||
|
|
||||||
|
class VideoSearchParam (TypedDict):
|
||||||
|
q: str
|
||||||
|
targets: str
|
||||||
|
_sort: str
|
||||||
|
fields: str
|
||||||
|
_limit: int
|
||||||
|
jsonFilter: str
|
||||||
|
|
||||||
|
|
||||||
def main (
|
def main (
|
||||||
) -> None:
|
) -> None:
|
||||||
conn = mysql.connector.connect (host = os.environ['MYSQL_HOST'],
|
conn = mysql.connector.connect (host = os.environ['MYSQL_HOST'],
|
||||||
user = os.environ['MYSQL_USER'],
|
user = os.environ['MYSQL_USER'],
|
||||||
password = os.environ['MYSQL_PASS'])
|
password = os.environ['MYSQL_PASS'])
|
||||||
|
|
||||||
|
now = datetime.now ()
|
||||||
|
|
||||||
video_dao = VideoDao (conn)
|
video_dao = VideoDao (conn)
|
||||||
|
tag_dao = TagDao (conn)
|
||||||
|
|
||||||
api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ'])
|
api_data = search_nico_by_tags (['伊地知ニジカ', 'ぼざろクリーチャーシリーズ'])
|
||||||
|
|
||||||
update_video_table (video_dao, api_data)
|
update_video_and_tag_table (video_dao, tag_dao, api_data, now)
|
||||||
|
|
||||||
# TODO: 書くこと
|
# TODO: 書くこと
|
||||||
|
|
||||||
|
|
||||||
def update_video_table (
|
def update_video_and_tag_table (
|
||||||
video_dao: VideoDao,
|
video_dao: VideoDao,
|
||||||
api_data: list[dict],
|
tag_dao: TagDao,
|
||||||
|
api_data: list[VideoResult],
|
||||||
|
now: datetime,
|
||||||
) -> None:
|
) -> None:
|
||||||
# TODO: videos 取得書くこと
|
videos: list[VideoDto] = []
|
||||||
|
for datum in api_data:
|
||||||
|
tags: list[TagDto] = []
|
||||||
|
for tag_name in datum['tags']:
|
||||||
|
tags.append (TagDto (name = tag_name))
|
||||||
|
tag_dao.upsert_all (tags)
|
||||||
|
# TODO: タグの対応づけすること
|
||||||
|
# TODO: コメント取得すること
|
||||||
|
video = VideoDto (code = datum['contentId'],
|
||||||
|
title = datum['title'],
|
||||||
|
description = datum['description'],
|
||||||
|
uploaded_at = datum['startTime'],
|
||||||
|
deleted_at = DbNull)
|
||||||
|
videos.append (video)
|
||||||
|
|
||||||
video_dao.upsert_all (videos)
|
video_dao.upsert_all (videos)
|
||||||
|
|
||||||
@@ -85,13 +126,13 @@ def fetch_comments (
|
|||||||
|
|
||||||
def search_nico_by_tag (
|
def search_nico_by_tag (
|
||||||
tag: str,
|
tag: str,
|
||||||
) -> list[dict]:
|
) -> list[VideoResult]:
|
||||||
return search_nico_by_tags ([tag])
|
return search_nico_by_tags ([tag])
|
||||||
|
|
||||||
|
|
||||||
def search_nico_by_tags (
|
def search_nico_by_tags (
|
||||||
tags: list[str],
|
tags: list[str],
|
||||||
) -> list[dict]:
|
) -> list[VideoResult]:
|
||||||
url = ('https://snapshot.search.nicovideo.jp'
|
url = ('https://snapshot.search.nicovideo.jp'
|
||||||
+ '/api/v2/snapshot/video/contents/search')
|
+ '/api/v2/snapshot/video/contents/search')
|
||||||
|
|
||||||
@@ -105,15 +146,15 @@ def search_nico_by_tags (
|
|||||||
'to': f"{year}-{end}T23:59:59+09:00",
|
'to': f"{year}-{end}T23:59:59+09:00",
|
||||||
'include_lower': True }] })
|
'include_lower': True }] })
|
||||||
|
|
||||||
params: dict[str, int | str]
|
params: VideoSearchParam
|
||||||
params = { 'q': ' OR '.join (tags),
|
params = { 'q': ' OR '.join (tags),
|
||||||
'targets': 'tagsExact',
|
'targets': 'tagsExact',
|
||||||
'_sort': '-viewCounter',
|
'_sort': '-viewCounter',
|
||||||
'fields': 'contentId,title,tags,viewCounter,startTime',
|
'fields': 'contentId,title,tags,description,viewCounter,startTime',
|
||||||
'_limit': 100,
|
'_limit': 100,
|
||||||
'jsonFilter': query_filter }
|
'jsonFilter': query_filter }
|
||||||
|
|
||||||
res = requests.get (url, params = params, timeout = 60).json ()
|
res = requests.get (url, params = cast (dict[str, int | str], params), timeout = 60).json ()
|
||||||
|
|
||||||
return res['data']
|
return res['data']
|
||||||
|
|
||||||
@@ -172,6 +213,43 @@ class VideoDao:
|
|||||||
videos.append (self._create_dto_from_row (row, with_relation_tables))
|
videos.append (self._create_dto_from_row (row, with_relation_tables))
|
||||||
return videos
|
return videos
|
||||||
|
|
||||||
|
def upsert (
|
||||||
|
self,
|
||||||
|
video: VideoDto,
|
||||||
|
with_relation_tables: bool = True,
|
||||||
|
) -> None:
|
||||||
|
with self.conn.cursor () as c:
|
||||||
|
c.execute ("""
|
||||||
|
INSERT INTO
|
||||||
|
videos(
|
||||||
|
code,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
uploaded_at,
|
||||||
|
deleted_at)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
%s,
|
||||||
|
%s,
|
||||||
|
%s,
|
||||||
|
%s,
|
||||||
|
%s)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
code = VALUES(code),
|
||||||
|
title = VALUES(title),
|
||||||
|
description = VALUES(description),
|
||||||
|
uploaded_at = VALUES(uploaded_at),
|
||||||
|
deleted_at = VALUES(deleted_at)""", (video.code,
|
||||||
|
video.title,
|
||||||
|
video.description,
|
||||||
|
video.uploaded_at,
|
||||||
|
video.deleted_at))
|
||||||
|
video.id_ = c.lastrowid
|
||||||
|
if with_relation_tables:
|
||||||
|
VideoTagDao (self.conn).upsert_all (video.video_tags, False)
|
||||||
|
CommentDao (self.conn).upsert_all (video.comments, False)
|
||||||
|
VideoHistoryDao (self.conn).upsert_all (video.video_histories, False)
|
||||||
|
|
||||||
def upsert_all (
|
def upsert_all (
|
||||||
self,
|
self,
|
||||||
videos: list[VideoDto],
|
videos: list[VideoDto],
|
||||||
@@ -179,36 +257,7 @@ class VideoDao:
|
|||||||
) -> None:
|
) -> None:
|
||||||
with self.conn.cursor () as c:
|
with self.conn.cursor () as c:
|
||||||
for video in videos:
|
for video in videos:
|
||||||
c.execute ("""
|
self.upsert (video, with_relation_tables)
|
||||||
INSERT INTO
|
|
||||||
videos(
|
|
||||||
code,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
uploaded_at,
|
|
||||||
deleted_at)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
%s,
|
|
||||||
%s,
|
|
||||||
%s,
|
|
||||||
%s,
|
|
||||||
%s)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
code = VALUES(code),
|
|
||||||
title = VALUES(title),
|
|
||||||
description = VALUES(description),
|
|
||||||
uploaded_at = VALUES(uploaded_at),
|
|
||||||
deleted_at = VALUES(deleted_at)""", (video.code,
|
|
||||||
video.title,
|
|
||||||
video.description,
|
|
||||||
video.uploaded_at,
|
|
||||||
video.deleted_at))
|
|
||||||
video.id_ = c.lastrowid
|
|
||||||
if with_relation_tables:
|
|
||||||
VideoTagDao (self.conn).upsert_all (video.video_tags, False)
|
|
||||||
CommentDao (self.conn).upsert_all (video.comments, False)
|
|
||||||
VideoHistoryDao (self.conn).upsert_all (video.video_histories, False)
|
|
||||||
|
|
||||||
def _create_dto_from_row (
|
def _create_dto_from_row (
|
||||||
self,
|
self,
|
||||||
@@ -222,7 +271,7 @@ class VideoDao:
|
|||||||
uploaded_at = row['uploaded_at'],
|
uploaded_at = row['uploaded_at'],
|
||||||
deleted_at = row['deleted_at'])
|
deleted_at = row['deleted_at'])
|
||||||
if with_relation_tables:
|
if with_relation_tables:
|
||||||
video.video_tags = VideoTagDao (self.conn).fetch_by_video_id (video.id_, False)
|
video.video_tags = VideoTagDao (self.conn).fetch_by_video_id (cast (int, video.id_), False)
|
||||||
for i in range (len (video.video_tags)):
|
for i in range (len (video.video_tags)):
|
||||||
video.video_tags[i].video = video
|
video.video_tags[i].video = video
|
||||||
video.comments = CommentDao (self.conn).fetch_by_video_id (video.id_, False)
|
video.comments = CommentDao (self.conn).fetch_by_video_id (video.id_, False)
|
||||||
@@ -236,12 +285,12 @@ class VideoDao:
|
|||||||
|
|
||||||
@dataclass (slots = True)
|
@dataclass (slots = True)
|
||||||
class VideoDto:
|
class VideoDto:
|
||||||
id_: int | None = None
|
|
||||||
code: str
|
code: str
|
||||||
title: str
|
title: str
|
||||||
description: str
|
description: str
|
||||||
uploaded_at: datetime
|
uploaded_at: datetime
|
||||||
deleted_at: datetime | None = None
|
id_: int | None = None
|
||||||
|
deleted_at: datetime | DbNullType = DbNull
|
||||||
video_tags: list[VideoTagDto] | None = None
|
video_tags: list[VideoTagDto] | None = None
|
||||||
comments: list[CommentDto] | None = None
|
comments: list[CommentDto] | None = None
|
||||||
video_histories: list[VideoHistoryDto] | None = None
|
video_histories: list[VideoHistoryDto] | None = None
|
||||||
@@ -296,13 +345,32 @@ class VideoTagDao:
|
|||||||
|
|
||||||
@dataclass (slots = True)
|
@dataclass (slots = True)
|
||||||
class VideoTagDto:
|
class VideoTagDto:
|
||||||
id_: int | None = None
|
|
||||||
video_id: int
|
|
||||||
tag_id: int
|
|
||||||
tagged_at: datetime
|
tagged_at: datetime
|
||||||
untagged_at: datetime
|
id_: int | None = None
|
||||||
video: VideoDto | None = None
|
video_id: int | None = None
|
||||||
tag: TagDto | None = None
|
tag_id: int | None = None
|
||||||
|
untagged_at: datetime | DbNullType = DbNull
|
||||||
|
video: VideoDto | None = None
|
||||||
|
tag: TagDto | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass (slots = True)
|
||||||
|
class TagDto:
|
||||||
|
name: str
|
||||||
|
id_: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass (slots = True)
|
||||||
|
class CommentDto:
|
||||||
|
video_id: int
|
||||||
|
comment_no: int
|
||||||
|
user_id: int
|
||||||
|
content: str
|
||||||
|
posted_at: datetime
|
||||||
|
id_: int | None = None
|
||||||
|
nico_count: int = 0
|
||||||
|
vpos_ms: int | DbNullType = DbNull
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする