From 1c6a83083aa458fb4a12a012b8f815454d90e337 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 8 Sep 2023 12:05:30 +0100 Subject: [PATCH 01/10] Add missing template --- templates/base.html | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 templates/base.html diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..5f2d87e --- /dev/null +++ b/templates/base.html @@ -0,0 +1,25 @@ + + + + + + +{% block title %}Xanadu{% endblock %} + + + +{% block style %} +{% endblock %} + + + +{% block content %} +{% endblock %} + + + +{% block scripts %} +{% endblock %} + + + From 0768cc101c810e92d005c25830a799c06e804adc Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 8 Sep 2023 12:11:42 +0100 Subject: [PATCH 02/10] Combine first and second pages. --- main.py | 16 ++++------ .../{flickr_search.html => combined.html} | 16 +++++++++- templates/start_form.html | 32 ------------------- templates/wikipedia_url.html | 20 ------------ 4 files changed, 22 insertions(+), 62 deletions(-) rename templates/{flickr_search.html => combined.html} (61%) delete mode 100644 templates/start_form.html delete mode 100644 templates/wikipedia_url.html diff --git a/main.py b/main.py index 36e910a..e47275e 100755 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ #!/usr/bin/python3 +"""Find photos on flickr for Wikipedia articls and contact the photographer.""" import collections import json @@ -18,17 +19,14 @@ enwiki = "en.wikipedia.org/wiki/" @app.route("/") def start() -> str: """Start form.""" - return flask.render_template("wikipedia_url.html") - - -@app.route("/flickr") -def flickr_search() -> str: - """Search flickr.""" - wikipedia_url = flask.request.args["wikipedia"] + wikipedia_url = flask.request.args.get("wikipedia") + if not wikipedia_url: + return flask.render_template("combined.html") start = wikipedia_url.find(enwiki) + len(enwiki) - name = unquote(wikipedia_url[start:]).replace("_", " ") return flask.render_template( - "flickr_search.html", name=name, wikipedia_url=wikipedia_url + "combined.html", + name=unquote(wikipedia_url[start:]).replace("_", " "), + wikipedia_url=wikipedia_url, ) diff --git a/templates/flickr_search.html b/templates/combined.html similarity index 61% rename from templates/flickr_search.html rename to templates/combined.html index 9a6d5be..f0c1306 100644 --- a/templates/flickr_search.html +++ b/templates/combined.html @@ -4,7 +4,19 @@ {% block content %}
-

Flickr mail

+
+

Enter URLs

+
+
+ + +
+ + +
+ + {% if name %} +

Wikipedia article: {{ name }}

Search flickr

@@ -18,6 +30,8 @@ + {% endif %}
+
{% endblock %} diff --git a/templates/start_form.html b/templates/start_form.html deleted file mode 100644 index 67e0862..0000000 --- a/templates/start_form.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Flickr Mail - - - -
-
-

Enter URLs

-
-
- - -
- -
- - -
- - -
- -
-
- - - diff --git a/templates/wikipedia_url.html b/templates/wikipedia_url.html deleted file mode 100644 index e31a938..0000000 --- a/templates/wikipedia_url.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Flickr mail{% endblock %} - -{% block content %} -
-
-

Enter URLs

