Première version de la gestion des articles

This commit is contained in:
Guillaume Ayoub 2018-04-30 01:36:46 +02:00
parent ffe385c590
commit 78fb6875e6
9 changed files with 110 additions and 63 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
__pycache__
afpy.egg-info
static/css/*.map
posts
# PyCharm project files
.idea

63
afpy.py
View File

@ -2,6 +2,8 @@ import datetime
import email
import locale
import time
from pathlib import Path
from xml.etree import ElementTree
import docutils.core
import docutils.writers.html5_polyglot
@ -16,11 +18,6 @@ cache = Cache(config={'CACHE_TYPE': 'simple', 'CACHE_DEFAULT_TIMEOUT': 600})
app = Flask(__name__)
cache.init_app(app)
FEEDS = {
'emplois': 'https://plone.afpy.org/rss-jobs/RSS',
'planet': 'https://plone.afpy.org/planet/rss.xml',
}
PLANET = {
'Emplois AFPy': 'https://plone.afpy.org/rss-jobs/RSS',
'Nouvelles AFPy': 'https://plone.afpy.org/rss-actualites/RSS',
@ -42,6 +39,17 @@ MEETUPS = {
'montpellier': 'https://www.meetup.com/fr-FR/Meetup-Python-Montpellier/',
}
POSTS = {
'actualites': 'Actualités',
'emplois': 'Emplois',
}
root = Path(__file__).parent / 'posts'
for category in POSTS:
for status in ('waiting', 'published'):
(root / category / status).mkdir(parents=True, exist_ok=True)
@app.errorhandler(404)
def page_not_found(e):
@ -51,12 +59,16 @@ def page_not_found(e):
@app.route('/')
@app.route('/<name>')
def pages(name='index'):
entries = ()
posts = {}
if name == 'index':
entries = feedparser.parse(FEEDS['planet']).entries
path = root / 'actualites' / 'published'
timestamps = sorted(path.iterdir(), reverse=True)[:4]
for timestamp in timestamps:
tree = ElementTree.parse(timestamp / 'post.xml')
posts[timestamp] = {item.tag: item.text for item in tree.iter()}
try:
return render_template(
f'{name}.html', body_id=name, meetups=MEETUPS, entries=entries)
f'{name}.html', body_id=name, meetups=MEETUPS, posts=posts)
except TemplateNotFound:
abort(404)
@ -75,15 +87,31 @@ def rest(name):
'rst.html', body_id=name, html=parts['body'], title=parts['title'])
@app.route('/feed/<name>')
def feed(name):
try:
feed = feedparser.parse(FEEDS[name])
except KeyError:
@app.route('/posts/<name>')
def posts(name):
if name not in POSTS:
abort(404)
path = root / name / 'published'
timestamps = sorted(path.iterdir(), reverse=True)[:12]
posts = {}
for timestamp in timestamps:
tree = ElementTree.parse(timestamp / 'post.xml')
posts[timestamp] = {item.tag: item.text for item in tree.iter()}
return render_template(
'feed.html', body_id=name, entries=feed.entries,
title=feed.feed.get('title'))
'posts.html', body_id=name, posts=posts, title=POSTS[name], name=name)
@app.route('/posts/<name>/<timestamp>')
def post(name, timestamp):
if name not in POSTS:
abort(404)
try:
tree = ElementTree.parse(
root / name / 'published' / timestamp / 'post.xml')
except Exception:
abort(404)
post = {item.tag: item.text for item in tree.iter()}
return render_template('post.html', body_id='post', post=post)
@app.route('/planet/')
@ -104,6 +132,11 @@ def jobs():
return redirect('https://plone.afpy.org/rss-jobs/RSS', code=307)
@app.template_filter('parse_rfc822_datetime')
def parse_rfc822_datetime(rfc822_datetime):
return email.utils.parsedate_tz(rfc822_datetime)
@app.template_filter('datetime')
def format_datetime(time_struct, format_):
return datetime.datetime(*time_struct[:6]).strftime(format_)

View File

@ -1,5 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Hind:300,600,700')
$bkg: #25252D
$bkg: #25252d
$header: #1d1e23
$action: #2e5cfd
$action-secondary: #ffcd05
@ -148,7 +148,7 @@ dd
time
display: block
#planet main, #emplois main, #index-news
#actualites main, #emplois main, #index-news
box-sizing: border-box
display: flex
flex-wrap: wrap
@ -186,6 +186,6 @@ time
font-weight: 600
@media screen and (max-width: 640px)
#planet main, #emplois main, #index-news
#actualites main, #emplois main, #index-news
article
flex: 1 100%

View File

@ -36,7 +36,7 @@ iframe {
width: 100%; }
body {
background: #25252D;
background: #25252d;
color: #eaeaea;
display: flex;
flex-direction: column;
@ -130,39 +130,39 @@ dd {
time {
display: block; }
#planet main, #emplois main, #index-news {
#actualites main, #emplois main, #index-news {
box-sizing: border-box;
display: flex;
flex-wrap: wrap; }
#planet main article, #emplois main article, #index-news article {
#actualites main article, #emplois main article, #index-news article {
background: #31313b;
border: 1px solid #25252D;
border: 1px solid #25252d;
box-sizing: border-box;
flex: 1 50%;
padding: 2em; }
#planet main article a, #emplois main article a, #index-news article a {
#actualites main article a, #emplois main article a, #index-news article a {
color: #ffcd05;
font-size: .8em;
font-weight: 700;
text-decoration: none;
text-transform: uppercase;
transition: color 250ms; }
#planet main article a:hover, #emplois main article a:hover, #index-news article a:hover {
#actualites main article a:hover, #emplois main article a:hover, #index-news article a:hover {
color: #ffd738; }
#planet main article h2, #emplois main article h2, #index-news article h2 {
#actualites main article h2, #emplois main article h2, #index-news article h2 {
flex: 1 100%; }
#planet main article article, #emplois main article article, #index-news article article {
#actualites main article article, #emplois main article article, #index-news article article {
background: #31313b;
border: 1px solid #25252D;
border: 1px solid #25252d;
box-sizing: border-box;
flex: 1 50%;
padding: 2em; }
#planet main article article h2, #emplois main article article h2, #index-news article article h2 {
#actualites main article article h2, #emplois main article article h2, #index-news article article h2 {
font-size: 1.2em;
font-weight: 600; }
@media screen and (max-width: 640px) {
#planet main article, #emplois main article, #index-news article {
#actualites main article, #emplois main article, #index-news article {
flex: 1 100%; } }
/*# sourceMappingURL=../static/css/style.sass.css.map */

