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
__pycache__

108
main.py
View file

@ -1,78 +1,70 @@
#!/usr/bin/python3
"""Find photos on flickr for Wikipedia articles 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 = False
app.debug = True
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."""
wikipedia_url = flask.request.args.get("wikipedia")
if not wikipedia_url:
return flask.render_template("combined.html")
return flask.render_template("wikipedia_url.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)
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
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/")]
@ -101,29 +93,9 @@ def start() -> str:
lines = msg.split("\n\n")
return flask.render_template(
"combined.html",
name=name,
wikipedia_url=wikipedia_url,
flickr_url=flickr_url,
subject=subject,
lines=lines,
nsid=nsid,
"show_message.html", 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__":
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,
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>
<html lang="en">
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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">
{% block style %}
{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
<div class="container">
<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>
{% block scripts %}
{% endblock %}
</body>
</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 %}