RSS feed implementation

This commit is contained in:
= 2023-08-24 13:46:56 +02:00
parent c33bf8596f
commit 1431c3b0ba
6 changed files with 95 additions and 91 deletions

View file

@ -174,13 +174,26 @@ def appsobservatory_history():
data = json.loads(open("./app/scripts/appListsHistory/count_history.json").read()) data = json.loads(open("./app/scripts/appListsHistory/count_history.json").read())
return render_template('applist_history.html', data=data) return render_template('applist_history.html', data=data)
@main.route('/appsobservatory/news') @main.route('/appsobservatory/news')
def appsobservatory_news(): def appsobservatory_news():
news_per_date = json.loads(open("./app/scripts/appListsHistory/news.json").read()) news_per_date = json.loads(open("./app/scripts/appListsHistory/news.json").read())
return render_template('applist_news.html', news_per_date=list(reversed(list(news_per_date.items())))) return render_template('applist_news.html', news_per_date=list(reversed(list(news_per_date.items()))))
@main.route('/appsobservatory/news/rss')
def appsobservatory_news_rss():
rss_news_per_date = json.loads(open("./app/scripts/appListsHistory/news_rss.json").read())
rss_xml = render_template('applist_news_rss.xml', rss_news_per_date=list(reversed(list(rss_news_per_date.items()))))
response = make_response(rss_xml)
response.headers['Content-Type'] = 'application/rss+xml'
response.headers['Content-Disposition'] = "inline; filename=YNH_catalog_news.xml"
return response
@main.app_template_filter()
def format_datetime(value, format="%d %b %Y %I:%M %p"):
if value is None:
return ""
return datetime.strptime(value, "%b %d %Y").strftime(format)
@main.route('/appsobservatory/unlisted') @main.route('/appsobservatory/unlisted')
def appsobservatory_unlisted(): def appsobservatory_unlisted():

View file

@ -1,77 +0,0 @@
{% if data.new %}
<h2>New apps</h2>
<ul>
{% for app in data.new %}
<li><a href="{{ app.url }}">{{ app.name.title() }}</a>
({{ app.state }}{% if app.level %}, level {{ app.level }}{% endif %}) </li>
{% endfor %}
</ul>
{% endif %}
{% if data.improvements %}
<h2>Improvements</h2>
<ul>
{% for app, changes in data.improvements %}
<li><a href="{{ app.url }}">{{ app.name.title() }}</a>:
{% for change in changes %}
{% if change == "updated" %}
Updated{{ "," if not loop.last }}
{% elif change[0] == "state" %}
State {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% elif change[0] == "level" %}
Level {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% endif %}
{% endfor %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if data.updates %}
<h2>Updates</h2>
<ul>
{% for app, changes in data.updates %}
<li><a href="{{ app.url }}">{{ app.name.title() }}</a>:
{% for change in changes %}
{% if change == "updated" %}
Updated{{ "," if not loop.last }}
{% elif change[0] == "state" %}
State {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% elif change[0] == "level" %}
Level {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% endif %}
{% endfor %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if data.regressions %}
<h2>Regressions</h2>
<ul>
{% for app, changes in data.regressions %}
<li><a href="{{ app.url }}">{{ app.name.title() }}</a>:
{% for change in changes %}
{% if change == "updated" %}
Updated{{ "," if not loop.last }}
{% elif change[0] == "state" %}
State {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% elif change[0] == "level" %}
Level {{ change[1] }} -&gt; {{ change[2] }}{{ "," if not loop.last }}
{% endif %}
{% endfor %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if data.removed %}
<h2>Apps removed</h2>
<ul>
{% for app in data.removed %}
<li><a href="{{ app.url }}">{{ app.name.title() }}</a>
({{ app.state }}{% if app.level %}, level {{ app.level }}{% endif %})
</li>
{% endfor %}
</ul>
{% endif %}

View file

