This commit is contained in:
Edward Betts 2023-09-29 14:17:56 +01:00
parent 7813d1a7f0
commit b6953cf52f
7 changed files with 135 additions and 34 deletions

View file

@ -30,19 +30,32 @@ def call(params: dict[str, str | int]) -> dict[str, Any]:
return data.json() return data.json()
def get_content(title: str) -> str: def article_exists(title: str) -> bool:
"""Get article text."""
params: dict[str, str | int] = {
"action": "query",
"format": "json",
"formatversion": 2,
"titles": title,
}
return not call(params)["query"]["pages"][0].get("missing")
def get_content(title: str) -> tuple[str, int]:
"""Get article text.""" """Get article text."""
params: dict[str, str | int] = { params: dict[str, str | int] = {
"action": "query", "action": "query",
"format": "json", "format": "json",
"formatversion": 2, "formatversion": 2,
"prop": "revisions|info", "prop": "revisions|info",
"rvprop": "content|timestamp", "rvprop": "content|timestamp|ids",
"titles": title, "titles": title,
} }
data = call(params) data = call(params)
rev: str = data["query"]["pages"][0]["revisions"][0]["content"] rev = data["query"]["pages"][0]["revisions"][0]
return rev content: str = rev["content"]
revid: int = int(rev["revid"])
return content, revid
def compare(title: str, new_text: str) -> str: def compare(title: str, new_text: str) -> str:
@ -58,3 +71,21 @@ def compare(title: str, new_text: str) -> str:
} }
diff: str = call(params)["compare"]["body"] diff: str = call(params)["compare"]["body"]
return diff return diff
def edit_page(
title: str, text: str, summary: str, baserevid: str, token: str
) -> dict[str, str | int]:
"""Edit a page on Wikipedia."""
params: dict[str, str | int] = {
"format": "json",
"formatversion": 2,
"action": "edit",
"title": title,
"text": text,
"baserevid": baserevid,
"token": token,
"summary": summary,
}
edit: str = call(params)["edit"]
return edit

View file

@ -163,9 +163,9 @@ class Article:
self.parse: Optional[dict[str, Any]] = None self.parse: Optional[dict[str, Any]] = None
self.dab_html: dict[str, str] = {} self.dab_html: dict[str, str] = {}
def save_endpoint(self) -> str: def preview_endpoint(self) -> str:
"""Endpoint for saving changes.""" """Endpoint for saving changes."""
href: str = flask.url_for("save", enwiki=self.enwiki.replace(" ", "_")) href: str = flask.url_for("preview", enwiki=self.enwiki.replace(" ", "_"))
return href return href
def load(self) -> None: def load(self) -> None:

View file

@ -53,7 +53,7 @@ a.new { color: red; }
<div id="dabs" class="p-3"> <div id="dabs" class="p-3">
<h1>{{ article.enwiki }}</h1> <h1>{{ article.enwiki }}</h1>
<div id="save-panel" class="d-none"> <div id="save-panel" class="d-none">
<form method="POST" action="{{ article.save_endpoint() }}"> <form method="POST" action="{{ article.preview_endpoint() }}">
<button class="btn btn-primary" id="save-btn">Preview before save</button> <button class="btn btn-primary" id="save-btn">Preview before save</button>
<span id="edit-count"></span> <span id="edit-count"></span>
<input type="hidden" value="{}" id="save-edits" name="edits"> <input type="hidden" value="{}" id="save-edits" name="edits">

View file

@ -1,7 +1,21 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}DAB Mechanic{% endblock %}
{% block content %} {% block content %}
<div class="m-3"> <div class="m-3">
<form>
article title:
<input name="title" value="{{ request.args.get("title", "") }}">
<button class="btn btn-sm btn-primary">go</button>
</form>
{% if title and not exists %}
<p>No article titled "{{ title }}" found in Wikipedia.</p>
{% endif %}
<ol> <ol>
{% for enwiki, count in articles %} {% for enwiki, count in articles %}
<li> <li>

View file

@ -15,13 +15,7 @@
<a class="navbar-brand" href="{{ url_for('index') }}">Dab Mechanic</a> <a class="navbar-brand" href="{{ url_for('index') }}">Dab Mechanic</a>
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a> <a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li> </li>
</ul> </ul>

View file

@ -30,7 +30,10 @@
</tbody> </tbody>
</table> </table>
<form method="POST" action="{{ url_for("save", enwiki=title) }}">
<button class="btn btn-primary" id="save-btn">Save changes</button> <button class="btn btn-primary" id="save-btn">Save changes</button>
<input type="hidden" value="{{ request.form.edits }}" id="save-edits" name="edits">
</form>
</body> </body>
</html> </html>

View file

