diff --git a/add_links/match.py b/add_links/match.py index 6c3a12e..eaac7f9 100644 --- a/add_links/match.py +++ b/add_links/match.py @@ -559,4 +559,6 @@ def get_diff(q: str, title: str, linkto: str | None) -> dict[str, typing.Any]: ) found["diff"] = call_get_diff(title, found["section_num"], section_text) + if not found["diff"]: + raise NoMatch return found diff --git a/add_links/mediawiki_oauth.py b/add_links/mediawiki_oauth.py index 39aecb0..4da3388 100644 --- a/add_links/mediawiki_oauth.py +++ b/add_links/mediawiki_oauth.py @@ -1,5 +1,6 @@ """Wikipedia OAuth.""" +import sys import typing import urllib from typing import cast @@ -73,9 +74,8 @@ def api_request(params: typing.Mapping[str, str | int]) -> dict[str, typing.Any] try: return cast(dict[str, typing.Any], r.json()) except Exception: - print("text") - print(r.text) - print("---") + print(f"API request failed: HTTP {r.status_code}", file=sys.stderr) + print(f"Response body: {r.text!r}", file=sys.stderr) raise @@ -105,7 +105,13 @@ def get_username() -> None | str: return None # not authorized if "username" not in session: - reply = userinfo_call() + try: + reply = userinfo_call() + except Exception as e: + print(f"get_username failed, clearing session: {e}", file=sys.stderr) + session.pop("owner_key", None) + session.pop("owner_secret", None) + return None if "query" not in reply: return None session["username"] = reply["query"]["userinfo"]["name"] diff --git a/static/css/diff.css b/static/css/diff.css index 65d5ef9..c7009f2 100644 --- a/static/css/diff.css +++ b/static/css/diff.css @@ -5,19 +5,16 @@ span.searchmatch { font-weight: bold; } table.diff,td.diff-otitle,td.diff-ntitle{background-color:white} td.diff-otitle,td.diff-ntitle{text-align:center} -td.diff-marker{text-align:right;font-weight:bold;font-size:1.25em} +td.diff-marker{width:1.5em;text-align:center;font-weight:bold;font-size:1.25em;padding:0 0.3em} td.diff-lineno{font-weight:bold} td.diff-addedline,td.diff-deletedline,td.diff-context{font-size:88%;vertical-align:top;white-space:-moz-pre-wrap;white-space:pre-wrap} -td.diff-addedline,td.diff-deletedline{border-style:solid;border-width:1px 1px 1px 4px;border-radius:0.33em} -td.diff-addedline{border-color:#a3d3ff} -td.diff-deletedline{border-color:#ffe49c} -td.diff-context{background:#f3f3f3;color:#333333;border-style:solid;border-width:1px 1px 1px 4px;border-color:#e6e6e6;border-radius:0.33em} +td.diff-addedline,td.diff-deletedline{border-left:3px solid} +td.diff-addedline{border-color:#a3d3ff;background:#f0f8ff} +td.diff-deletedline{border-color:#ffe49c;background:#fffaf0} +td.diff-context{color:#555} .diffchange{font-weight:bold;text-decoration:none} -table.diff{border:none;width:98%;border-spacing:4px; table-layout:fixed} -td.diff-addedline .diffchange,td.diff-deletedline .diffchange{border-radius:0.33em;padding:0.25em 0} +table.diff{border:none;width:100%;border-spacing:0;border-collapse:collapse;table-layout:auto} td.diff-addedline .diffchange{background:#d8ecff} td.diff-deletedline .diffchange{background:#feeec8} -table.diff td{padding:0.33em 0.66em} -table.diff col.diff-marker{width:2%} -table.diff col.diff-content{width:48%} -table.diff td div{ word-wrap:break-word; overflow:auto} +table.diff td{padding:0.2em 0.5em} +table.diff td div{word-wrap:break-word;overflow:auto} diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..181c415 --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,3 @@ + + 🔗 + diff --git a/templates/all_done.html b/templates/all_done.html index c346482..e3135f7 100644 --- a/templates/all_done.html +++ b/templates/all_done.html @@ -1,10 +1,11 @@ {% extends "base.html" %} -{% block title %}Index{% endblock %} +{% block title %}All done{% endblock %} {% block content %} -
-

All done

-
back to index
-
+
+

All done

+

No more candidates found for this article.

+ Search another article +
{% endblock %} diff --git a/templates/article.html b/templates/article.html index 55d05b0..4d5c97f 100644 --- a/templates/article.html +++ b/templates/article.html @@ -1,48 +1,54 @@ {% extends "base.html" %} -{% block title %}Link '{{ title }}' in '{{ hit_title }}'{% endblock %} +{% block title %}{{ title }} in {{ hit_title }}{% endblock %} {% block style %} {% endblock %} {% block content %} -
-

Link '{{ title }}' in '{{ hit_title }}'

-
- - -
+
+ -
Username: {{ g.user }}
+
+

Link "{{ title }}" in "{{ hit_title }}"

