Commit 1f687a83 authored by Roland Denis's avatar Roland Denis
Browse files

Adding expiration date for job offers.

Related to #57
parent 86b0b405
Pipeline #3788 canceled with stages
......@@ -19,8 +19,9 @@ FOR DEVELOPERS:
1) if it can raises an error (ie an invalid form), first add a property to
FormError class (contains the error message) and had a condition in the
has_error method.
2) add a corresponding property to FormData class and add (if necessary)
a sanity check in the check method. Also check if the field is mandatory.
2) add a corresponding property to FormData class (with default value) and
add (if necessary) a sanity check in the check method. Also check if
the field is mandatory.
3) update the web form template and/or the update_html_form function.
Don't forget to escape Jinja script so that the pelican pass don't
remove it. Add `required` attribute and a `*` if necessary.
......@@ -28,11 +29,12 @@ FOR DEVELOPERS:
functions.
5) add a corresponding property in JobOffer class
6) include this property in the id calculation (_calc_id method of JobOffer).
7) update job offer template and add corresponding line when launching
the rendering (_render method of JobOffer).
8) add corresponding line in the interface processors
7) add a corresponding line in the job offer file template
(content/job_offers/job_offer.md.template) and add corresponding line when
launching the rendering (_render method of JobOffer).
8) update the Pelican job offer template (job_offer.html).
9) add corresponding line in the interface processors
(process_cmdline and process_cgi so far).
9) add a corresponding line in the job offer template job_offer.md.template
# How has SimpleMDE editor been customized ?
1) Firstly, please consider using another editor since it seems to be
......@@ -97,6 +99,7 @@ JOBOFFER_TYPE = {
PELICAN_JOB_OFFER_PATH = 'content/job_offers'
ATTACHMENT_MIME_TYPE = ['application/pdf']
JOBOFFER_EXPIRATION_DELAY = datetime.timedelta(weeks=3*4)
###############################################################################
class Debug(object):
......@@ -121,6 +124,7 @@ class FormData(object):
location='',
duration='',
website='',
expiration='',
attachment_content=None,
attachment_name=''):
......@@ -133,6 +137,7 @@ class FormData(object):
self.location = location.strip()
self.duration = duration.strip()
self.website = website.strip()
self.expiration = expiration if expiration else (datetime.datetime.now() + JOBOFFER_EXPIRATION_DELAY).strftime('%Y-%m-%d')
self.attachment_content = attachment_content
self.attachment_name = attachment_name
......@@ -182,6 +187,14 @@ class FormData(object):
if attachment_mime not in ATTACHMENT_MIME_TYPE:
errors.attachment = 'Type de fichier invalide'
# Checking expiration date
try:
expiration_date = datetime.datetime.strptime(self.expiration, '%Y-%m-%d')
if expiration_date <= datetime.datetime.now():
errors.expiration = "L'offre doit expirer dans le futur"
except:
errors.expiration = "Format de date invalide"
return errors
def __str__(self):
......@@ -203,6 +216,7 @@ class FormError(object):
employer='',
email='',
job_type='',
expiration='',
attachment=''):
self.title = title
......@@ -211,6 +225,7 @@ class FormError(object):
self.employer = employer
self.email = email
self.job_type = job_type
self.expiration = expiration
self.attachment = attachment
def has_error(self):
......@@ -222,6 +237,7 @@ class FormError(object):
or self.employer
or self.email
or self.job_type
or self.expiration
or self.attachment
)
......@@ -272,6 +288,7 @@ class JobOffer(object):
self.location = form_data.location
self.duration = form_data.duration
self.website = form_data.website
self.expiration = datetime.datetime.strptime(form_data.expiration, '%Y-%m-%d')
# Attachment special care
if form_data.has_attachment():
......@@ -355,7 +372,7 @@ class JobOffer(object):
# Feeding the hash algo with the job_offer fields
m.update(b'|'.join(str(getattr(self, field)).encode() for field in
('title', 'job_type', 'author', 'employer', 'date', 'description', 'email', 'attachment_user_name', 'location', 'duration', 'website')
('title', 'job_type', 'author', 'employer', 'date', 'description', 'email', 'attachment_user_name', 'location', 'duration', 'website', 'expiration')
))
# Feeding the hash algo with the job_offer attachment
......@@ -401,6 +418,7 @@ class JobOffer(object):
location=self.location,
duration=self.duration,
website=self.website,
expiration=self.expiration,
)
......@@ -434,7 +452,7 @@ def filter_linebreaks(text, replacement='<br>'):
def filter_datetime(date, date_format='%Y-%m-%d %H:%M'):
""" Format a date. """
""" Format a date and time. """
return date.strftime(date_format)
......@@ -682,6 +700,11 @@ def process_cmdline():
default='',
help='Web page'
)
form_parser.add_argument(
'--expiration',
default='',
help='Offer expiration date'
)
form_parser.add_argument(
'--description',
default='',
......@@ -721,6 +744,7 @@ def process_cmdline():
location=args.location,
duration=args.duration,
website=args.website,
expiration=args.expiration,
description=args.description,
attachment_name=attachment_name,
attachment_content=attachment_content
......@@ -770,6 +794,9 @@ def process_cgi():
if 'website' in cgi_form:
form_data.website = cgi_form['website'].value
if 'expiration' in cgi_form:
form_data.expiration = cgi_form['expiration'].value
# Checking attachment
# TODO: checking 'done' attribute for transfer error
if 'file' in cgi_form:
......
......@@ -11,6 +11,7 @@ Job_Location: {{location|e|linebreaks(', ')}}
Job_Duration: {{duration|e|linebreaks(', ')}}
Job_Website: {{website|e|linebreaks(', ')}}
Job_Employer: {{employer|e|linebreaks(', ')}}
Expiration_Date: {{expiration|datetime('%Y-%m-%d')}}
Attachment: {{attachment_name|e|linebreaks(', ')}}
{{description|e|pelican}}
......@@ -102,7 +102,8 @@ MARKDOWN = {
# Processors for custom metadata (see CalculReader)
CUSTOM_METADATA_PROCESSORS = {
'start_date': lambda x, y: get_date(x),
'end_date': lambda x, y: get_date(x)
'end_date': lambda x, y: get_date(x),
'expiration_date': lambda x, y: get_date(x),
}
# Mandatory metadata per category (see CalculReader)
......
# -*- coding: utf-8 -*-
""" Adding custom filters """
import datetime
from pelican import signals
from pelican.utils import get_date
def date_interval(dt_interval):
......@@ -42,13 +44,14 @@ def previewed_articles(articles):
"""Select the articles that will be displayed on homepage preview"""
max_counter = {"job": 1, 'journee': 2, 'formation': 2}
max_selection = 5
while True:
selection = []
category_counter = {}
for article in articles:
for article in filter_by_date(articles, 'expiration_date'):
if article.category in max_counter.keys():
counter = category_counter.get(article.category, 0)
if counter < max_counter[article.category]: # Limit to 2 articles by category
......@@ -56,13 +59,15 @@ def previewed_articles(articles):
category_counter.update({article.category: counter + 1})
# Stop here because we display only 5 article previews
if len(selection) > 4:
return selection[:5]
if len(selection) >= max_selection:
return selection[:max_selection]
if len(articles) >= 5: # No job offer => increase max_counter for journee and formation
max_counter['journee'] += 1
max_counter['formation'] += 1
else: # Number of total articles is less than max preview (5)
# There is enough articles to fill the preview => increase all counters
if len(articles) >= max_selection:
for category in max_counter.keys():
max_counter[category] += 1
else: # Number of total articles is less than max preview
return selection
......@@ -74,12 +79,28 @@ def group_by_year(articles_gen):
return [(year, [a for a in articles if a.start_date.year == year]) for year in years]
def filter_by_date(articles, field_name, date = None):
"""Keep only articles with given date field that is in the future compared to an (optionaly) given date"""
if date is None:
date = datetime.datetime.now().strftime('%Y-%m-%d')
def predicate(a):
try:
return getattr(a, field_name) >= get_date(date)
except:
return True
return [a for a in articles if predicate(a)]
def add_filter(pelican):
pelican.env.filters.update({
'date_interval': date_interval,
'highlighted_article': highlighted_article,
'previewed_articles': previewed_articles,
'group_by_year': group_by_year,
'filter_by_date': filter_by_date,
})
......
......@@ -11,7 +11,7 @@
<div class="container text-center py-1">
<div class="row d-flex justify-content-center">
{% for tag, articles in tags | sort %}
{% if tag in JOB_OFFER_TAGS %}
{% if tag in JOB_OFFER_TAGS and articles | filter_by_date('expiration_date') | length %}
<div class="col-md-3 px-0">
<a href="{{SITEURL}}/tag/{{tag}}.html">{{JOB_OFFER_TAGS[tag]}}</a>
</div>
......@@ -39,7 +39,7 @@
<div class="container-fluid event">
<div class="row d-flex justify-content-center mx-xl-5 mx-lg-3 event" style="margin-top: 22px;">
{% import 'cards.html' as cards with context %}
{% for article in articles %}
{% for article in articles | filter_by_date('expiration_date') %}
{{ cards.dispatch(article) }}
{% endfor %}
</div>
......
......@@ -91,6 +91,11 @@
<textarea name="description" rows="5" cols="40" id="job_offer_description">{{ '{{form.description}}' }}</textarea>
<span class="error">{{ '{{errors.description}}' }}</span>
</p>
<p>
Date d'expiration de l'offre* <br>
<input type="date" name="expiration" value="{{ '{{form.expiration}}' }}" required>
<span class="error">{{ '{{errors.expiration}}' }}</span>
</p>
<p>
Fichier joint :
<input type="file" name="file" accept="{{ '{{file_accept}}' }}">
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment