This commit is contained in:
gauthiier 2019-12-08 21:42:16 +01:00
parent 893515735a
commit 46a0cc8c0b
16 changed files with 1539 additions and 0 deletions

51
selection/construct.py Normal file
View File

@ -0,0 +1,51 @@
import os, json, glob, logging
ARCH = "archives/"
EXP = "selection/"
sel = os.path.join(EXP, "tm-selection.js")
def find(li, url):
d = os.path.join(ARCH, li)
if not os.path.isdir(d):
logging.warning("Invalid archive path: " + d)
return None
dir_files = [f for f in glob.glob(os.path.join(d, "*.json"))]
for f in dir_files:
with open(f) as fp:
dj = json.load(fp)
for t in dj['threads']:
if t['url'] == url:
return t
return None
def construct():
dump = {}
with open(sel) as f:
d = json.load(f)
for k, v in d.items():
dump[k] = []
for i in v:
m = find(i['list'], i['url'])
if m is not None:
m['list'] = i['list']
dump[k].append(m)
fout = os.path.join(EXP, "tm-selection-dump.js")
with open(fout, 'w+', encoding='utf-8') as f:
json.dump(dump, f, ensure_ascii=False, indent=4)
if __name__ == "__main__":
construct()

File diff suppressed because one or more lines are too long

15
selection/tm-selection.js Normal file
View File

@ -0,0 +1,15 @@
{
"net.art": [
{
"list": "crumb",
"url": "https://www.jiscmail.ac.uk/cgi-bin/webadmin?A2=ind1406&L=new-media-curating&F=&S=&P=15160"
}
],
"end2end": [],
"new media art": [
{
"list": "nettime-l",
"url": "https://nettime.org/Lists-Archives/nettime-l-0905/msg00038.html"
}
]
}

3
www-serve Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
source activate listserv
gunicorn -w 1 --bind 0.0.0.0:6767 www-serve:app

4
www-serve.py Normal file
View File

@ -0,0 +1,4 @@
from www import app
if __name__ == "__main__":
app.run(debug=True, use_reloader=False)

5
www/__init__.py Normal file
View File

@ -0,0 +1,5 @@
from flask import Flask
app = Flask(__name__)
from www import routes

57
www/archive.py Normal file
View File

@ -0,0 +1,57 @@
import os, glob, json
ARCH = "archives/"
EXP = "selection/"
sel = os.path.join(EXP, "tm-selection.js")
with open(sel) as f:
d = json.load(f)
def lists():
return os.listdir(ARCH)
def tags():
global d
return list(d.keys())
def commit(li, url, tag):
global sel
if tag not in list(d.keys()):
print("new tag: " + tag)
d[tag] = []
for i in d[tag]:
if i['url'] == url:
return False
d[tag].append({'list': li, 'url': url})
with open(sel, 'w', encoding='utf-8') as f:
json.dump(d, f, ensure_ascii=False, indent=4)
return True
def report():
re = "Report: \n"
for k, v in d.items():
lre = {}
for i in v:
if i['list'] not in lre:
lre[i['list']] = 0
lre[i['list']] += 1
re += "<" + k + ">: " + str(len(v)) + " ("
for kk, vv in lre.items():
re += kk + ": " + str(vv) + " / "
re += ")\n"
return re

37
www/routes.py Normal file
View File

@ -0,0 +1,37 @@
from flask import render_template, request, jsonify, send_from_directory
from www import app
import logging
from www import archive
@app.route('/')
def index():
l = archive.lists()
return render_template("index.html", lists=l, tags=archive.tags())
@app.route('/tags')
def tags():
return jsonify(result=archive.tags())
@app.route('/report')
def report():
return jsonify(report=archive.report())
# @app.route('/favicon.ico')
# def favicon():
# return send_from_directory(os.path.join(app.root_path, 'static'),
# 'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/collect')
def collect():
url = request.args.get('url')
tag = request.args.get('tags')
li = request.args.get('list')
msg = "Added " + url + " to " + tag
if not archive.commit(li, url, tag):
msg = url + " already exists..."
return jsonify({ "msg": msg, "report": archive.report()})

30
www/static/collect.js Normal file
View File

@ -0,0 +1,30 @@
// https://goodies.pixabay.com/jquery/tag-editor/demo.html
$.get("/tags", function(data, status){
$('#tags').tagEditor({
autocomplete: {
delay: 0, // show suggestions immediately
position: { collision: 'flip' }, // automatic menu position up/down
source: data.result
},
maxLength: 25,
placeholder: 'Enter tags ...'
});
});
$.get("/report", function(data, status){
$('#report').text(data.report);
});
$('#collect').submit(function(e) {
e.preventDefault();
args = $(this).serialize();
$.get('/collect?'+ args, function(data) {
console.log(data);
$('#serv').append(data.msg + "<br>");
$('#report').text(data.report);
});
});

4
www/static/jquery-1.11.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

12
www/static/jquery-ui-1.10.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
www/static/jquery.caret.min.js vendored Executable file
View File

