From b6953cf52f1186770b7b03eacf07eb9d780edd81 Mon Sep 17 00:00:00 2001
From: Edward Betts <edward@4angle.com>
Date: Fri, 29 Sep 2023 14:17:56 +0100
Subject: [PATCH] WIP

---
 dab_mechanic/mediawiki_api.py | 39 ++++++++++++--
 dab_mechanic/wikipedia.py     |  4 +-
 templates/article.html        |  2 +-
 templates/index.html          | 14 +++++
 templates/navbar.html         |  8 +--
 templates/preview.html        |  3 ++
 web_view.py                   | 99 ++++++++++++++++++++++++++++-------
 7 files changed, 135 insertions(+), 34 deletions(-)

diff --git a/dab_mechanic/mediawiki_api.py b/dab_mechanic/mediawiki_api.py
index f681af3..265cb73 100644
--- a/dab_mechanic/mediawiki_api.py
+++ b/dab_mechanic/mediawiki_api.py
@@ -30,19 +30,32 @@ def call(params: dict[str, str | int]) -> dict[str, Any]:
     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."""
     params: dict[str, str | int] = {
         "action": "query",
         "format": "json",
         "formatversion": 2,
         "prop": "revisions|info",
-        "rvprop": "content|timestamp",
+        "rvprop": "content|timestamp|ids",
         "titles": title,
     }
     data = call(params)
-    rev: str = data["query"]["pages"][0]["revisions"][0]["content"]
-    return rev
+    rev = data["query"]["pages"][0]["revisions"][0]
+    content: str = rev["content"]
+    revid: int = int(rev["revid"])
+    return content, revid
 
 
 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"]
     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
diff --git a/dab_mechanic/wikipedia.py b/dab_mechanic/wikipedia.py
index 96eb110..fecf7f2 100644
--- a/dab_mechanic/wikipedia.py
+++ b/dab_mechanic/wikipedia.py
@@ -163,9 +163,9 @@ class Article:
         self.parse: Optional[dict[str, Any]] = None
         self.dab_html: dict[str, str] = {}
 
-    def save_endpoint(self) -> str:
+    def preview_endpoint(self) -> str:
         """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
 
     def load(self) -> None:
diff --git a/templates/article.html b/templates/article.html
index 87680b0..eb1e62d 100644
--- a/templates/article.html
+++ b/templates/article.html
@@ -53,7 +53,7 @@ a.new { color: red; }
   <div id="dabs" class="p-3">
     <h1>{{ article.enwiki }}</h1>
     <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>
       <span id="edit-count"></span>
       <input type="hidden" value="{}" id="save-edits" name="edits">
diff --git a/templates/index.html b/templates/index.html
index 1758d8e..d6d6ca3 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,7 +1,21 @@
 {% extends "base.html" %}
 
+{% block title %}DAB Mechanic{% endblock %}
+
 {% block content %}
 <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>
     {% for enwiki, count in articles %}
     <li>
diff --git a/templates/navbar.html b/templates/navbar.html
index 9ce6f07..0b52925 100644
--- a/templates/navbar.html
+++ b/templates/navbar.html
@@ -15,13 +15,7 @@
       <a class="navbar-brand" href="{{ url_for('index') }}">Dab Mechanic</a>
       <ul class="navbar-nav me-auto mb-2 mb-lg-0">
         <li class="nav-item">
-          <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>
+          <a class="nav-link active" aria-current="page" href="/">Home</a>
         </li>
       </ul>
 
diff --git a/templates/preview.html b/templates/preview.html
index 68fa693..cd4bb3a 100644
--- a/templates/preview.html
+++ b/templates/preview.html
@@ -30,7 +30,10 @@
   </tbody>
 </table>
 
+    <form method="POST" action="{{ url_for("save", enwiki=title) }}">
       <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>
 </html>
diff --git a/web_view.py b/web_view.py
index cfdff71..aa962bc 100755
--- a/web_view.py
+++ b/web_view.py
@@ -5,6 +5,7 @@ import json
 import re
 from typing import Optional, TypedDict
 import mwparserfromhell
+from pprint import pprint
 
 import flask
 import lxml.html
@@ -65,13 +66,25 @@ def parse_articles_with_dab_links(root: lxml.html.Element) -> list[tuple[str, in
 
 @app.route("/")
 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})
     root = lxml.html.fromstring(r.content)
     articles = parse_articles_with_dab_links(root)
 
     # 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):
@@ -82,7 +95,7 @@ class Edit(TypedDict):
     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."""
 
     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]]"
 
-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}
     return [
         link for link in wikicode.filter_wikilinks() if str(link.title) in dab_titles
     ]
 
 
-@app.route("/preview/<path:enwiki>", methods=["POST"])
-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)
+def apply_edits(text, dab_links):
     wikicode = mwparserfromhell.parse(text)
     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)
 
     for wikilink, edit in zip(links, dab_links):
-        print(edit, wikilink)
         if not edit.get("title"):
             continue
         if not wikilink.text:
             wikilink.text = wikilink.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(
-        "peview.html",
-        edit_summary=edit_summary,
+        "preview.html",
+        edit_summary=build_edit_summary(dab_links),
         title=enwiki,
         edits=dab_links,
-        # text=str(wikicode),
         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]:
     """Check if there are spaces in the article name and redirect."""
     return (
@@ -175,6 +231,9 @@ def article_page(enwiki: str) -> Response:
     if redirect:
         return redirect
 
+    if "owner_key" not in flask.session:
+        return flask.render_template("login_needed.html")
+
     article = wikipedia.Article(enwiki)
     article.load()
     article.process_links()