-
-
- - -
- - -
- -
-
-{% endblock %} From 5d257055a6cbb499c33e51a4469185ffe6406241 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 8 Sep 2023 19:03:07 +0100 Subject: [PATCH 03/10] Combine into single page, add copy buttons. --- main.py | 53 ++++++++++++++++++++++++++++++++++++++++- templates/combined.html | 37 ++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index e47275e..9a39761 100755 --- a/main.py +++ b/main.py @@ -22,11 +22,62 @@ def start() -> str: wikipedia_url = flask.request.args.get("wikipedia") if not wikipedia_url: return flask.render_template("combined.html") + start = wikipedia_url.find(enwiki) + len(enwiki) + wiki_part2 = unquote(wikipedia_url[start:]) + + name = wiki_part2 + + if "_(" in name: + name = name[: name.find("_(")] + name = name.replace("_", " ") + + flickr_url = flask.request.args.get("flickr") + if not flickr_url: + return flask.render_template( + "combined.html", + name=name, + wikipedia_url=wikipedia_url, + ) + + wiki_part1 = wikipedia_url[:start] + + if "/in/" in flickr_url: + flickr_url = flickr_url[: flickr_url.find("/in/")] + + flickr_start = "https://flickr.com/photos/" + + assert flickr_url.startswith(flickr_start) + flickr_username = flickr_url[ + len(flickr_start) : flickr_url.find("/", len(flickr_start)) + ] + + # nsid = flickr_usrename_to_nsid(flickr_username) + nsid = "disabled" + assert nsid + print(nsid) + + msg = flask.render_template( + "message.jinja", + flickr_url=flickr_url, + wikipedia_url=wikipedia_url, + name=name, + wiki_part1=wiki_part1, + wiki_part2=wiki_part2, + ) + + subject = f"Request to use your photo of {name} on Wikipedia" + + lines = msg.split("\n\n") + return flask.render_template( "combined.html", - name=unquote(wikipedia_url[start:]).replace("_", " "), + name=name, wikipedia_url=wikipedia_url, + flickr_url=flickr_url, + subject=subject, + lines=lines, + nsid=nsid, ) diff --git a/templates/combined.html b/templates/combined.html index f0c1306..3c846d1 100644 --- a/templates/combined.html +++ b/templates/combined.html @@ -20,11 +20,11 @@

Wikipedia article: {{ name }}

Search flickr

-
+
- +
@@ -32,6 +32,39 @@ {% endif %} + {% if flickr_url %} +

send message +

Subject: {{ subject }} +
+

message + +

+ {% for p in lines %} +

{{ p }}

+ {% endfor %} +
+ + + {% endif %} +
{% endblock %} + +{% block scripts %} + +{% endblock %} From 3f573043a10f348203669fa12c638593b139d827 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 8 Sep 2023 19:03:51 +0100 Subject: [PATCH 04/10] Tidy code --- main.py | 55 +------------------------------------------------------ 1 file changed, 1 insertion(+), 54 deletions(-) diff --git a/main.py b/main.py index 9a39761..cedfd4a 100755 --- a/main.py +++ b/main.py @@ -3,7 +3,6 @@ import collections import json -import sys import typing from urllib.parse import unquote @@ -52,8 +51,7 @@ def start() -> str: len(flickr_start) : flickr_url.find("/", len(flickr_start)) ] - # nsid = flickr_usrename_to_nsid(flickr_username) - nsid = "disabled" + nsid = flickr_usrename_to_nsid(flickr_username) assert nsid print(nsid) @@ -95,56 +93,5 @@ def flickr_usrename_to_nsid(username: str) -> str: return typing.cast(str, params["nsid"]) -@app.route("/message") -def show_message() -> str: - """Show message.""" - flickr_url = flask.request.args["flickr"] - wikipedia_url = flask.request.args["wikipedia"] - - start = wikipedia_url.find(enwiki) + len(enwiki) - wiki_part1 = wikipedia_url[:start] - - if len(sys.argv) > 4: - name = sys.argv[4] - else: - wiki_part2 = unquote(wikipedia_url[start:]) - name = wiki_part2 - - if "_(" in name: - name = name[: name.find("_(")] - name = name.replace("_", " ") - - if "/in/" in flickr_url: - flickr_url = flickr_url[: flickr_url.find("/in/")] - - flickr_start = "https://flickr.com/photos/" - - assert flickr_url.startswith(flickr_start) - flickr_username = flickr_url[ - len(flickr_start) : flickr_url.find("/", len(flickr_start)) - ] - - nsid = flickr_usrename_to_nsid(flickr_username) - assert nsid - print(nsid) - - msg = flask.render_template( - "message.jinja", - flickr_url=flickr_url, - wikipedia_url=wikipedia_url, - name=name, - wiki_part1=wiki_part1, - wiki_part2=wiki_part2, - ) - - subject = f"Request to use your photo of {name} on Wikipedia" - - lines = msg.split("\n\n") - - return flask.render_template( - "show_message.html", subject=subject, lines=lines, nsid=nsid - ) - - if __name__ == "__main__": app.run(host="0.0.0.0") From 0090c3fab6d7a904afd4ff563ed5aa7737ecfebb Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 9 Sep 2023 17:05:11 +0300 Subject: [PATCH 05/10] Add email address --- templates/message.jinja | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/message.jinja b/templates/message.jinja index 9469cfa..7871dc5 100644 --- a/templates/message.jinja +++ b/templates/message.jinja @@ -21,3 +21,5 @@ Thank you for your consideration, and if you have any questions or require furth Warm regards, Edward + +edward@4angle.com From 3c7a81eaf4fe00749cb079f9e8be46ed18c218f4 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 9 Sep 2023 17:05:29 +0300 Subject: [PATCH 06/10] Change page heading. --- templates/combined.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/combined.html b/templates/combined.html index 3c846d1..d98daee 100644 --- a/templates/combined.html +++ b/templates/combined.html @@ -5,7 +5,7 @@ {% block content %}
-

