wikidot.module.page_revision のソースコード
"""
Wikidotページの編集履歴(リビジョン)を扱うモジュール
このモジュールは、Wikidotページの編集履歴(リビジョン)に関連するクラスや機能を提供する。
リビジョンの取得、ソースの取得、HTML表示などの操作が可能。
"""
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, Optional
from bs4 import BeautifulSoup
from ..common.exceptions import NoElementException
from .page_source import PageSource
if TYPE_CHECKING:
from .page import Page
from .user import AbstractUser
[ドキュメント]
class PageRevisionCollection(list["PageRevision"]):
"""
ページリビジョンのコレクションを表すクラス
ページの編集履歴(リビジョン)の複数バージョンを格納し、一括して操作するための
リスト拡張クラス。ソースコードやHTMLの一括取得など、便利な機能を提供する。
"""
def __init__(
self,
page: Optional["Page"] = None,
revisions: Optional[list["PageRevision"]] = None,
):
"""
初期化メソッド
Parameters
----------
page : Page | None, default None
リビジョンが属するページ。Noneの場合は最初のリビジョンから推測する
revisions : list[PageRevision] | None, default None
格納するリビジョンのリスト
"""
super().__init__(revisions or [])
self.page = page or self[0].page if len(self) > 0 else None
def __iter__(self) -> Iterator["PageRevision"]:
"""
コレクション内のリビジョンを順に返すイテレータ
Returns
-------
Iterator[PageRevision]
リビジョンオブジェクトのイテレータ
"""
return super().__iter__()
[ドキュメント]
def find(self, id: int) -> Optional["PageRevision"]:
"""
指定したリビジョンIDのリビジョンを取得する
Parameters
----------
id : int
取得するリビジョンのID
Returns
-------
PageRevision | None
指定したIDのリビジョン。見つからない場合はNone
"""
for revision in self:
if revision.id == id:
return revision
return None
@staticmethod
def _acquire_sources(page, revisions: list["PageRevision"]):
"""
複数のリビジョンのソースコードを一括取得する内部メソッド
未取得のリビジョンソースコードを一括でリクエストし、取得する。
Parameters
----------
page : Page
リビジョンが属するページ
revisions : list[PageRevision]
ソースコードを取得するリビジョンのリスト
Returns
-------
list[PageRevision]
ソースコード情報が更新されたリビジョンのリスト
Raises
------
NoElementException
ソース要素が見つからない場合
"""
target_revisions = [revision for revision in revisions if not revision.is_source_acquired()]
if len(target_revisions) == 0:
return revisions
responses = page.site.amc_request(
[{"moduleName": "history/PageSourceModule", "revision_id": revision.id} for revision in target_revisions]
)
for revision, response in zip(target_revisions, responses):
body = response.json()["body"]
body_html = BeautifulSoup(body, "lxml")
wiki_text_elem = body_html.select_one("div.page-source")
if wiki_text_elem is None:
raise NoElementException("Wiki text element not found")
revision.source = PageSource(
page=page,
wiki_text=wiki_text_elem.text.strip(),
)
return revisions
[ドキュメント]
def get_sources(self):
"""
コレクション内のすべてのリビジョンのソースコードを取得する
Returns
-------
PageRevisionCollection
自身(メソッドチェーン用)
"""
return self._acquire_sources(self.page, self)
@staticmethod
def _acquire_htmls(page, revisions: list["PageRevision"]):
"""
複数のリビジョンのHTML表示を一括取得する内部メソッド
未取得のリビジョンHTMLを一括でリクエストし、取得する。
Parameters
----------
page : Page
リビジョンが属するページ
revisions : list[PageRevision]
HTMLを取得するリビジョンのリスト
Returns
-------
list[PageRevision]
HTML情報が更新されたリビジョンのリスト
"""
target_revisions = [revision for revision in revisions if not revision.is_html_acquired()]
if len(target_revisions) == 0:
return revisions
responses = page.site.amc_request(
[{"moduleName": "history/PageVersionModule", "revision_id": revision.id} for revision in target_revisions]
)
for revision, response in zip(target_revisions, responses):
body = response.json()["body"]
# onclick="document.getElementById('page-version-info').style.display='none'">(.*?)</a>\n\t</div>\n\n\n\n
# 以降をソースとして取得
source = body.split(
"onclick=\"document.getElementById('page-version-info').style.display='none'\">",
maxsplit=1,
)[1]
source = source.split("</a>\n\t</div>\n\n\n\n", maxsplit=1)[1]
revision._html = source
return revisions
[ドキュメント]
def get_htmls(self):
"""
コレクション内のすべてのリビジョンのHTML表示を取得する
Returns
-------
PageRevisionCollection
自身(メソッドチェーン用)
"""
return self._acquire_htmls(self.page, self)
[ドキュメント]
@dataclass
class PageRevision:
"""
ページのリビジョン(編集履歴のバージョン)を表すクラス
ページの特定のバージョンに関する情報を保持する。リビジョン番号、作成者、作成日時、
編集コメントなどの基本情報に加え、ソースコードやHTML表示へのアクセス機能を提供する。
Attributes
----------
page : Page
リビジョンが属するページ
id : int
リビジョンID
rev_no : int
リビジョン番号
created_by : AbstractUser
リビジョンの作成者
created_at : datetime
リビジョンの作成日時
comment : str
編集コメント
_source : PageSource | None, default None
リビジョンのソースコード(内部キャッシュ用)
_html : str | None, default None
リビジョンのHTML表示(内部キャッシュ用)
"""
page: "Page"
id: int
rev_no: int
created_by: "AbstractUser"
created_at: datetime
comment: str
_source: Optional["PageSource"] = None
_html: Optional[str] = None
[ドキュメント]
def is_source_acquired(self) -> bool:
"""
ソースコードが既に取得済みかどうかを確認する
Returns
-------
bool
ソースコードが取得済みの場合はTrue、そうでない場合はFalse
"""
return self._source is not None
[ドキュメント]
def is_html_acquired(self) -> bool:
"""
HTML表示が既に取得済みかどうかを確認する
Returns
-------
bool
HTML表示が取得済みの場合はTrue、そうでない場合はFalse
"""
return self._html is not None
@property
def source(self) -> Optional["PageSource"]:
"""
リビジョンのソースコードを取得する
ソースコードが未取得の場合は自動的に取得処理を行う。
Returns
-------
PageSource | None
リビジョンのソースコード
"""
if not self.is_source_acquired():
PageRevisionCollection(self.page, [self]).get_sources()
return self._source
@source.setter
def source(self, value: "PageSource"):
"""
リビジョンのソースコードを設定する
Parameters
----------
value : PageSource
設定するソースコード
"""
self._source = value
@property
def html(self) -> Optional[str]:
"""
リビジョンのHTML表示を取得する
HTML表示が未取得の場合は自動的に取得処理を行う。
Returns
-------
str | None
リビジョンのHTML表示
"""
if not self.is_html_acquired():
PageRevisionCollection(self.page, [self]).get_htmls()
return self._html
@html.setter
def html(self, value: str):
"""
リビジョンのHTML表示を設定する
Parameters
----------
value : str
設定するHTML表示
"""
self._html = value