+ {{ title }} ↗ + {{ hit_title }} ↗ +
-
view article
+
+ {{ total }} mentions total + {{ with_link }} already linked ({{ "{:.0%}".format(with_link / total) }}) +
-
back to index
+
+ {{ diff | safe }}
+
-
total: {{ total }}
-
with link: {{ with_link }}
-
ratio: {{ "{:.1%}".format(with_link / total) }}
- {#
hit: {{ hit }}
#} -
replacement: {{ found.replacement }}
-
section: {{ found.section }}
- - {{ diff | safe }} -
-
- -
- - skip -
-
+
+ +
+ + Skip +
+
-
    + {% if hits %} +
    + {{ hits | length }} other candidates +
      {% for hit in hits %} - {% set url = url_for("article_page", url_title=url_title, title=hit.title) %} -
    1. {{ hit.title }} – {{ hit.snippet | safe }}
    2. +
    3. + {{ hit.title }} +
    4. {% endfor %}
    -
+ + {% endif %} +
{% endblock %} - diff --git a/templates/base.html b/templates/base.html index d29dec1..7043811 100644 --- a/templates/base.html +++ b/templates/base.html @@ -4,19 +4,33 @@ - - - {% block title %}{% endblock %} - - + + {% block title %}{% endblock %} – Find Link {% block style %}{% endblock %} + + {% block content %}{% endblock %} - - + {% block script %}{% endblock %} diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..c1a5018 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block title %}Error{% endblock %} + +{% block content %} +
+
+
+
+

Something went wrong

+

{{ message }}

+
+ Back to home +
+
+
+{% endblock %} diff --git a/templates/index.html b/templates/index.html index feab172..af5314b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,25 +1,44 @@ {% extends "base.html" %} -{% block title %}Index{% endblock %} +{% block title %}Find Link{% endblock %} {% block content %} -
-

Index

-
- - -
- -
Username: {{ g.user }}
- - - {% for item in examples %} - - - - - - {% endfor %} -
{{ item.title }}{{ item.total }}{{ "{:.1%}".format(item.with_links / item.total) }}
+
+
+
+

Find Link

+

Find unlinked mentions of a Wikipedia article and add the links.

+
+ + +
+
+ + {% if debug %} +
+
+

Examples

+ + + + + + + + + + {% for item in examples %} + + + + + + {% endfor %} + +
ArticleTotal% linked
{{ item.title }}{{ item.total }}{{ "{:.0%}".format(item.with_links / item.total) }}
+
+
+ {% endif %} +
{% endblock %} diff --git a/templates/save_done.html b/templates/save_done.html index c96cc66..ffca063 100644 --- a/templates/save_done.html +++ b/templates/save_done.html @@ -1,10 +1,11 @@ {% extends "base.html" %} -{% block title %}Index{% endblock %} +{% block title %}Edit saved{% endblock %} {% block content %} -
-

Save done

-
Save is complete.
-
+
+

Edit saved

+

Your edit has been saved to Wikipedia.

+ Search another article +
{% endblock %} diff --git a/web_view.py b/web_view.py index 32fb1da..c90865e 100755 --- a/web_view.py +++ b/web_view.py @@ -4,11 +4,13 @@ import html import itertools import json import re +import sys import typing import flask import werkzeug from requests_oauthlib import OAuth1Session +from requests_oauthlib.oauth1_session import TokenRequestDenied from werkzeug.wrappers.response import Response from add_links import api, core, mediawiki_api, mediawiki_oauth @@ -118,17 +120,20 @@ def index() -> str | Response: url = flask.url_for("oauth_callback", **flask.request.args) # type: ignore return flask.redirect(url) - examples = load_examples() - examples.sort( - key=lambda i: float(i["with_links"]) / float(i["total"]), reverse=True - ) - if q := flask.request.args.get("q"): if q_trimmed := q.strip(): return flask.redirect(article_url(q_trimmed)) + debug = flask.request.args.get("debug") + examples: list[dict[str, str | int]] = [] + if debug: + examples = load_examples() + examples.sort( + key=lambda i: float(i["with_links"]) / float(i["total"]), reverse=True + ) + return flask.render_template( - "index.html", examples=examples, article_url=article_url + "index.html", examples=examples, article_url=article_url, debug=debug ) @@ -187,7 +192,12 @@ def start_oauth() -> Response: oauth = OAuth1Session(client_key, client_secret=client_secret, callback_uri="oob") oauth.headers.update({"User-Agent": api.ua}) - fetch_response = oauth.fetch_request_token(request_token_url) + try: + fetch_response = oauth.fetch_request_token(request_token_url) + except TokenRequestDenied as e: + return flask.make_response( + flask.render_template("error.html", message=str(e)), 502 + ) flask.session["owner_key"] = fetch_response.get("oauth_token") flask.session["owner_secret"] = fetch_response.get("oauth_token_secret") @@ -229,7 +239,8 @@ def oauth_callback() -> werkzeug.wrappers.response.Response: flask.session["owner_key"] = oauth_tokens.get("oauth_token") flask.session["owner_secret"] = oauth_tokens.get("oauth_token_secret") - print("login successful") + username = mediawiki_oauth.get_username() + print(f"login successful: {username}", file=sys.stderr) next_page = flask.session.get("after_login") return flask.redirect(next_page if next_page else flask.url_for("index"))