Enter URLs

+

Flickr mail

From 60708b5bb78c86ab462ff5143a697762b08e28f6 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 9 Sep 2023 17:05:38 +0300 Subject: [PATCH 07/10] Avoid crash --- templates/combined.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/combined.html b/templates/combined.html index d98daee..4f40640 100644 --- a/templates/combined.html +++ b/templates/combined.html @@ -52,6 +52,7 @@ {% endblock %} {% block scripts %} +{% if subject and lines %} +{% endif %} {% endblock %} From af1640abecec6f4d664fc2d1cc4e0a8552d6526a Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 9 Sep 2023 17:06:01 +0300 Subject: [PATCH 08/10] Show errors, even in production. --- main.py | 34 ++++++++++++++++- static/css/exception.css | 78 +++++++++++++++++++++++++++++++++++++++ templates/show_error.html | 23 ++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 static/css/exception.css create mode 100644 templates/show_error.html diff --git a/main.py b/main.py index cedfd4a..268400f 100755 --- a/main.py +++ b/main.py @@ -2,19 +2,51 @@ """Find photos on flickr for Wikipedia articls and contact the photographer.""" import collections +import inspect import json +import sys +import traceback import typing from urllib.parse import unquote import flask import requests +import werkzeug +from werkzeug.debug.tbtools import DebugTraceback app = flask.Flask(__name__) -app.debug = True +app.debug = False enwiki = "en.wikipedia.org/wiki/" +@app.errorhandler(werkzeug.exceptions.InternalServerError) +def exception_handler(e: werkzeug.exceptions.InternalServerError) -> tuple[str, int]: + """Handle exception.""" + exec_type, exc_value, current_traceback = sys.exc_info() + assert exc_value + tb = DebugTraceback(exc_value) + + summary = tb.render_traceback_html(include_title=False) + exc_lines = "".join(tb._te.format_exception_only()) + + last_frame = list(traceback.walk_tb(current_traceback))[-1][0] + last_frame_args = inspect.getargs(last_frame.f_code) + + return ( + flask.render_template( + "show_error.html", + plaintext=tb.render_traceback_text(), + exception=exc_lines, + exception_type=tb._te.exc_type.__name__, + summary=summary, + last_frame=last_frame, + last_frame_args=last_frame_args, + ), + 500, + ) + + @app.route("/") def start() -> str: """Start form.""" diff --git a/static/css/exception.css b/static/css/exception.css new file mode 100644 index 0000000..1f141c5 --- /dev/null +++ b/static/css/exception.css @@ -0,0 +1,78 @@ +div.debugger { text-align: left; padding: 12px; margin: auto; + background-color: white; } +div.detail { cursor: pointer; } +div.detail p { margin: 0 0 8px 13px; font-size: 14px; white-space: pre-wrap; + font-family: monospace; } +div.explanation { margin: 20px 13px; font-size: 15px; color: #555; } +div.footer { font-size: 13px; text-align: right; margin: 30px 0; + color: #86989B; } + +h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 9px; + background-color: #11557C; color: white; } +h2 em, h3 em { font-style: normal; color: #A5D6D9; font-weight: normal; } + +div.traceback, div.plain { border: 1px solid #ddd; margin: 0 0 1em 0; padding: 10px; } +div.plain p { margin: 0; } +div.plain textarea, +div.plain pre { margin: 10px 0 0 0; padding: 4px; + background-color: #E8EFF0; border: 1px solid #D3E7E9; } +div.plain textarea { width: 99%; height: 300px; } +div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; } +div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; } +div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; } +div.traceback pre { margin: 0; padding: 5px 0 3px 15px; + background-color: #E8EFF0; border: 1px solid #D3E7E9; } +div.traceback .library .current { background: white; color: #555; } +div.traceback .expanded .current { background: #E8EFF0; color: black; } +div.traceback pre:hover { background-color: #DDECEE; color: black; cursor: pointer; } +div.traceback div.source.expanded pre + pre { border-top: none; } + +div.traceback span.ws { display: none; } +div.traceback pre.before, div.traceback pre.after { display: none; background: white; } +div.traceback div.source.expanded pre.before, +div.traceback div.source.expanded pre.after { + display: block; +} + +div.traceback div.source.expanded span.ws { + display: inline; +} + +div.traceback blockquote { margin: 1em 0 0 0; padding: 0; white-space: pre-line; } +div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; } +div.traceback img:hover { background-color: #ddd; cursor: pointer; + border-color: #BFDDE0; } +div.traceback pre:hover img { display: block; } +div.traceback cite.filename { font-style: normal; color: #3B666B; } + +pre.console { border: 1px solid #ccc; background: white!important; + color: black; padding: 5px!important; + margin: 3px 0 0 0!important; cursor: default!important; + max-height: 400px; overflow: auto; } +pre.console form { color: #555; } +pre.console input { background-color: transparent; color: #555; + width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', monospace; font-size: 14px; + border: none!important; } + +span.string { color: #30799B; } +span.number { color: #9C1A1C; } +span.help { color: #3A7734; } +span.object { color: #485F6E; } +span.extended { opacity: 0.5; } +span.extended:hover { opacity: 1; } +a.toggle { text-decoration: none; background-repeat: no-repeat; + background-position: center center; + background-image: url(?__debugger__=yes&cmd=resource&f=more.png); } +a.toggle:hover { background-color: #444; } +a.open { background-image: url(?__debugger__=yes&cmd=resource&f=less.png); } + +div.traceback pre, div.console pre { + white-space: pre-wrap; /* css-3 should we be so lucky... */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 ?? */ + white-space: -o-pre-wrap; /* Opera 7 ?? */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ + _white-space: pre; /* IE only hack to re-specify in + addition to word-wrap */ +} diff --git a/templates/show_error.html b/templates/show_error.html new file mode 100644 index 0000000..dd79c3c --- /dev/null +++ b/templates/show_error.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block style %} + +{% endblock %} + +{% block content %} +
+ +

Software error: {{ exception_type }}

+
+
{{ exception }}
+
+ +

Traceback (most recent call last)

+{{ summary | safe }} + +

Error in function "{{ last_frame.f_code.co_name }}": {{ last_frame_args | pprint }}

+
{{ last_frame.f_locals | pprint }}
+ +
+ +{% endblock %} From 1ebed94f5db57bdbdf0f88016a72a4ee31cb0759 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 29 Sep 2023 12:28:03 +0100 Subject: [PATCH 09/10] Add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d7f7a42..0c4323f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .mypy_cache +__pycache__ From adc7cffbd63025ad0b3a6df3733da3fd37557168 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Fri, 29 Sep 2023 12:28:23 +0100 Subject: [PATCH 10/10] Fix typo --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 268400f..154b71e 100755 --- a/main.py +++ b/main.py @@ -1,5 +1,5 @@ #!/usr/bin/python3 -"""Find photos on flickr for Wikipedia articls and contact the photographer.""" +"""Find photos on flickr for Wikipedia articles and contact the photographer.""" import collections import inspect