@ -1,8 +1,10 @@
import toml import toml
import json import json
import markdown
import os import os
import sys import sys
import inspect import inspect
import requests
from datetime import datetime from datetime import datetime
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
@ -148,7 +150,6 @@ def make_news():
else: else:
return int(lev) return int(lev)
for d in time_points_until_today: for d in time_points_until_today:
d_label = d.strftime("%b %d %Y") d_label = d.strftime("%b %d %Y")
@ -167,19 +168,45 @@ def make_news():
news = news_per_date[d_label] news = news_per_date[d_label]
for app in set(apps_previous_good & apps_current_broken): for app in set(apps_previous_good & apps_current_broken):
news["broke"].append((app, j[app]["url"])) news["broke"].append([app, j[app]["url"], j[app].get("branch", "master"), j[app].get("category", ""), j[app].get("subtags", "")])
for app in set(apps_previous_broken & apps_current_good): for app in set(apps_previous_broken & apps_current_good):
news["repaired"].append((app, j[app]["url"])) news["repaired"].append([app, j[app]["url"], j[app].get("branch", "master"), j[app].get("category", ""), j[app].get("subtags", "")])
for app in set(apps_current - apps_previous): for app in set(apps_current - apps_previous):
news["added"].append((app, j[app]["url"])) news["added"].append([app, j[app]["url"], j[app].get("branch", "master"), j[app].get("category", ""), j[app].get("subtags", "")])
for app in set(apps_previous - apps_current): for app in set(apps_previous - apps_current):
news["removed"].append((app, previous_j[app]["url"])) news["removed"].append([app, previous_j[app]["url"], previous_j[app].get("branch", "master"), previous_j[app].get("category", ""), previous_j[app].get("subtags", "")])
previous_j = j previous_j = j
json.dump(news_per_date, open('news.json', 'w')) json.dump(news_per_date, open('news.json', 'w'))
# Generate json for RSS feed
rss_number_of_times_points_to_include = 2
rss_d_label = list(news_per_date)[-rss_number_of_times_points_to_include:]
rss_feed = {k:news_per_date.get(k) for k in rss_d_label}
for d, status_list in rss_feed.items():
print("Pushing all entries from " + d + " to RSS feed...")
for status, apps_list in status_list.items():
for app in apps_list:
# Memo: at this point, 'app' list's indexes correspond to [0] - app name, [1] - app url, [2] - app branch name, [3] - app category, [4] - app subtags
# Merge list of category and subtags into a unique string and store it in [3]
if app[3] and app[4]:
app[3] = app[3] + ", " + ", ".join(app[4])
# Retrieve app README and store it in [4]
print("Retrieve Github README for app " + app[0])
readme_url = "https://raw.githubusercontent.com/YunoHost-Apps/" + app[0] + "_ynh/" + app[2] + "/README.md"
r = requests.get(readme_url)
readme_md = r.text
readme_md = readme_md.replace("./doc/screenshots/", "https://raw.githubusercontent.com/YunoHost-Apps/" + app[0] + "_ynh/" + app[2] + "/doc/screenshots/")
readme_html = markdown.markdown(readme_md)
app[4] = readme_html
json.dump(rss_feed, open('news_rss.json', 'w'))
def update_catalog_stats(app, history): def update_catalog_stats(app, history):
print(app) print(app)

View file

