Première version de la gestion des articles
This commit is contained in:
parent
ffe385c590
commit
78fb6875e6
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,6 +5,7 @@
|
|||
__pycache__
|
||||
afpy.egg-info
|
||||
static/css/*.map
|
||||
posts
|
||||
|
||||
# PyCharm project files
|
||||
.idea
|
||||
|
|
63
afpy.py
63
afpy.py
|
@ -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_)
|
||||
|
|
|
@ -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%
|
||||
|
|
|
@ -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 */
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
|
@ -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
19
templates/post.html
Normal 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
18
templates/posts.html
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user