forked from varia/multifeeder
JSON responses, latest + today + past requests work, web interface is pink, header image added
This commit is contained in:
parent
1bc6fbbf10
commit
c59d206bed
121
feedtools.py
121
feedtools.py
@ -3,80 +3,99 @@ from simpledatabase import SimpleDatabase
|
|||||||
import json
|
import json
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
def load():
|
def update():
|
||||||
""" Load all feeds """
|
""" Update all feeds """
|
||||||
feeds = open('feeds.txt').readlines()
|
feeds = open('feeds.txt').readlines()
|
||||||
db = SimpleDatabase('feeds.json', 'feeds.log')
|
db = SimpleDatabase('feeds.json', 'feeds.log')
|
||||||
|
|
||||||
tmp = {}
|
tmp = {}
|
||||||
|
tmp['feeds'] = {}
|
||||||
|
tmp['all_posts_sorted'] = {}
|
||||||
|
|
||||||
for x, feed in enumerate(feeds):
|
for x, feed in enumerate(feeds):
|
||||||
parsed = feedparser.parse(feed)
|
parsed = feedparser.parse(feed)
|
||||||
x = str(x)
|
x = str(x)
|
||||||
tmp[x] = {}
|
|
||||||
tmp[x]['title'] = parsed.feed.title
|
print(parsed)
|
||||||
tmp[x]['link'] = parsed.feed.link
|
|
||||||
tmp[x]['description'] = parsed.feed.description
|
tmp['feeds'][x] = {}
|
||||||
tmp[x]['entries'] = parsed.entries
|
tmp['feeds'][x]['title'] = parsed.feed.title
|
||||||
|
tmp['feeds'][x]['link'] = parsed.feed.link
|
||||||
|
tmp['feeds'][x]['description'] = parsed.feed.description
|
||||||
|
|
||||||
|
for post in parsed.entries:
|
||||||
|
year = post['published_parsed'][0]
|
||||||
|
month = post['published_parsed'][1]
|
||||||
|
day = post['published_parsed'][2]
|
||||||
|
post_date = date(year, month, day)
|
||||||
|
|
||||||
|
if not str(post_date) in tmp['all_posts_sorted']:
|
||||||
|
tmp['all_posts_sorted'][str(post_date)] = []
|
||||||
|
|
||||||
|
post['feed_details'] = {}
|
||||||
|
post['feed_details']['title'] = parsed.feed.title
|
||||||
|
post['feed_details']['link'] = parsed.feed.link
|
||||||
|
post['feed_details']['description'] = parsed.feed.description
|
||||||
|
tmp['all_posts_sorted'][str(post_date)].append(post)
|
||||||
|
|
||||||
db.update(tmp)
|
db.update(tmp)
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
def load():
|
||||||
|
db = SimpleDatabase('feeds.json', 'feeds.log')
|
||||||
|
return db
|
||||||
|
|
||||||
def latest(num):
|
def latest(num):
|
||||||
""" Placeholder request """
|
""" Collect the <num> latest published posts """
|
||||||
request = [
|
db = load()
|
||||||
{
|
|
||||||
"feedtitle" : "Varia EN",
|
dates = [key for key in db['all_posts_sorted'].keys()]
|
||||||
"post": "hello world",
|
dates.sort(reverse=True)
|
||||||
"date" : "Monday 15th of February 2021",
|
request = []
|
||||||
"url" : "https://vvvvvvaria.org/en/rr-wireless-imagination-1.html"
|
|
||||||
}
|
for date in dates:
|
||||||
]
|
posts = db['all_posts_sorted'][date]
|
||||||
|
for post in posts:
|
||||||
|
if len(request) < int(num):
|
||||||
|
request.append(post)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def today():
|
def today():
|
||||||
""" Collect posts from today """
|
""" Collect posts from today """
|
||||||
db = load()
|
db = load()
|
||||||
|
today = date.today()
|
||||||
today = str(date.today()).split('-')
|
|
||||||
today_year = "{:02d}".format(int(today[0]))
|
|
||||||
today_month = "{:02d}".format(int(today[1]))
|
|
||||||
today_day = "{:02d}".format(int(today[2]))
|
|
||||||
print('TODAY =', today_year, today_month, today_day)
|
|
||||||
|
|
||||||
request = []
|
request = []
|
||||||
for x, feed in db.items():
|
|
||||||
for post in feed['entries']:
|
|
||||||
if post['published_parsed']:
|
|
||||||
year = "{:02d}".format(post['published_parsed'][0])
|
|
||||||
month = "{:02d}".format(post['published_parsed'][1])
|
|
||||||
day = "{:02d}".format(post['published_parsed'][2] + 1)
|
|
||||||
print('POST DATE =', year, month, day)
|
|
||||||
|
|
||||||
# Check if this post is published today
|
for date_str, posts in db['all_posts_sorted'].items():
|
||||||
if year == today_year:
|
year = int(date_str.split('-')[0])
|
||||||
if month == today_month:
|
month = int(date_str.split('-')[1])
|
||||||
if day == today_day:
|
day = int(date_str.split('-')[2])
|
||||||
request.append(post)
|
d = date(year, month, day)
|
||||||
|
|
||||||
|
# Check if any posts are published today
|
||||||
|
if d == today:
|
||||||
|
for post in posts:
|
||||||
|
request.append(post)
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def past(days):
|
def past(days):
|
||||||
""" Collect posts from this week """
|
""" Collect posts from this week """
|
||||||
db = load()
|
db = load()
|
||||||
|
|
||||||
point_in_the_past = date.today() - timedelta(int(days))
|
point_in_the_past = date.today() - timedelta(int(days))
|
||||||
print(f"{ days } days in the past =", point_in_the_past)
|
|
||||||
|
|
||||||
request = []
|
request = []
|
||||||
for x, feed in db.items():
|
|
||||||
for post in feed['entries']:
|
for date_str, posts in db['all_posts_sorted'].items():
|
||||||
if post['published_parsed']:
|
year = int(date_str.split('-')[0])
|
||||||
year = post['published_parsed'][0]
|
month = int(date_str.split('-')[1])
|
||||||
month = post['published_parsed'][1]
|
day = int(date_str.split('-')[2])
|
||||||
day = post['published_parsed'][2]
|
d = date(year, month, day)
|
||||||
|
|
||||||
post_date = date(year, month, day)
|
|
||||||
print("post date =",post_date)
|
|
||||||
|
|
||||||
if post_date > point_in_the_past:
|
|
||||||
request.append(post)
|
|
||||||
|
|
||||||
return request
|
if d > point_in_the_past:
|
||||||
|
for post in posts:
|
||||||
|
request.append(post)
|
||||||
|
|
||||||
|
return request
|
||||||
|
31
start.py
31
start.py
@ -1,5 +1,6 @@
|
|||||||
import flask
|
import flask
|
||||||
import feedtools
|
import feedtools
|
||||||
|
import json
|
||||||
|
|
||||||
APP = flask.Flask(__name__,
|
APP = flask.Flask(__name__,
|
||||||
static_url_path="",
|
static_url_path="",
|
||||||
@ -11,26 +12,42 @@ def index():
|
|||||||
db = feedtools.load()
|
db = feedtools.load()
|
||||||
template = flask.render_template(
|
template = flask.render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
feeds=db,
|
db=db,
|
||||||
)
|
)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
# @APP.route("/API/latest/<num>")
|
@APP.route("/API/latest/<num>")
|
||||||
# def latest(num):
|
def latest(num):
|
||||||
# request = feedtools.latest(num)
|
request = feedtools.latest(num)
|
||||||
# return request
|
response = APP.response_class(
|
||||||
|
response=json.dumps(request),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
@APP.route("/API/today/")
|
@APP.route("/API/today/")
|
||||||
def today():
|
def today():
|
||||||
request = feedtools.today()
|
request = feedtools.today()
|
||||||
return str(request)
|
response = APP.response_class(
|
||||||
|
response=json.dumps(request),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
@APP.route("/API/past/<days>")
|
@APP.route("/API/past/<days>")
|
||||||
def past(days):
|
def past(days):
|
||||||
request = feedtools.past(days)
|
request = feedtools.past(days)
|
||||||
return str(request)
|
response = APP.response_class(
|
||||||
|
response=json.dumps(request),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/json'
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
feedtools.update()
|
||||||
APP.debug = True
|
APP.debug = True
|
||||||
APP.run(port=5678)
|
APP.run(port=5678)
|
||||||
|
|
||||||
|
@ -1,14 +1,21 @@
|
|||||||
body{
|
body{
|
||||||
background-color: pink;
|
background-color: pink;
|
||||||
color: red;
|
/*color: red;*/
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
h1,
|
|
||||||
h2{
|
h1#title{
|
||||||
margin: 1em;
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
h1#title img{
|
||||||
|
width: 500px;
|
||||||
}
|
}
|
||||||
h2{
|
h2{
|
||||||
color: fuchsia;
|
margin: 0 0 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
table{
|
table{
|
||||||
@ -16,22 +23,31 @@ table{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
table tr{
|
||||||
|
border-bottom: 20px solid white;
|
||||||
|
}
|
||||||
table td{
|
table td{
|
||||||
padding: 1em 2em;
|
padding: 1em 2em;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
table td:first-of-type{
|
table td:first-of-type{
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
table tr{
|
|
||||||
border-bottom: 20px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
section#api{
|
section#api{
|
||||||
margin: 6em 0;
|
margin: 6em 0em;
|
||||||
color: fuchsia;
|
color: fuchsia;
|
||||||
}
|
}
|
||||||
section#api div.accesspoint{
|
section#api div.accesspoint{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 1em;
|
padding: 1em 2em;
|
||||||
border-bottom: 20px solid yellow;
|
border-bottom: 20px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:active,
|
||||||
|
a:visited,
|
||||||
|
a:hover{
|
||||||
|
text-decoration-line: underline;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
81
static/img/multifeeder.svg
Normal file
81
static/img/multifeeder.svg
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="200mm"
|
||||||
|
height="50mm"
|
||||||
|
viewBox="0 0 200 50"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||||
|
sodipodi:docname="multifeeder.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter863"
|
||||||
|
x="-6.5066022e-06"
|
||||||
|
width="1.000013"
|
||||||
|
y="-3.1750932e-05"
|
||||||
|
height="1.0000635">
|
||||||
|
<feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.00051484815"
|
||||||
|
id="feGaussianBlur865" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="362.93895"
|
||||||
|
inkscape:cy="176.11126"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
width="100mm"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:43.7437px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:4,4;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter863);stroke-linecap:round"
|
||||||
|
x="4.3744469"
|
||||||
|
y="43.628365"
|
||||||
|
id="text835"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan833"
|
||||||
|
x="4.3744469"
|
||||||
|
y="43.628365"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.7437px;font-family:'UKIJ Tiken';-inkscape-font-specification:'UKIJ Tiken';fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:4,4;stroke-dashoffset:0;stroke-opacity:1;stroke-linecap:round">multifeeder</tspan></text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
@ -6,33 +6,52 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/css/stylesheet.css">
|
<link rel="stylesheet" type="text/css" href="/css/stylesheet.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>multifeeder</h1>
|
<h1 id="title">
|
||||||
|
<img src="/img/multifeeder.svg">
|
||||||
|
</h1>
|
||||||
|
<div align="center">(<a href="https://git.vvvvvvaria.org/varia/multifeeder/src/branch/master/feeds.txt" target="_blank">Add a feed</a>)</div>
|
||||||
|
<div align="center">Currently feeding:</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for x, feed in feeds.items() %}
|
{% for x, feed in db['feeds'].items() %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ feed.title }}</td>
|
<td class="title">{{ feed.title }}</td>
|
||||||
<td>{{ feed.link }}</td>
|
<td class="link"><a href="{{ feed.link }}" target="_blank">{{ feed.link }}</a></td>
|
||||||
|
<td class="description">{{ feed.description }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
<section id="api">
|
<section id="api">
|
||||||
<h2>API</h2>
|
|
||||||
<!-- <div class="accesspoint">
|
|
||||||
/API/latest/[num]
|
|
||||||
<br><br>
|
|
||||||
(not there yet)
|
|
||||||
</div> -->
|
|
||||||
<div class="accesspoint">
|
<div class="accesspoint">
|
||||||
/API/today/
|
<h2>/API/latest/[num]</h2>
|
||||||
|
For example: <a href="/API/latest/5" target="_blank">https://multi.vvvvvvaria.org/API/latest/5</a>
|
||||||
|
<br>
|
||||||
|
(for the latest 5 posts in all feeds)
|
||||||
<br><br>
|
<br><br>
|
||||||
For example: <a href="/API/today/" target="_blank">localhost:5678/API/today/</a>
|
Format: JSON
|
||||||
</div>
|
</div>
|
||||||
<div class="accesspoint">
|
<div class="accesspoint">
|
||||||
/API/past/[days]
|
<h2>/API/today/</h2>
|
||||||
|
For example: <a href="/API/today/" target="_blank">https://multi.vvvvvvaria.org/API/today/</a>
|
||||||
|
<br>
|
||||||
|
(for the posts published today)
|
||||||
<br><br>
|
<br><br>
|
||||||
For example: <a href="/API/past/30" target="_blank">localhost:5678/API/past/30</a>
|
Format: JSON
|
||||||
|
</div>
|
||||||
|
<div class="accesspoint">
|
||||||
|
<h2>/API/past/[days]</h2>
|
||||||
|
For example: <a href="/API/past/30" target="_blank">https://multi.vvvvvvaria.org/API/past/30</a>
|
||||||
|
<br>
|
||||||
|
(for all the posts published in the last 30 days)
|
||||||
|
<br><br>
|
||||||
|
Format: JSON
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
Reference in New Issue
Block a user