@ -66,3 +66,6 @@ td.ci-app-test-result > div.danger { background-color: rgb(225,80,62); }
text-overflow: ellipsis; text-overflow: ellipsis;
max-width:400px; max-width:400px;
} }
.btn-rss { background-color: #ee802f; border-color: #ee802f; }
.btn-rss:hover, .btn-rss:not(:disabled):not(.disabled):active { background-color: #ca630c; border-color: #c15b00; }

View file

@ -2,6 +2,12 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-10 mx-3">
<link rel="alternate" type="application/rss+xml" title="YNH Catalog News" href="{{ url_for('main.appsobservatory_news_rss')}}" />
<a href="{{ url_for('main.appsobservatory_news_rss')}}" class = "btn btn-sm mb-3 py-2 px-3 btn-warning btn-rss text-uppercase font-weight-bold"><i class="oi oi-rss-alt pr-1" aria-hidden="true"></i>Suscribe via RSS</a>
</div>
<div class="col-10 offset-1"> <div class="col-10 offset-1">
{% for date, news in news_per_date %} {% for date, news in news_per_date %}
@ -9,28 +15,29 @@
<h2>{{ date }}</h2> <h2>{{ date }}</h2>
<ul> <ul>
{% for app, url in news["added"] %} {% for app in news["added"] %}
{# MEMO: app list's indexes correspond to [0] - app name, [1] - app url, [2] - app Github branch name, [3] - app description (keywords), [4] - app readme in HTML #}
<li> <li>
<span class="badge bg-primary" style="color: white;"><i class="oi oi-plus pr-1" aria-hidden="true"></i>Added</span> <span class="badge bg-primary" style="color: white;"><i class="oi oi-plus pr-1" aria-hidden="true"></i>Added</span>
<a href="{{url}}">{{app}}</a> <a href="{{app[1]}}">{{app[0]}}</a>
</li> </li>
{% endfor %} {% endfor %}
{% for app, url in news["repaired"] %} {% for app in news["repaired"] %}
<li> <li>
<span class="badge bg-success" style="color: white;"><i class="oi oi-circle-check pr-1" aria-hidden="true"></i>Repaired</span> <span class="badge bg-success" style="color: white;"><i class="oi oi-circle-check pr-1" aria-hidden="true"></i>Repaired</span>
<a href="{{url}}">{{app}}</a> <a href="{{app[1]}}">{{app[0]}}</a>
</li> </li>
{% endfor %} {% endfor %}
{% for app, url in news["broke"] %} {% for app in news["broke"] %}
<li> <li>
<span class="badge bg-warning" style="color: white;"><i class="oi oi-warning pr-1" aria-hidden="true"></i>Broke</span> <span class="badge bg-warning" style="color: white;"><i class="oi oi-warning pr-1" aria-hidden="true"></i>Broke</span>
<a href="{{url}}">{{app}}</a> <a href="{{app[1]}}">{{app[0]}}</a>
</li> </li>
{% endfor %} {% endfor %}
{% for app, url in news["removed"] %} {% for app in news["removed"] %}
<li> <li>
<span class="badge bg-danger" style="color: white;"><i class="oi oi-circle-x pr-1" aria-hidden="true"></i>Removed</span> <span class="badge bg-danger" style="color: white;"><i class="oi oi-circle-x pr-1" aria-hidden="true"></i>Removed</span>
<a href="{{url}}">{{app}}</a> <a href="{{app[1]}}">{{app[0]}}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
<channel>
<title>YNH Catalog News</title>
<link>{{ url_for('main.appsobservatory_news')}}</link>
<atom:link href="{{ url_for('main.appsobservatory_news_rss')}}" rel="self" type="application/rss+xml" />
<description>YunoHost apps catalog's news</description>
{%- for date, status_list in rss_news_per_date %}
{%- for status in ("added", "repaired", "broke", "removed") %}
{%- for app in status_list[status] %}
{#- MEMO: app list's indexes correspond to [0] - app name, [1] - app url, [2] - app Github branch name, [3] - app description (keywords), [4] - app readme in HTML #}
<item>
<title>[{{ status|upper }}] {{ app[0]|capitalize }}</title>
<link>{{ app[1] }}</link>
<guid>{{ app[1] }}#{{ date|format_datetime("%Y%m%d") }}</guid>
<pubDate>{{ date|format_datetime("%a, %d %b %Y %H:%M:%S +0000") }}</pubDate>
<description>{{ app[3]|capitalize }}</description>
<content:encoded><![CDATA[{{ app[4] }}]]></content:encoded>
</item>
{% endfor -%}
{% endfor -%}
{% endfor %}
</channel>
</rss>