@ -5,6 +5,7 @@ import json
import re import re
from typing import Optional, TypedDict from typing import Optional, TypedDict
import mwparserfromhell import mwparserfromhell
from pprint import pprint
import flask import flask
import lxml.html import lxml.html
@ -65,13 +66,25 @@ def parse_articles_with_dab_links(root: lxml.html.Element) -> list[tuple[str, in
@app.route("/") @app.route("/")
def index(): def index():
title = flask.request.args.get("title")
exists = None
if title:
title = title.strip()
exists = mediawiki_api.article_exists(title)
if exists:
return flask.redirect(
flask.url_for("article_page", enwiki=title.replace(" ", "_"))
)
r = requests.get(awdl_url, params={"limit": 100}) r = requests.get(awdl_url, params={"limit": 100})
root = lxml.html.fromstring(r.content) root = lxml.html.fromstring(r.content)
articles = parse_articles_with_dab_links(root) articles = parse_articles_with_dab_links(root)
# articles = [line[:-1] for line in open("article_list")] # articles = [line[:-1] for line in open("article_list")]
return flask.render_template("index.html", articles=articles) return flask.render_template(
"index.html", title=title, exists=exists, articles=articles,
)
class Edit(TypedDict): class Edit(TypedDict):
@ -82,7 +95,7 @@ class Edit(TypedDict):
title: str title: str
def apply_edits(article_text: str, edits: list[Edit]) -> str: def old_apply_edits(article_text: str, edits: list[Edit]) -> str:
"""Apply edits to article text.""" """Apply edits to article text."""
def escape(s: str) -> str: def escape(s: str) -> str:
@ -114,49 +127,92 @@ def build_edit_summary(edits: list[Edit]) -> str:
return f"Disambiguate {titles} using [[User:Edward/Dab mechanic]]" return f"Disambiguate {titles} using [[User:Edward/Dab mechanic]]"
def get_links(wikicode, edits):
def get_links(wikicode, dab_links):
edits = [edit for edit in dab_links if edit.get("title")]
dab_titles = {dab["link_to"] for dab in edits} dab_titles = {dab["link_to"] for dab in edits}
return [ return [
link for link in wikicode.filter_wikilinks() if str(link.title) in dab_titles link for link in wikicode.filter_wikilinks() if str(link.title) in dab_titles
] ]
@app.route("/preview/<path:enwiki>", methods=["POST"]) def apply_edits(text, dab_links):
def preview(enwiki: str) -> Response | str:
"""Save edits to article."""
enwiki = enwiki.replace("_", " ")
dab_links = json.loads(flask.request.form["edits"])
edits = [edit for edit in dab_links if edit.get("title")]
edit_summary = build_edit_summary(edits)
# return flask.jsonify(edits=dab_links, edit_summary=edit_summary)
text = mediawiki_api.get_content(enwiki)
wikicode = mwparserfromhell.parse(text) wikicode = mwparserfromhell.parse(text)
links = get_links(wikicode, dab_links) links = get_links(wikicode, dab_links)
if len(links) != len(dab_links):
print("links:", len(links))
print("dab_links:", len(dab_links))
print("dab_links:", dab_links)
assert len(links) == len(dab_links) assert len(links) == len(dab_links)
for wikilink, edit in zip(links, dab_links): for wikilink, edit in zip(links, dab_links):
print(edit, wikilink)
if not edit.get("title"): if not edit.get("title"):
continue continue
if not wikilink.text: if not wikilink.text:
wikilink.text = wikilink.title wikilink.text = wikilink.title
wikilink.title = edit["title"] wikilink.title = edit["title"]
diff = mediawiki_api.compare(enwiki, str(wikicode)) return str(wikicode)
@app.route("/preview/<path:enwiki>", methods=["POST"])
def preview(enwiki: str) -> Response | str:
"""Preview article edits."""
enwiki = enwiki.replace("_", " ")
dab_links = json.loads(flask.request.form["edits"])
dab_links = [link for link in dab_links if "title" in link]
cur_text, baserevid = mediawiki_api.get_content(enwiki)
text = apply_edits(cur_text, dab_links)
diff = mediawiki_api.compare(enwiki, text)
return flask.render_template( return flask.render_template(
"peview.html", "preview.html",
edit_summary=edit_summary, edit_summary=build_edit_summary(dab_links),
title=enwiki, title=enwiki,
edits=dab_links, edits=dab_links,
# text=str(wikicode),
diff=diff, diff=diff,
) )
def do_save(enwiki: str):
"""Update page on Wikipedia."""
dab_links = json.loads(flask.request.form["edits"])
dab_links = [link for link in dab_links if "title" in link]
cur_text, baserevid = mediawiki_api.get_content(enwiki)
new_text = apply_edits(cur_text, dab_links)
token = wikidata_oauth.get_token()
summary = build_edit_summary(dab_links)
print(summary)
edit = mediawiki_api.edit_page(
title=enwiki,
text=new_text,
summary=summary,
baserevid=baserevid,
token=token,
)
return edit
@app.route("/save/<path:enwiki>", methods=["GET", "POST"])
def save(enwiki: str) -> Response | str:
"""Save edits to article."""
enwiki_norm = enwiki.replace("_", " ")
if flask.request.method == "GET":
return flask.render_template("edit_saved.html", title=enwiki_norm)
do_save(enwiki_norm)
return flask.redirect(flask.url_for(flask.request.endpoint, enwiki=enwiki))
def redirect_if_needed(enwiki: str) -> Optional[Response]: def redirect_if_needed(enwiki: str) -> Optional[Response]:
"""Check if there are spaces in the article name and redirect.""" """Check if there are spaces in the article name and redirect."""
return ( return (
@ -175,6 +231,9 @@ def article_page(enwiki: str) -> Response:
if redirect: if redirect:
return redirect return redirect
if "owner_key" not in flask.session:
return flask.render_template("login_needed.html")
article = wikipedia.Article(enwiki) article = wikipedia.Article(enwiki)
article.load() article.load()
article.process_links() article.process_links()