@ -0,0 +1,2 @@
// http://code.accursoft.com/caret - 1.3.3
!function(e){e.fn.caret=function(e){var t=this[0],n="true"===t.contentEditable;if(0==arguments.length){if(window.getSelection){if(n){t.focus();var o=window.getSelection().getRangeAt(0),r=o.cloneRange();return r.selectNodeContents(t),r.setEnd(o.endContainer,o.endOffset),r.toString().length}return t.selectionStart}if(document.selection){if(t.focus(),n){var o=document.selection.createRange(),r=document.body.createTextRange();return r.moveToElementText(t),r.setEndPoint("EndToEnd",o),r.text.length}var e=0,c=t.createTextRange(),r=document.selection.createRange().duplicate(),a=r.getBookmark();for(c.moveToBookmark(a);0!==c.moveStart("character",-1);)e++;return e}return t.selectionStart?t.selectionStart:0}if(-1==e&&(e=this[n?"text":"val"]().length),window.getSelection)n?(t.focus(),window.getSelection().collapse(t.firstChild,e)):t.setSelectionRange(e,e);else if(document.body.createTextRange)if(n){var c=document.body.createTextRange();c.moveToElementText(t),c.moveStart("character",e),c.collapse(!0),c.select()}else{var c=t.createTextRange();c.move("character",e),c.select()}return n||t.focus(),e}}(jQuery);

View File

@ -0,0 +1,45 @@
/* surrounding tag container */
.tag-editor {
list-style-type: none; padding: 0 5px 0 0; margin: 0; overflow: hidden; border: 1px solid #eee; cursor: text;
font: normal 14px sans-serif; color: #555; background: #fff; line-height: 20px;
}
/* core styles usually need no change */
.tag-editor li { display: block; float: left; overflow: hidden; margin: 3px 0; }
.tag-editor div { float: left; padding: 0 4px; }
.tag-editor .placeholder { padding: 0 8px; color: #bbb; }
.tag-editor .tag-editor-spacer { padding: 0; width: 8px; overflow: hidden; color: transparent; background: none; }
.tag-editor input {
vertical-align: inherit; border: 0; outline: none; padding: 0; margin: 0; cursor: text;
font-family: inherit; font-weight: inherit; font-size: inherit; font-style: inherit;
box-shadow: none; background: none; color: #444;
}
/* hide original input field or textarea visually to allow tab navigation */
.tag-editor-hidden-src { position: absolute !important; left: -99999px; }
/* hide IE10 "clear field" X */
.tag-editor ::-ms-clear { display: none; }
/* tag style */
.tag-editor .tag-editor-tag {
padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap;
overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px;
}
/* delete icon */
.tag-editor .tag-editor-delete { background: #e0eaf1; cursor: pointer; border-radius: 0 2px 2px 0; padding-left: 3px; padding-right: 4px; }
.tag-editor .tag-editor-delete i { line-height: 18px; display: inline-block; }
.tag-editor .tag-editor-delete i:before { font-size: 16px; color: #8ba7ba; content: "×"; font-style: normal; }
.tag-editor .tag-editor-delete:hover i:before { color: #d65454; }
.tag-editor .tag-editor-tag.active+.tag-editor-delete, .tag-editor .tag-editor-tag.active+.tag-editor-delete i { visibility: hidden; cursor: text; }
.tag-editor .tag-editor-tag.active { background: none !important; }
/* jQuery UI autocomplete - code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css */
.ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; font-size: 14px; }
.ui-front { z-index: 9999; }
.ui-menu { list-style: none; padding: 1px; margin: 0; display: block; outline: none; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.4; min-height: 0; /* support: IE7 */ }
.ui-widget-content { border: 1px solid #bbb; background: #fff; color: #555; }
.ui-widget-content a { color: #46799b; }
.ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { background: #e0eaf1; }
.ui-helper-hidden-accessible { display: none; }

3
www/static/jquery.tag-editor.min.js vendored Executable file

File diff suppressed because one or more lines are too long

16
www/static/lestyle.css Normal file
View File

@ -0,0 +1,16 @@
#collect {
width: 30em;
display: block;
border: 1px black;
}
input[type="text"] {
width: 100%;
margin-top: 1em;
margin-bottom: 1em;
/*font-size: 1em;*/
}
button {
margin-top: 2em;
}

54
www/templates/index.html Normal file
View File

@ -0,0 +1,54 @@
<html>
<head>
<meta charset="utf-8">
<title>URLCOLLECT</title>
<link rel="stylesheet" href="{{ url_for('static',filename='jquery.tag-editor.css') }}">
<link rel="stylesheet" href="{{ url_for('static',filename='lestyle.css') }}">
</head>
<body>
<form action="/collect" method="get" id="collect">
<h2>*************************************GET::</h2>
<label for="url">url</label>
<input type="text" name="url" id="url">
<br><br>
{% for l in lists %}
{{l}} <input type="radio" name="list" value="{{l}}">
{% endfor %}
<br><br>
<label for="tags">tags</label>
<input type="text" name="tags" id="tags">
<br><br>
<label for="tags">existing tags: </label>
<pre id="list_tags">
{% for t in tags %}
{{t}}
{% endfor %}
</pre>
<br><br>
<button type="submit" id="submit">collect!</button>
<br><br>
---
<br>
<pre id="report"></pre>
---
<br>
<pre id="serv"></pre>
</form>
<div id="collected"></div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="{{ url_for('static',filename='jquery.caret.min.js') }}"></script>
<script src="{{ url_for('static',filename='jquery.tag-editor.min.js') }}"></script>
<script src="{{ url_for('static',filename='collect.js') }}"></script>
</body>
</html>