View File

@ -18,11 +18,11 @@
<li{% if body_id == 'a-propos' %} class="active"{% endif %}>
<a href="{{ url_for('rest', name='a-propos') }}">Qui sommes-nous ?</a>
</li>
<li{% if body_id == 'planet' %} class="active"{% endif %}>
<a href="{{ url_for('feed', name='planet') }}">Actualités</a>
<li{% if body_id == 'actualites' %} class="active"{% endif %}>
<a href="{{ url_for('posts', name='actualites') }}">Actualités</a>
</li>
<li{% if body_id == 'emplois' %} class="active"{% endif %}>
<a href="{{ url_for('feed', name='emplois') }}">Offres d'emplois</a>
<a href="{{ url_for('posts', name='emplois') }}">Offres d'emplois</a>
</li>
<li{% if body_id == 'communaute' %} class="active"{% endif %}>
<a href="{{ url_for('pages', name='communaute') }}">Communauté</a>

View File

@ -1,27 +0,0 @@
{% extends '_layout.jinja2' %}
{% block header %}
<h1>{{ title }}</h1>
{% endblock header %}
{% block main %}
{# TODO: change this when the new website for news is available #}
{% if body_id == 'emplois' %}
<p>
Vous souhaitez publier une offre d'emploi ? Merci de vous rendre sur
la <a href="https://plone.afpy.org/doc/afpy/faq.html">Foire aux
Questions</a> et de suivre les indications qui y sont détaillées.
</p>
{% endif %}
{% for entry in entries[:12] %}
<article>
<h2>{{ entry.title }}</h2>
<time pubdate datetime="{{ entry.updated_parsed | datetime('%Y-%m-%d') }}">
{{ entry.updated_parsed | datetime('%x') }}
</time>
{{ entry.summary | safe }}
<p><a href="{{ entry.link }}">Lire la suite…</a></p>
</article>
{% endfor %}
{% endblock main %}

View File

@ -21,11 +21,14 @@
<h2>Actualités</h2>
<section id="index-news">
{% for entry in entries[:4] %}
{% for timestamp, post in posts.items() %}
<article>
<h3>{{ entry.title }}</h3>
{{ entry.summary | safe }}
<p><a href="{{ entry.link }}">Lire la suite…</a></p>
<h3>{{ post.title }}</h3>
<time pubdate datetime="{{ post.pubDate | parse_rfc822_datetime | datetime('%Y-%m-%d') }}">
{{ post.pubDate | parse_rfc822_datetime | datetime('%x') }}
</time>
{{ post.description | safe }}
<p><a href="{{ url_for('post', name=name, timestamp=timestamp) }}">Lire la suite…</a></p>
</article>
{% endfor %}
</section>

19
templates/post.html Normal file
View File

@ -0,0 +1,19 @@
{% extends '_layout.jinja2' %}
{% block header %}
<h1>{{ post.title }}</h1>
{% endblock header %}
{% block main %}
<article>
<time pubdate datetime="{{ post.pubDate | parse_rfc822_datetime | datetime('%Y-%m-%d') }}">
Posté le {{ post.pubDate | parse_rfc822_datetime | datetime('%x') }}
</time>
<p>
<em>
{{ post.description | safe }}
</em>
</p>
{{ post.content | safe }}
</article>
{% endblock main %}

18
templates/posts.html Normal file
View File

@ -0,0 +1,18 @@
{% extends '_layout.jinja2' %}
{% block header %}
<h1>{{ title }}</h1>
{% endblock header %}
{% block main %}
{% for timestamp, post in posts.items() %}
<article>
<h2>{{ post.title }}</h2>
<time pubdate datetime="{{ post.pubDate | parse_rfc822_datetime | datetime('%Y-%m-%d') }}">
{{ post.pubDate | parse_rfc822_datetime | datetime('%x') }}
</time>
{{ post.description | safe }}
<p><a href="{{ url_for('post', name=name, timestamp=timestamp) }}">Lire la suite…</a></p>
</article>
{% endfor %}
{% endblock main %}