xtools 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xtools-0.2.0 → xtools-0.3.0}/PKG-INFO +11 -12
- {xtools-0.2.0 → xtools-0.3.0}/README.md +3 -4
- xtools-0.3.0/pyproject.toml +68 -0
- {xtools-0.2.0 → xtools-0.3.0}/xtools/__init__.py +11 -2
- {xtools-0.2.0 → xtools-0.3.0}/xtools/base.py +11 -13
- {xtools-0.2.0 → xtools-0.3.0}/xtools/exceptions.py +3 -3
- {xtools-0.2.0 → xtools-0.3.0}/xtools/page.py +11 -10
- {xtools-0.2.0 → xtools-0.3.0}/xtools/project.py +26 -26
- {xtools-0.2.0 → xtools-0.3.0}/xtools/quote.py +3 -2
- {xtools-0.2.0 → xtools-0.3.0}/xtools/user.py +60 -61
- xtools-0.2.0/LICENSE +0 -18
- xtools-0.2.0/pyproject.toml +0 -31
- {xtools-0.2.0 → xtools-0.3.0}/xtools/py.typed +0 -0
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: xtools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: XTools API wrapper
|
|
5
|
-
Home-page: https://github.com/bfontaine/xtools
|
|
6
5
|
Author: Baptiste Fontaine
|
|
7
|
-
Author-email: b@ptistefontaine.fr
|
|
8
|
-
Requires-Python: >=3.8,<4.0
|
|
6
|
+
Author-email: Baptiste Fontaine <b@ptistefontaine.fr>
|
|
9
7
|
Classifier: Development Status :: 5 - Production/Stable
|
|
10
8
|
Classifier: Intended Audience :: Developers
|
|
11
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
10
|
Classifier: Operating System :: OS Independent
|
|
13
11
|
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
-
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Dist: requests>=2.28.2,<3
|
|
18
|
+
Requires-Python: >=3.10, <4.0
|
|
19
|
+
Project-URL: Homepage, https://github.com/bfontaine/xtools
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
|
|
22
22
|
# xtools
|
|
23
|
-
[](https://pypi.org/project/
|
|
24
|
-
[](https://coveralls.io/github/bfontaine/xtools?branch=master)
|
|
23
|
+
[](https://pypi.org/project/xtools/)
|
|
25
24
|
|
|
26
25
|
**xtools** is a Python wrapper around [XTools][]’ API.
|
|
27
26
|
|
|
@@ -35,7 +34,7 @@ Note this project is not affiliated with nor endorsed by XTools.
|
|
|
35
34
|
|
|
36
35
|
### Requirements
|
|
37
36
|
|
|
38
|
-
Python 3.
|
|
37
|
+
Python 3.10+.
|
|
39
38
|
|
|
40
39
|
## Usage
|
|
41
40
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# xtools
|
|
2
|
-
[](https://pypi.org/project/
|
|
3
|
-
[](https://coveralls.io/github/bfontaine/xtools?branch=master)
|
|
2
|
+
[](https://pypi.org/project/xtools/)
|
|
4
3
|
|
|
5
4
|
**xtools** is a Python wrapper around [XTools][]’ API.
|
|
6
5
|
|
|
@@ -14,8 +13,8 @@ Note this project is not affiliated with nor endorsed by XTools.
|
|
|
14
13
|
|
|
15
14
|
### Requirements
|
|
16
15
|
|
|
17
|
-
Python 3.
|
|
16
|
+
Python 3.10+.
|
|
18
17
|
|
|
19
18
|
## Usage
|
|
20
19
|
|
|
21
|
-
[Read the docs](https://python-xtools.readthedocs.io/en/latest/).
|
|
20
|
+
[Read the docs](https://python-xtools.readthedocs.io/en/latest/).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xtools"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "XTools API wrapper"
|
|
5
|
+
authors = [{ name = "Baptiste Fontaine", email = "b@ptistefontaine.fr" }]
|
|
6
|
+
requires-python = ">=3.10,<4.0"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Development Status :: 5 - Production/Stable",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.10",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
]
|
|
20
|
+
dependencies = ["requests>=2.28.2,<3"]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/bfontaine/xtools"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"types-requests>=2.28.11.17,<3",
|
|
28
|
+
"mypy>=2,<3",
|
|
29
|
+
"pytest>=9,<10",
|
|
30
|
+
"sphinx>=8,<9",
|
|
31
|
+
"requests-mock>=1.10,<2",
|
|
32
|
+
"pytest-cov>=7,<8",
|
|
33
|
+
"ruff>=0.15.17",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[tool.uv]
|
|
37
|
+
default-groups = "all"
|
|
38
|
+
|
|
39
|
+
[tool.uv.build-backend]
|
|
40
|
+
module-root = ""
|
|
41
|
+
source-include = ["xtools/py.typed"]
|
|
42
|
+
|
|
43
|
+
[build-system]
|
|
44
|
+
requires = ["uv_build>=0.9.17,<0.10.0"]
|
|
45
|
+
build-backend = "uv_build"
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
line-length = 120
|
|
49
|
+
target-version = "py310"
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = [
|
|
52
|
+
"PERF",
|
|
53
|
+
"W",
|
|
54
|
+
"F",
|
|
55
|
+
"UP",
|
|
56
|
+
"ASYNC",
|
|
57
|
+
# "A",
|
|
58
|
+
# "B",
|
|
59
|
+
# "PT",
|
|
60
|
+
"C4",
|
|
61
|
+
"PIE800",
|
|
62
|
+
"SIM",
|
|
63
|
+
"SLOT",
|
|
64
|
+
"YTT",
|
|
65
|
+
"PLE",
|
|
66
|
+
"FURB",
|
|
67
|
+
"PLC0207",
|
|
68
|
+
]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from .exceptions import BaseXToolsException, NotFound, TooManyEdits
|
|
1
2
|
from .page import article_info, prose, links, top_editors, assessments
|
|
2
3
|
from .project import (normalize_project, namespaces, page_assessments, page_assessments_configuration, automated_tools,
|
|
3
4
|
admins_and_user_groups, admin_statistics, patroller_statistics, steward_statistics)
|
|
@@ -6,6 +7,14 @@ from .user import (simple_edit_count, number_of_pages_created, pages_created, pa
|
|
|
6
7
|
automated_edit_counter, non_automated_edits, automated_edits, edit_summaries, top_edits,
|
|
7
8
|
category_edit_counter, log_counts, namespace_totals, month_counts, time_card)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
__version__ = "0.3.0"
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
__all__ = (
|
|
13
|
+
"__version__", "article_info", "prose", "links", "top_editors", "assessments", "normalize_project", "namespaces",
|
|
14
|
+
"page_assessments", "page_assessments_configuration", "automated_tools", "admins_and_user_groups",
|
|
15
|
+
"admin_statistics", "patroller_statistics", "steward_statistics", "random_quote", "single_quote", "all_quotes",
|
|
16
|
+
"simple_edit_count", "number_of_pages_created", "pages_created", "pages_created_iter", "automated_edit_counter",
|
|
17
|
+
"non_automated_edits", "automated_edits", "edit_summaries", "top_edits", "category_edit_counter", "log_counts",
|
|
18
|
+
"namespace_totals", "month_counts", "time_card",
|
|
19
|
+
"BaseXToolsException", "NotFound", "TooManyEdits",
|
|
20
|
+
)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Internal base utilities.
|
|
3
3
|
"""
|
|
4
|
-
|
|
5
|
-
from
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Sequence
|
|
6
6
|
from json.decoder import JSONDecodeError
|
|
7
|
+
from typing import Any
|
|
7
8
|
from urllib.parse import quote as urlquote
|
|
8
9
|
|
|
9
|
-
import time
|
|
10
10
|
import requests
|
|
11
|
+
|
|
11
12
|
from .exceptions import BaseXToolsException, NotFound, TooManyEdits
|
|
12
13
|
|
|
13
14
|
BASE_URL = "https://xtools.wmflabs.org/api"
|
|
@@ -24,7 +25,7 @@ def url(path: str) -> str:
|
|
|
24
25
|
return BASE_URL + path
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def error_exception(response: dict) ->
|
|
28
|
+
def error_exception(response: dict[str, Any]) -> BaseXToolsException | None:
|
|
28
29
|
"""
|
|
29
30
|
Return an optional Exception instance for an error response. Return ``None`` if the given response is not an error.
|
|
30
31
|
:param response: response from the API.
|
|
@@ -54,7 +55,7 @@ def error_exception(response: dict) -> Optional[BaseXToolsException]:
|
|
|
54
55
|
return BaseXToolsException(str(error))
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
def get(path: str, params=None, retry=3, retry_delay=1):
|
|
58
|
+
def get(path: str, params: dict[str, Any] | None = None, retry: int = 3, retry_delay: int = 1) -> dict[str, Any]:
|
|
58
59
|
"""
|
|
59
60
|
Perform a GET request against the API.
|
|
60
61
|
:param path:
|
|
@@ -72,7 +73,7 @@ def get(path: str, params=None, retry=3, retry_delay=1):
|
|
|
72
73
|
r.raise_for_status()
|
|
73
74
|
|
|
74
75
|
try:
|
|
75
|
-
response = r.json()
|
|
76
|
+
response: dict[str, Any] = r.json()
|
|
76
77
|
except JSONDecodeError as e:
|
|
77
78
|
r.raise_for_status()
|
|
78
79
|
raise e
|
|
@@ -83,7 +84,7 @@ def get(path: str, params=None, retry=3, retry_delay=1):
|
|
|
83
84
|
return response
|
|
84
85
|
|
|
85
86
|
|
|
86
|
-
def build_path(path_format: str, params: Sequence[
|
|
87
|
+
def build_path(path_format: str, params: Sequence[tuple[str, Any, str]]) -> str:
|
|
87
88
|
"""
|
|
88
89
|
Build a path for the XTools API.
|
|
89
90
|
|
|
@@ -100,13 +101,10 @@ def build_path(path_format: str, params: Sequence[Tuple[str, Any, str]]) -> str:
|
|
|
100
101
|
:param params:
|
|
101
102
|
:return:
|
|
102
103
|
"""
|
|
103
|
-
params_dict = {}
|
|
104
|
+
params_dict: dict[str, str] = {}
|
|
104
105
|
|
|
105
|
-
def has_more(index):
|
|
106
|
-
for _, val, _ in params[index + 1:]
|
|
107
|
-
if val:
|
|
108
|
-
return True
|
|
109
|
-
return False
|
|
106
|
+
def has_more(index: int) -> bool:
|
|
107
|
+
return any(val for _, val, _ in params[index + 1:])
|
|
110
108
|
|
|
111
109
|
for i, (name, value, default) in enumerate(params):
|
|
112
110
|
if value:
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
__all__ = ['BaseXToolsException', 'NotFound', 'TooManyEdits']
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class BaseXToolsException(Exception):
|
|
7
|
-
def __init__(self, message, code:
|
|
7
|
+
def __init__(self, message: str, code: int | None = None) -> None:
|
|
8
8
|
super().__init__(message)
|
|
9
9
|
self.message = message
|
|
10
10
|
self.code = code
|
|
11
11
|
|
|
12
|
-
def __eq__(self, other):
|
|
12
|
+
def __eq__(self, other: Any) -> bool:
|
|
13
13
|
return isinstance(other, BaseXToolsException) \
|
|
14
14
|
and str(self) == str(other) \
|
|
15
15
|
and self.code == other.code
|
|
@@ -4,13 +4,14 @@ Endpoints related to pages.
|
|
|
4
4
|
https://xtools.readthedocs.io/en/stable/api/page.html
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from collections.abc import Sequence
|
|
8
8
|
from datetime import date
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
from . import base
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def _get_page_dict(what: str, project: str, article: str) -> dict:
|
|
14
|
+
def _get_page_dict(what: str, project: str, article: str) -> dict[str, Any]:
|
|
14
15
|
return base.get(base.build_path("/page/{what}/{project}/{article}", (
|
|
15
16
|
("what", what, ""),
|
|
16
17
|
("project", project, ""),
|
|
@@ -18,7 +19,7 @@ def _get_page_dict(what: str, project: str, article: str) -> dict:
|
|
|
18
19
|
)))
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def article_info(project: str, article: str) -> dict:
|
|
22
|
+
def article_info(project: str, article: str) -> dict[str, Any]:
|
|
22
23
|
"""
|
|
23
24
|
Return basic information about an article, such as page views, watchers, edits counts; author; assessment.
|
|
24
25
|
|
|
@@ -31,7 +32,7 @@ def article_info(project: str, article: str) -> dict:
|
|
|
31
32
|
return _get_page_dict("articleinfo", project, article)
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
def prose(project: str, article: str) -> dict:
|
|
35
|
+
def prose(project: str, article: str) -> dict[str, Any]:
|
|
35
36
|
"""
|
|
36
37
|
Return prose information about an article, such as references and words counts.
|
|
37
38
|
|
|
@@ -58,7 +59,7 @@ def prose(project: str, article: str) -> dict:
|
|
|
58
59
|
return _get_page_dict("prose", project, article)
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
def links(project: str, article: str) -> dict:
|
|
62
|
+
def links(project: str, article: str) -> dict[str, Any]:
|
|
62
63
|
"""
|
|
63
64
|
Return in and outgoing links counts of an article.
|
|
64
65
|
|
|
@@ -85,10 +86,10 @@ def links(project: str, article: str) -> dict:
|
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
def top_editors(project: str, article: str,
|
|
88
|
-
start:
|
|
89
|
-
end:
|
|
90
|
-
limit:
|
|
91
|
-
exclude_bots: bool = False) -> dict:
|
|
89
|
+
start: date | None = None,
|
|
90
|
+
end: date | None = None,
|
|
91
|
+
limit: int | None = None,
|
|
92
|
+
exclude_bots: bool = False) -> dict[str, Any]:
|
|
92
93
|
"""
|
|
93
94
|
Get top editors on an article.
|
|
94
95
|
|
|
@@ -140,7 +141,7 @@ def top_editors(project: str, article: str,
|
|
|
140
141
|
|
|
141
142
|
|
|
142
143
|
def assessments(project: str, articles: Sequence[str],
|
|
143
|
-
class_only: bool = False) -> dict:
|
|
144
|
+
class_only: bool = False) -> dict[str, Any]:
|
|
144
145
|
"""
|
|
145
146
|
Get assessment data for the given articles.
|
|
146
147
|
|
|
@@ -3,18 +3,18 @@ Endpoints related to projects.
|
|
|
3
3
|
|
|
4
4
|
https://xtools.readthedocs.io/en/stable/api/project.html
|
|
5
5
|
"""
|
|
6
|
-
|
|
7
|
-
from typing import Optional, Sequence
|
|
6
|
+
from collections.abc import Sequence
|
|
8
7
|
from datetime import date
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
from . import base
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def _get_project_dict(what: str, project: str) -> dict:
|
|
14
|
-
return base.get("/project
|
|
13
|
+
def _get_project_dict(what: str, project: str) -> dict[str, Any]:
|
|
14
|
+
return base.get(f"/project/{what}/{project}")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def normalize_project(project: str) -> dict:
|
|
17
|
+
def normalize_project(project: str) -> dict[str, Any]:
|
|
18
18
|
"""
|
|
19
19
|
https://xtools.readthedocs.io/en/stable/api/project.html#normalize-project
|
|
20
20
|
|
|
@@ -36,7 +36,7 @@ def normalize_project(project: str) -> dict:
|
|
|
36
36
|
return _get_project_dict("normalize", project)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def namespaces(project: str) -> dict:
|
|
39
|
+
def namespaces(project: str) -> dict[str, Any]:
|
|
40
40
|
"""
|
|
41
41
|
https://xtools.readthedocs.io/en/stable/api/project.html#namespaces
|
|
42
42
|
|
|
@@ -46,7 +46,7 @@ def namespaces(project: str) -> dict:
|
|
|
46
46
|
return _get_project_dict("namespaces", project)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def page_assessments(project: str) -> dict:
|
|
49
|
+
def page_assessments(project: str) -> dict[str, Any]:
|
|
50
50
|
"""
|
|
51
51
|
https://xtools.readthedocs.io/en/stable/api/project.html#page-assessments
|
|
52
52
|
|
|
@@ -56,7 +56,7 @@ def page_assessments(project: str) -> dict:
|
|
|
56
56
|
return _get_project_dict("assessments", project)
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def page_assessments_configuration() -> dict:
|
|
59
|
+
def page_assessments_configuration() -> dict[str, Any]:
|
|
60
60
|
"""
|
|
61
61
|
Return the list of wikis that support page assessments and the configuration for each.
|
|
62
62
|
|
|
@@ -83,7 +83,7 @@ def page_assessments_configuration() -> dict:
|
|
|
83
83
|
return base.get("/project/assessments")
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def automated_tools(project: str) -> dict:
|
|
86
|
+
def automated_tools(project: str) -> dict[str, Any]:
|
|
87
87
|
"""
|
|
88
88
|
Return a list of known (semi-)automated tools used on the project.
|
|
89
89
|
|
|
@@ -117,7 +117,7 @@ def automated_tools(project: str) -> dict:
|
|
|
117
117
|
return _get_project_dict("automated_tools", project)
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
def admins_and_user_groups(project: str) -> dict:
|
|
120
|
+
def admins_and_user_groups(project: str) -> dict[str, Any]:
|
|
121
121
|
"""
|
|
122
122
|
https://xtools.readthedocs.io/en/stable/api/project.html#admins-and-user-groups
|
|
123
123
|
|
|
@@ -128,9 +128,9 @@ def admins_and_user_groups(project: str) -> dict:
|
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
def _get_project_stats_dict(what: str, project: str,
|
|
131
|
-
start:
|
|
132
|
-
end:
|
|
133
|
-
actions:
|
|
131
|
+
start: date | None = None,
|
|
132
|
+
end: date | None = None,
|
|
133
|
+
actions: Sequence[str] | None = None) -> dict[str, Any]:
|
|
134
134
|
path = base.build_path("/project/{what}/{project}/{start}/{end}", (
|
|
135
135
|
("what", what, ""),
|
|
136
136
|
("project", project, ""),
|
|
@@ -146,9 +146,9 @@ def _get_project_stats_dict(what: str, project: str,
|
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
def admin_statistics(project: str,
|
|
149
|
-
start:
|
|
150
|
-
end:
|
|
151
|
-
actions:
|
|
149
|
+
start: date | None = None,
|
|
150
|
+
end: date | None = None,
|
|
151
|
+
actions: Sequence[str] | None = None) -> dict[str, Any]:
|
|
152
152
|
"""
|
|
153
153
|
Return admin users of the project with counts of the actions they took.
|
|
154
154
|
|
|
@@ -167,27 +167,27 @@ def admin_statistics(project: str,
|
|
|
167
167
|
|
|
168
168
|
|
|
169
169
|
def patroller_statistics(project: str,
|
|
170
|
-
start:
|
|
171
|
-
end:
|
|
172
|
-
actions:
|
|
170
|
+
start: date | None = None,
|
|
171
|
+
end: date | None = None,
|
|
172
|
+
actions: Sequence[str] | None = None) -> dict[str, Any]:
|
|
173
173
|
"""
|
|
174
174
|
Same as ``admin_statistics`` with different actions.
|
|
175
175
|
|
|
176
176
|
https://xtools.readthedocs.io/en/stable/api/project.html#patroller-statistics
|
|
177
177
|
|
|
178
|
-
:param project:
|
|
179
|
-
:param start:
|
|
180
|
-
:param end:
|
|
178
|
+
:param project:
|
|
179
|
+
:param start:
|
|
180
|
+
:param end:
|
|
181
181
|
:param actions: available actions include: ``patrol``, ``page-curation``, ``pc-accept``, ``pc-reject``.
|
|
182
|
-
:return:
|
|
182
|
+
:return:
|
|
183
183
|
"""
|
|
184
184
|
return _get_project_stats_dict("patroller_stats", project, start, end, actions)
|
|
185
185
|
|
|
186
186
|
|
|
187
187
|
def steward_statistics(project: str,
|
|
188
|
-
start:
|
|
189
|
-
end:
|
|
190
|
-
actions:
|
|
188
|
+
start: date | None = None,
|
|
189
|
+
end: date | None = None,
|
|
190
|
+
actions: Sequence[str] | None = None) -> dict[str, Any]:
|
|
191
191
|
"""
|
|
192
192
|
Same as ``admin_statistics`` with different actions.
|
|
193
193
|
|
|
@@ -6,8 +6,9 @@ A Quote is a named tuple with ``id`` and ``text`` fields.
|
|
|
6
6
|
https://xtools.readthedocs.io/en/stable/api/quote.html
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from typing import Sequence
|
|
10
9
|
from collections import namedtuple
|
|
10
|
+
from collections.abc import Sequence
|
|
11
|
+
from urllib.parse import quote as urlquote
|
|
11
12
|
|
|
12
13
|
from . import base
|
|
13
14
|
|
|
@@ -44,7 +45,7 @@ def single_quote(quote_id: int) -> Quote:
|
|
|
44
45
|
:param quote_id:
|
|
45
46
|
:return: Quote.
|
|
46
47
|
"""
|
|
47
|
-
return _get_quotes("/quote
|
|
48
|
+
return _get_quotes(f"/quote/{urlquote(str(quote_id))}")[0]
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def all_quotes() -> Sequence[Quote]:
|
|
@@ -4,16 +4,18 @@ Endpoints related to users.
|
|
|
4
4
|
https://xtools.readthedocs.io/en/stable/api/user.html
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from collections.abc import Generator, Sequence
|
|
8
8
|
from datetime import date
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
from . import base
|
|
12
|
+
import contextlib
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def simple_edit_count(project: str, username: str,
|
|
14
|
-
namespace:
|
|
15
|
-
start:
|
|
16
|
-
end:
|
|
16
|
+
namespace: int | None = None,
|
|
17
|
+
start: date | None = None,
|
|
18
|
+
end: date | None = None) -> dict[str, Any]:
|
|
17
19
|
"""
|
|
18
20
|
https://xtools.readthedocs.io/en/stable/api/user.html#simple-edit-count
|
|
19
21
|
|
|
@@ -51,11 +53,11 @@ def simple_edit_count(project: str, username: str,
|
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
def number_of_pages_created(project: str, username: str,
|
|
54
|
-
namespace:
|
|
55
|
-
redirects:
|
|
56
|
-
deleted:
|
|
57
|
-
start:
|
|
58
|
-
end:
|
|
56
|
+
namespace: str | None = None,
|
|
57
|
+
redirects: str | None = None,
|
|
58
|
+
deleted: str | None = None,
|
|
59
|
+
start: date | None = None,
|
|
60
|
+
end: date | None = None) -> dict[str, Any]:
|
|
59
61
|
"""
|
|
60
62
|
Return the number of pages created by a user along with some other info.
|
|
61
63
|
|
|
@@ -99,7 +101,7 @@ def number_of_pages_created(project: str, username: str,
|
|
|
99
101
|
return base.get(path)
|
|
100
102
|
|
|
101
103
|
|
|
102
|
-
def _fix_page(page: dict) -> dict:
|
|
104
|
+
def _fix_page(page: dict[str, Any]) -> dict[str, Any]:
|
|
103
105
|
"""
|
|
104
106
|
Fix a page dict as returned by the API.
|
|
105
107
|
:param page:
|
|
@@ -113,13 +115,13 @@ def _fix_page(page: dict) -> dict:
|
|
|
113
115
|
|
|
114
116
|
|
|
115
117
|
def pages_created(project: str, username: str,
|
|
116
|
-
namespace:
|
|
117
|
-
redirects:
|
|
118
|
-
deleted:
|
|
119
|
-
start:
|
|
120
|
-
end:
|
|
121
|
-
offset:
|
|
122
|
-
all_times: bool = False) -> dict:
|
|
118
|
+
namespace: str | None = None,
|
|
119
|
+
redirects: str | None = None,
|
|
120
|
+
deleted: str | None = None,
|
|
121
|
+
start: date | str | None = None,
|
|
122
|
+
end: date | str | None = None,
|
|
123
|
+
offset: int | None = None,
|
|
124
|
+
all_times: bool = False) -> dict[str, Any]:
|
|
123
125
|
"""
|
|
124
126
|
Return pages created by a user. This does not handle pagination; see ``pages_created_iter`` for that.
|
|
125
127
|
|
|
@@ -194,10 +196,8 @@ def pages_created(project: str, username: str,
|
|
|
194
196
|
keys = sorted(pages.keys(), key=int)
|
|
195
197
|
|
|
196
198
|
is_namespaced = False
|
|
197
|
-
|
|
199
|
+
with contextlib.suppress(StopIteration):
|
|
198
200
|
is_namespaced = isinstance(next(pages.values().__iter__()), list)
|
|
199
|
-
except StopIteration:
|
|
200
|
-
pass
|
|
201
201
|
|
|
202
202
|
if is_namespaced:
|
|
203
203
|
ret["pages"] = [_fix_page(page) for k in keys for page in pages[k]]
|
|
@@ -208,12 +208,12 @@ def pages_created(project: str, username: str,
|
|
|
208
208
|
|
|
209
209
|
|
|
210
210
|
def pages_created_iter(project: str, username: str,
|
|
211
|
-
namespace:
|
|
212
|
-
redirects:
|
|
213
|
-
deleted:
|
|
214
|
-
start:
|
|
215
|
-
end:
|
|
216
|
-
all_times: bool = False) -> Generator[dict,
|
|
211
|
+
namespace: str | None = None,
|
|
212
|
+
redirects: str | None = None,
|
|
213
|
+
deleted: str | None = None,
|
|
214
|
+
start: date | None = None,
|
|
215
|
+
end: date | None = None,
|
|
216
|
+
all_times: bool = False) -> Generator[dict[str, Any]]:
|
|
217
217
|
"""
|
|
218
218
|
Equivalent of ``pages_created`` that yields page dicts and does the pagination for you.
|
|
219
219
|
|
|
@@ -235,23 +235,22 @@ def pages_created_iter(project: str, username: str,
|
|
|
235
235
|
and ``end``.
|
|
236
236
|
:return:
|
|
237
237
|
"""
|
|
238
|
-
offset:
|
|
238
|
+
offset: int | None = 0
|
|
239
239
|
|
|
240
240
|
while offset is not None:
|
|
241
241
|
ret = pages_created(project, username,
|
|
242
242
|
namespace=namespace, redirects=redirects, deleted=deleted,
|
|
243
243
|
start=start, end=end, all_times=all_times, offset=offset)
|
|
244
244
|
offset = ret.get("continue")
|
|
245
|
-
|
|
246
|
-
yield page
|
|
245
|
+
yield from ret["pages"]
|
|
247
246
|
|
|
248
247
|
|
|
249
248
|
def automated_edit_counter(project: str, username: str,
|
|
250
|
-
namespace:
|
|
251
|
-
start:
|
|
252
|
-
end:
|
|
253
|
-
offset:
|
|
254
|
-
tools: bool = False) -> dict:
|
|
249
|
+
namespace: str | None = None,
|
|
250
|
+
start: date | None = None,
|
|
251
|
+
end: date | None = None,
|
|
252
|
+
offset: int | None = None,
|
|
253
|
+
tools: bool = False) -> dict[str, Any]:
|
|
255
254
|
"""
|
|
256
255
|
https://xtools.readthedocs.io/en/stable/api/user.html#automated-edit-counter
|
|
257
256
|
|
|
@@ -278,10 +277,10 @@ def automated_edit_counter(project: str, username: str,
|
|
|
278
277
|
return base.get(path)
|
|
279
278
|
|
|
280
279
|
|
|
281
|
-
def _edits(what: str, project: str, username: str, namespace:
|
|
282
|
-
start:
|
|
283
|
-
end:
|
|
284
|
-
offset:
|
|
280
|
+
def _edits(what: str, project: str, username: str, namespace: str | None = None,
|
|
281
|
+
start: date | None = None,
|
|
282
|
+
end: date | None = None,
|
|
283
|
+
offset: int | None = None) -> dict[str, Any]:
|
|
285
284
|
"""
|
|
286
285
|
Base function for both ``non_automated_edits`` and ``automated_edits``.
|
|
287
286
|
|
|
@@ -307,10 +306,10 @@ def _edits(what: str, project: str, username: str, namespace: Optional[str] = No
|
|
|
307
306
|
|
|
308
307
|
|
|
309
308
|
def non_automated_edits(project: str, username: str,
|
|
310
|
-
namespace:
|
|
311
|
-
start:
|
|
312
|
-
end:
|
|
313
|
-
offset:
|
|
309
|
+
namespace: str | None = None,
|
|
310
|
+
start: date | None = None,
|
|
311
|
+
end: date | None = None,
|
|
312
|
+
offset: int | None = None) -> dict[str, Any]:
|
|
314
313
|
"""
|
|
315
314
|
https://xtools.readthedocs.io/en/stable/api/user.html#non-automated-edits
|
|
316
315
|
|
|
@@ -327,10 +326,10 @@ def non_automated_edits(project: str, username: str,
|
|
|
327
326
|
|
|
328
327
|
|
|
329
328
|
def automated_edits(project: str, username: str,
|
|
330
|
-
namespace:
|
|
331
|
-
start:
|
|
332
|
-
end:
|
|
333
|
-
offset:
|
|
329
|
+
namespace: str | None = None,
|
|
330
|
+
start: date | None = None,
|
|
331
|
+
end: date | None = None,
|
|
332
|
+
offset: int | None = None) -> dict[str, Any]:
|
|
334
333
|
"""
|
|
335
334
|
https://xtools.readthedocs.io/en/stable/api/user.html#automated-edits
|
|
336
335
|
|
|
@@ -347,9 +346,9 @@ def automated_edits(project: str, username: str,
|
|
|
347
346
|
|
|
348
347
|
|
|
349
348
|
def edit_summaries(project: str, username: str,
|
|
350
|
-
namespace:
|
|
351
|
-
start:
|
|
352
|
-
end:
|
|
349
|
+
namespace: str | None = None,
|
|
350
|
+
start: date | None = None,
|
|
351
|
+
end: date | None = None) -> dict[str, Any]:
|
|
353
352
|
"""
|
|
354
353
|
https://xtools.readthedocs.io/en/stable/api/user.html#edit-summaries
|
|
355
354
|
|
|
@@ -371,8 +370,8 @@ def edit_summaries(project: str, username: str,
|
|
|
371
370
|
|
|
372
371
|
|
|
373
372
|
def top_edits(project: str, username: str,
|
|
374
|
-
namespace:
|
|
375
|
-
page_title:
|
|
373
|
+
namespace: str | None = None,
|
|
374
|
+
page_title: str | None = None) -> dict[str, Any]:
|
|
376
375
|
"""
|
|
377
376
|
Return the top-edited pages by a user, or all edits made by a user to a specific page.
|
|
378
377
|
|
|
@@ -395,8 +394,8 @@ def top_edits(project: str, username: str,
|
|
|
395
394
|
|
|
396
395
|
|
|
397
396
|
def category_edit_counter(project: str, username: str, categories: Sequence[str],
|
|
398
|
-
start:
|
|
399
|
-
end:
|
|
397
|
+
start: date | None = None,
|
|
398
|
+
end: date | None = None) -> dict[str, Any]:
|
|
400
399
|
"""
|
|
401
400
|
https://xtools.readthedocs.io/en/stable/api/user.html#category-edit-counter
|
|
402
401
|
|
|
@@ -417,7 +416,7 @@ def category_edit_counter(project: str, username: str, categories: Sequence[str]
|
|
|
417
416
|
return base.get(path)
|
|
418
417
|
|
|
419
418
|
|
|
420
|
-
def log_counts(project: str, username: str) -> dict:
|
|
419
|
+
def log_counts(project: str, username: str) -> dict[str, Any]:
|
|
421
420
|
"""
|
|
422
421
|
Return counts of logged actions made by a user.
|
|
423
422
|
|
|
@@ -445,10 +444,10 @@ def log_counts(project: str, username: str) -> dict:
|
|
|
445
444
|
:param username:
|
|
446
445
|
:return:
|
|
447
446
|
"""
|
|
448
|
-
return base.get("/user/log_counts
|
|
447
|
+
return base.get(f"/user/log_counts/{project}/{username}")
|
|
449
448
|
|
|
450
449
|
|
|
451
|
-
def namespace_totals(project: str, username: str) -> dict:
|
|
450
|
+
def namespace_totals(project: str, username: str) -> dict[str, Any]:
|
|
452
451
|
"""
|
|
453
452
|
Return the edit count for each namespace with at least 1 edit.
|
|
454
453
|
|
|
@@ -458,10 +457,10 @@ def namespace_totals(project: str, username: str) -> dict:
|
|
|
458
457
|
:param username:
|
|
459
458
|
:return:
|
|
460
459
|
"""
|
|
461
|
-
return base.get("/user/namespace_totals
|
|
460
|
+
return base.get(f"/user/namespace_totals/{project}/{username}")
|
|
462
461
|
|
|
463
462
|
|
|
464
|
-
def month_counts(project: str, username: str) -> dict:
|
|
463
|
+
def month_counts(project: str, username: str) -> dict[str, Any]:
|
|
465
464
|
"""
|
|
466
465
|
Return the edit count of a user, grouped by namespace then year and month.
|
|
467
466
|
|
|
@@ -471,10 +470,10 @@ def month_counts(project: str, username: str) -> dict:
|
|
|
471
470
|
:param username:
|
|
472
471
|
:return:
|
|
473
472
|
"""
|
|
474
|
-
return base.get("/user/month_counts
|
|
473
|
+
return base.get(f"/user/month_counts/{project}/{username}")
|
|
475
474
|
|
|
476
475
|
|
|
477
|
-
def time_card(project: str, username: str) -> dict:
|
|
476
|
+
def time_card(project: str, username: str) -> dict[str, Any]:
|
|
478
477
|
"""
|
|
479
478
|
Return the relative distribution of edits made by a user based on hour of the day and day of the week.
|
|
480
479
|
|
|
@@ -501,4 +500,4 @@ def time_card(project: str, username: str) -> dict:
|
|
|
501
500
|
:param username:
|
|
502
501
|
:return:
|
|
503
502
|
"""
|
|
504
|
-
return base.get("/user/timecard
|
|
503
|
+
return base.get(f"/user/timecard/{project}/{username}")
|
xtools-0.2.0/LICENSE
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
Copyright © 2020-2023 – Baptiste Fontaine
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
-
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
-
the Software without restriction, including without limitation the rights to
|
|
6
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
-
subject to the following conditions:
|
|
9
|
-
|
|
10
|
-
The above copyright notice and this permission notice shall be included in all
|
|
11
|
-
copies or substantial portions of the Software.
|
|
12
|
-
|
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
xtools-0.2.0/pyproject.toml
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "xtools"
|
|
3
|
-
version = "0.2.0"
|
|
4
|
-
description = "XTools API wrapper"
|
|
5
|
-
authors = ["Baptiste Fontaine <b@ptistefontaine.fr>"]
|
|
6
|
-
include = ["xtools/py.typed"]
|
|
7
|
-
readme = "README.md"
|
|
8
|
-
homepage = "https://github.com/bfontaine/xtools"
|
|
9
|
-
classifiers = [
|
|
10
|
-
"Development Status :: 5 - Production/Stable",
|
|
11
|
-
"Intended Audience :: Developers",
|
|
12
|
-
"License :: OSI Approved :: MIT License",
|
|
13
|
-
"Operating System :: OS Independent",
|
|
14
|
-
"Programming Language :: Python :: 3",
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
[tool.poetry.dependencies]
|
|
18
|
-
python = ">=3.8,<4.0"
|
|
19
|
-
requests = "^2.28.2"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
[tool.poetry.group.dev.dependencies]
|
|
23
|
-
types-requests = "^2.28.11.17"
|
|
24
|
-
mypy = "^1"
|
|
25
|
-
pytest = "^7.2.2"
|
|
26
|
-
sphinx = "^6.1.3"
|
|
27
|
-
requests-mock = "^1.10.0"
|
|
28
|
-
|
|
29
|
-
[build-system]
|
|
30
|
-
requires = ["poetry-core"]
|
|
31
|
-
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|