manetta
4 years ago
10 changed files with 289 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
__pycache__* |
||||
|
.venv* |
||||
|
feeds.json |
@ -0,0 +1,9 @@ |
|||||
|
default: run |
||||
|
|
||||
|
setup: |
||||
|
@python3 -m venv .venv && \
|
||||
|
.venv/bin/pip install -r requirements.txt |
||||
|
|
||||
|
run: |
||||
|
@python3 start.py |
||||
|
|
@ -1,3 +1,19 @@ |
|||||
# Multifeeder |
# Multifeeder |
||||
|
|
||||
Multifeeding RSS streams into a point of access to publish from! (work-in-slow-progress) |
Multifeeding RSS streams into a point of access to publish from! (work-in-slow-progress) |
||||
|
|
||||
|
## Using the multifeeder |
||||
|
|
||||
|
To install: make a virtual environment + install the dependencies. |
||||
|
|
||||
|
`make setup` |
||||
|
|
||||
|
To use: activate the virtual environment ... |
||||
|
|
||||
|
`make activate` |
||||
|
|
||||
|
... and run the Flask application. |
||||
|
|
||||
|
`make` |
||||
|
|
||||
|
Open `localhost:5678` in a browser and there we go! |
||||
|
@ -0,0 +1,3 @@ |
|||||
|
https://vvvvvvaria.org/feeds/all-nl.rss.xml |
||||
|
https://vvvvvvaria.org/en/feeds/all-en.rss.xml |
||||
|
https://post.lurk.org/tags/varia.rss |
@ -0,0 +1,82 @@ |
|||||
|
import feedparser |
||||
|
from simpledatabase import SimpleDatabase |
||||
|
import json |
||||
|
from datetime import date, timedelta |
||||
|
|
||||
|
def load(): |
||||
|
""" Load all feeds """ |
||||
|
feeds = open('feeds.txt').readlines() |
||||
|
db = SimpleDatabase('feeds.json', 'feeds.log') |
||||
|
|
||||
|
tmp = {} |
||||
|
for x, feed in enumerate(feeds): |
||||
|
parsed = feedparser.parse(feed) |
||||
|
x = str(x) |
||||
|
tmp[x] = {} |
||||
|
tmp[x]['title'] = parsed.feed.title |
||||
|
tmp[x]['link'] = parsed.feed.link |
||||
|
tmp[x]['description'] = parsed.feed.description |
||||
|
tmp[x]['entries'] = parsed.entries |
||||
|
db.update(tmp) |
||||
|
return db |
||||
|
|
||||
|
def latest(num): |
||||
|
""" Placeholder request """ |
||||
|
request = [ |
||||
|
{ |
||||
|
"feedtitle" : "Varia EN", |
||||
|
"post": "hello world", |
||||
|
"date" : "Monday 15th of February 2021", |
||||
|
"url" : "https://vvvvvvaria.org/en/rr-wireless-imagination-1.html" |
||||
|
} |
||||
|
] |
||||
|
return request |
||||
|
|
||||
|
def today(): |
||||
|
""" Collect posts from today """ |
||||
|
db = load() |
||||
|
|
||||
|
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 = [] |
||||
|
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 |
||||
|
if year == today_year: |
||||
|
if month == today_month: |
||||
|
if day == today_day: |
||||
|
request.append(post) |
||||
|
return request |
||||
|
|
||||
|
def past(days): |
||||
|
""" Collect posts from this week """ |
||||
|
db = load() |
||||
|
|
||||
|
point_in_the_past = date.today() - timedelta(int(days)) |
||||
|
print(f"{ days } days in the past =", point_in_the_past) |
||||
|
|
||||
|
request = [] |
||||
|
for x, feed in db.items(): |
||||
|
for post in feed['entries']: |
||||
|
if post['published_parsed']: |
||||
|
year = post['published_parsed'][0] |
||||
|
month = post['published_parsed'][1] |
||||
|
day = post['published_parsed'][2] |
||||
|
|
||||
|
post_date = date(year, month, day) |
||||
|
print("post date =",post_date) |
||||
|
|
||||
|
if post_date > point_in_the_past: |
||||
|
request.append(post) |
||||
|
|
||||
|
return request |
@ -0,0 +1,3 @@ |
|||||
|
flask |
||||
|
feedparser |
||||
|
pathlib |
@ -0,0 +1,61 @@ |
|||||
|
from os import environ, mkdir |
||||
|
from os.path import exists |
||||
|
from pathlib import Path |
||||
|
from logging import DEBUG, INFO, basicConfig, getLogger |
||||
|
from json import dumps, loads |
||||
|
|
||||
|
class SimpleDatabase(dict): |
||||
|
"""A simple database. |
||||
|
It is a dictionary which saves to disk on all writes. It is optimised for |
||||
|
ease of hacking and accessibility and not for performance or efficiency. |
||||
|
|
||||
|
Written by decentral1se, as part of: |
||||
|
https://git.vvvvvvaria.org/decentral1se/xbotlib/src/branch/main/xbotlib.py |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, filename, log, *args, **kwargs): |
||||
|
"""Initialise the object.""" |
||||
|
self.filename = Path(filename).absolute() |
||||
|
self.log = getLogger(__name__) |
||||
|
|
||||
|
self._loads() |
||||
|
self.update(*args, **kwargs) |
||||
|
|
||||
|
def _loads(self): |
||||
|
"""Load the database.""" |
||||
|
if not exists(self.filename): |
||||
|
return |
||||
|
|
||||
|
try: |
||||
|
with open(self.filename, "r") as handle: |
||||
|
self.update(loads(handle.read())) |
||||
|
except Exception as exception: |
||||
|
message = f"Loading file storage failed: {exception}" |
||||
|
self.log.error(message, exc_info=exception) |
||||
|
exit(1) |
||||
|
|
||||
|
def _dumps(self): |
||||
|
"""Save the databse to disk.""" |
||||
|
try: |
||||
|
with open(self.filename, "w") as handle: |
||||
|
handle.write(dumps(self, indent=4, sort_keys=True)) |
||||
|
except Exception as exception: |
||||
|
message = f"Saving file storage failed: {exception}" |
||||
|
self.log.error(message, exc_info=exception) |
||||
|
exit(1) |
||||
|
|
||||
|
def __setitem__(self, key, val): |
||||
|
"""Write data to the database.""" |
||||
|
super().__setitem__(key, val) |
||||
|
self._dumps() |
||||
|
|
||||
|
def __delitem__(self, key): |
||||
|
"""Remove data from the database.""" |
||||
|
super().__delitem__(key) |
||||
|
self._dumps() |
||||
|
|
||||
|
def update(self, *args, **kwargs): |
||||
|
"""Update the database.""" |
||||
|
for k, v in dict(*args, **kwargs).items(): |
||||
|
self[k] = v |
||||
|
self._dumps() |
@ -0,0 +1,36 @@ |
|||||
|
import flask |
||||
|
import feedtools |
||||
|
|
||||
|
APP = flask.Flask(__name__, |
||||
|
static_url_path="", |
||||
|
static_folder="static", |
||||
|
template_folder="templates") |
||||
|
|
||||
|
@APP.route("/") |
||||
|
def index(): |
||||
|
db = feedtools.load() |
||||
|
template = flask.render_template( |
||||
|
"index.html", |
||||
|
feeds=db, |
||||
|
) |
||||
|
return template |
||||
|
|
||||
|
# @APP.route("/API/latest/<num>") |
||||
|
# def latest(num): |
||||
|
# request = feedtools.latest(num) |
||||
|
# return request |
||||
|
|
||||
|
@APP.route("/API/today/") |
||||
|
def today(): |
||||
|
request = feedtools.today() |
||||
|
return str(request) |
||||
|
|
||||
|
@APP.route("/API/past/<days>") |
||||
|
def past(days): |
||||
|
request = feedtools.past(days) |
||||
|
return str(request) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
APP.debug = True |
||||
|
APP.run(port=5678) |
||||
|
|
@ -0,0 +1,37 @@ |
|||||
|
body{ |
||||
|
background-color: pink; |
||||
|
color: red; |
||||
|
margin: 1em; |
||||
|
} |
||||
|
h1, |
||||
|
h2{ |
||||
|
margin: 1em; |
||||
|
} |
||||
|
h2{ |
||||
|
color: fuchsia; |
||||
|
} |
||||
|
|
||||
|
table{ |
||||
|
table-layout: fixed; |
||||
|
width: 100%; |
||||
|
border-collapse: collapse; |
||||
|
} |
||||
|
table td{ |
||||
|
padding: 1em 2em; |
||||
|
} |
||||
|
table td:first-of-type{ |
||||
|
width: 100px; |
||||
|
} |
||||
|
table tr{ |
||||
|
border-bottom: 20px solid white; |
||||
|
} |
||||
|
|
||||
|
section#api{ |
||||
|
margin: 6em 0; |
||||
|
color: fuchsia; |
||||
|
} |
||||
|
section#api div.accesspoint{ |
||||
|
margin: 0; |
||||
|
padding: 1em; |
||||
|
border-bottom: 20px solid yellow; |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<title>multifeeder</title> |
||||
|
<link rel="stylesheet" type="text/css" href="/css/stylesheet.css"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>multifeeder</h1> |
||||
|
<table> |
||||
|
<tbody> |
||||
|
{% for x, feed in feeds.items() %} |
||||
|
<tr> |
||||
|
<td>{{ feed.title }}</td> |
||||
|
<td>{{ feed.link }}</td> |
||||
|
</tr> |
||||
|
{% endfor %} |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<section id="api"> |
||||
|
<h2>API</h2> |
||||
|
<!-- <div class="accesspoint"> |
||||
|
/API/latest/[num] |
||||
|
<br><br> |
||||
|
(not there yet) |
||||
|
</div> --> |
||||
|
<div class="accesspoint"> |
||||
|
/API/today/ |
||||
|
<br><br> |
||||
|
For example: <a href="/API/today/" target="_blank">localhost:5678/API/today/</a> |
||||
|
</div> |
||||
|
<div class="accesspoint"> |
||||
|
/API/past/[days] |
||||
|
<br><br> |
||||
|
For example: <a href="/API/past/30" target="_blank">localhost:5678/API/past/30</a> |
||||
|
</div> |
||||
|
</section> |
||||
|
</body> |
||||
|
</html> |
Loading…
Reference in new issue