Compare commits

..

No commits in common. "adc7cffbd63025ad0b3a6df3733da3fd37557168" and "d29285d468e95cd4865cdc62b399e0a637301c53" have entirely different histories.

9 changed files with 104 additions and 258 deletions

1
.gitignore vendored
View file

@ -1,2 +1 @@
.mypy_cache .mypy_cache
__pycache__

108
main.py
View file

@ -1,78 +1,70 @@
#!/usr/bin/python3 #!/usr/bin/python3
"""Find photos on flickr for Wikipedia articles and contact the photographer."""
import collections import collections
import inspect
import json import json
import sys import sys
import traceback
import typing import typing
from urllib.parse import unquote from urllib.parse import unquote
import flask import flask
import requests import requests
import werkzeug
from werkzeug.debug.tbtools import DebugTraceback
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.debug = False app.debug = True
enwiki = "en.wikipedia.org/wiki/" 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("/") @app.route("/")
def start() -> str: def start() -> str:
"""Start form.""" """Start form."""
wikipedia_url = flask.request.args.get("wikipedia") return flask.render_template("wikipedia_url.html")
if not wikipedia_url:
return flask.render_template("combined.html")
@app.route("/flickr")
def flickr_search() -> str:
"""Search flickr."""
wikipedia_url = flask.request.args["wikipedia"]
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
)
def get_params(line_iter: collections.abc.Iterable[str]) -> str:
"""Find and return params from flickr profile page."""
look_for = 'params: {"isEditingTestimonial":false,'
return next(line[line.find("{") :] for line in line_iter if look_for in line)
def flickr_usrename_to_nsid(username: str) -> str:
"""Get NSID from flickr username."""
url = f"https://www.flickr.com/people/{username}/"
r = requests.get(url)
params = json.loads(get_params(r.text.splitlines()))
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) start = wikipedia_url.find(enwiki) + len(enwiki)
wiki_part2 = unquote(wikipedia_url[start:]) wiki_part1 = wikipedia_url[:start]
if len(sys.argv) > 4:
name = sys.argv[4]
else:
wiki_part2 = unquote(wikipedia_url[start:])
name = wiki_part2 name = wiki_part2
if "_(" in name: if "_(" in name:
name = name[: name.find("_(")] name = name[: name.find("_(")]
name = name.replace("_", " ") 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: if "/in/" in flickr_url:
flickr_url = flickr_url[: flickr_url.find("/in/")] flickr_url = flickr_url[: flickr_url.find("/in/")]
@ -101,29 +93,9 @@ def start() -> str:
lines = msg.split("\n\n") lines = msg.split("\n\n")
return flask.render_template( return flask.render_template(
"combined.html", "show_message.html", subject=subject, lines=lines, nsid=nsid
name=name,
wikipedia_url=wikipedia_url,
flickr_url=flickr_url,
subject=subject,
lines=lines,
nsid=nsid,
) )
def get_params(line_iter: collections.abc.Iterable[str]) -> str:
"""Find and return params from flickr profile page."""
look_for = 'params: {"isEditingTestimonial":false,'
return next(line[line.find("{") :] for line in line_iter if look_for in line)
def flickr_usrename_to_nsid(username: str) -> str:
"""Get NSID from flickr username."""
url = f"https://www.flickr.com/people/{username}/"
r = requests.get(url)
params = json.loads(get_params(r.text.splitlines()))
return typing.cast(str, params["nsid"])
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0") app.run(host="0.0.0.0")

View file

@ -1,78 +0,0 @@
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 */
}

View file

@ -1,72 +0,0 @@
{% extends "base.html" %}
{% block title %}Flickr mail{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<h1>Flickr mail</h1>
<form action="{{ url_for(request.endpoint) }}">
<div class="mb-3">
<label for="wikipedia" class="form-label">Wikipedia URL:</label>
<input type="text" class="form-control" id="wikipedia" name="wikipedia" value="{{ wikipedia_url }}" required>
</div>
<input type="submit" value="Submit">
</form>
{% if name %}
<p>Wikipedia article: {{ name }}</p>
<p><a href="https://flickr.com/search/?view_all=1&safe_search=3&text={{ '"' + name + '"' | urlencode }}">Search flickr</a></p>
<form action="{{ url_for(request.endpoint) }}">
<input type="hidden" name="wikipedia" value="{{ wikipedia_url }}"></input>
<div class="mb-3">
<label for="flickr" class="form-label">Flickr URL:</label>
<input type="text" class="form-control" id="flickr" name="flickr" value="{{ flickr_url }}" required>
</div>
<input type="submit" value="Submit">
</form>
{% endif %}
{% if flickr_url %}
<p><a href="https://www.flickr.com/mail/write/?to={{nsid}}">send message</a>
<div><strong>Subject:</strong> {{ subject }} <button class="btn btn-primary" id="copy-subject">copy</button>
<div>
<h3>message
<button class="btn btn-primary" id="copy-message">copy</button>
</h3>
{% for p in lines %}
<p>{{ p }}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
{% if subject and lines %}
<script>
var copy_subject = document.getElementById("copy-subject");
var copy_message = document.getElementById("copy-message");
var subject = {{ subject | tojson }};
var message = {{ "\n\n".join(lines) | tojson }};
copy_subject.addEventListener("click", function(e) {
navigator.clipboard.writeText(subject);
});
copy_message.addEventListener("click", function(e) {
navigator.clipboard.writeText(message);
});
</script>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block title %}Flickr mail{% endblock %}
{% block content %}
<div class="container">
<h1>Flickr mail</h1>
<p>Wikipedia article: {{ name }}</p>
<p><a href="https://flickr.com/search/?view_all=1&safe_search=3&text={{ '"' + name + '"' | urlencode }}">Search flickr</a></p>
<form action="{{ url_for("show_message") }}">
<input type="hidden" name="wikipedia" value="{{ wikipedia_url }}"></input>
<div class="mb-3">
<label for="flickr" class="form-label">Flickr URL:</label>
<input type="text" class="form-control" id="flickr" name="flickr" required>
</div>
<input type="submit" value="Submit">
</form>
</div>
{% endblock %}

View file

@ -21,5 +21,3 @@ Thank you for your consideration, and if you have any questions or require furth
Warm regards, Warm regards,
Edward Edward
edward@4angle.com

View file

@ -1,23 +0,0 @@
{% extends "base.html" %}
{% block style %}
<link rel="stylesheet" href="{{url_for('static', filename='css/exception.css')}}" />
{% endblock %}
{% block content %}
<div class="p-2">
<h1>Software error: {{ exception_type }}</h1>
<div>
<pre>{{ exception }}</pre>
</div>
<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
{{ summary | safe }}
<p>Error in function "{{ last_frame.f_code.co_name }}": {{ last_frame_args | pprint }}</p>
<pre>{{ last_frame.f_locals | pprint }}</pre>
</div>
{% endblock %}

View file

@ -1,25 +1,32 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Xanadu{% endblock %}</title> <title>Flickr Mail</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
{% block style %}
{% endblock %}
</head> </head>
<body> <body>
{% block content %} <div class="container">
{% endblock %} <div class="row">
<h1>Enter URLs</h1>
<form action="{{ url_for("show_message") }}">
<div class="mb-3">
<label for="flickr" class="form-label">Flickr URL:</label>
<input type="text" class="form-control" id="flickr" name="flickr" required>
</div>
<div class="mb-3">
<label for="wikipedia" class="form-label">Wikipedia URL:</label>
<input type="text" class="form-control" id="wikipedia" name="wikipedia" required>
</div>
<input type="submit" value="Submit">
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
{% block scripts %}
{% endblock %}
</body> </body>
</html> </html>

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block title %}Flickr mail{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<h1>Enter URLs</h1>
<form action="{{ url_for("flickr_search") }}">
<div class="mb-3">
<label for="wikipedia" class="form-label">Wikipedia URL:</label>
<input type="text" class="form-control" id="wikipedia" name="wikipedia" required>
</div>
<input type="submit" value="Submit">
</form>
</div>
</div>
{% endblock %}