forked from ScoDoc/DocScoDoc
added python-docx
This commit is contained in:
parent
e51b09e7f6
commit
70db38bbb4
@ -1,274 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Gestion des "nouvelles"
|
||||
"""
|
||||
import re
|
||||
import time
|
||||
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import g
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.scodoc import sco_preferences
|
||||
from app import email
|
||||
|
||||
|
||||
_scolar_news_editor = ndb.EditableTable(
|
||||
"scolar_news",
|
||||
"news_id",
|
||||
("date", "authenticated_user", "type", "object", "text", "url"),
|
||||
filter_dept=True,
|
||||
sortkey="date desc",
|
||||
output_formators={"date": ndb.DateISOtoDMY},
|
||||
input_formators={"date": ndb.DateDMYtoISO},
|
||||
html_quote=False, # no user supplied data, needed to store html links
|
||||
)
|
||||
|
||||
NEWS_INSCR = "INSCR" # inscription d'étudiants (object=None ou formsemestre_id)
|
||||
NEWS_NOTE = "NOTES" # saisie note (object=moduleimpl_id)
|
||||
NEWS_FORM = "FORM" # modification formation (object=formation_id)
|
||||
NEWS_SEM = "SEM" # creation semestre (object=None)
|
||||
NEWS_MISC = "MISC" # unused
|
||||
NEWS_MAP = {
|
||||
NEWS_INSCR: "inscription d'étudiants",
|
||||
NEWS_NOTE: "saisie note",
|
||||
NEWS_FORM: "modification formation",
|
||||
NEWS_SEM: "création semestre",
|
||||
NEWS_MISC: "opération", # unused
|
||||
}
|
||||
NEWS_TYPES = list(NEWS_MAP.keys())
|
||||
|
||||
scolar_news_create = _scolar_news_editor.create
|
||||
scolar_news_list = _scolar_news_editor.list
|
||||
|
||||
_LAST_NEWS = {} # { (authuser_name, type, object) : time }
|
||||
|
||||
|
||||
def add(typ, object=None, text="", url=None, max_frequency=False):
|
||||
"""Ajoute une nouvelle.
|
||||
Si max_frequency, ne genere pas 2 nouvelles identiques à moins de max_frequency
|
||||
secondes d'intervalle.
|
||||
"""
|
||||
from app.scodoc import sco_users
|
||||
|
||||
authuser_name = current_user.user_name
|
||||
cnx = ndb.GetDBConnexion()
|
||||
args = {
|
||||
"authenticated_user": authuser_name,
|
||||
"user_info": sco_users.user_info(authuser_name),
|
||||
"type": typ,
|
||||
"object": object,
|
||||
"text": text,
|
||||
"url": url,
|
||||
}
|
||||
t = time.time()
|
||||
if max_frequency:
|
||||
last_news_time = _LAST_NEWS.get((authuser_name, typ, object), False)
|
||||
if last_news_time and (t - last_news_time < max_frequency):
|
||||
# log("not recording")
|
||||
return
|
||||
|
||||
log("news: %s" % args)
|
||||
|
||||
_LAST_NEWS[(authuser_name, typ, object)] = t
|
||||
|
||||
_send_news_by_mail(args)
|
||||
return scolar_news_create(cnx, args)
|
||||
|
||||
|
||||
def scolar_news_summary(n=5):
|
||||
"""Return last n news.
|
||||
News are "compressed", ie redondant events are joined.
|
||||
"""
|
||||
from app.scodoc import sco_users
|
||||
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
cursor.execute(
|
||||
"""SELECT id AS news_id, *
|
||||
FROM scolar_news
|
||||
WHERE dept_id=%(dept_id)s
|
||||
ORDER BY date DESC LIMIT 100
|
||||
""",
|
||||
{"dept_id": g.scodoc_dept_id},
|
||||
)
|
||||
selected_news = {} # (type,object) : news dict
|
||||
news = cursor.dictfetchall() # la plus récente d'abord
|
||||
|
||||
for r in reversed(news): # la plus ancienne d'abord
|
||||
# si on a deja une news avec meme (type,object)
|
||||
# et du meme jour, on la remplace
|
||||
dmy = ndb.DateISOtoDMY(r["date"]) # round
|
||||
key = (r["type"], r["object"], dmy)
|
||||
selected_news[key] = r
|
||||
|
||||
news = list(selected_news.values())
|
||||
# sort by date, descending
|
||||
news.sort(key=itemgetter("date"), reverse=True)
|
||||
news = news[:n]
|
||||
# mimic EditableTable.list output formatting:
|
||||
for n in news:
|
||||
n["date822"] = n["date"].strftime("%a, %d %b %Y %H:%M:%S %z")
|
||||
# heure
|
||||
n["hm"] = n["date"].strftime("%Hh%M")
|
||||
for k in n.keys():
|
||||
if n[k] is None:
|
||||
n[k] = ""
|
||||
if k in _scolar_news_editor.output_formators:
|
||||
n[k] = _scolar_news_editor.output_formators[k](n[k])
|
||||
# date resumee
|
||||
j, m = n["date"].split("/")[:2]
|
||||
mois = scu.MONTH_NAMES_ABBREV[int(m) - 1]
|
||||
n["formatted_date"] = f'{j} {mois} {n["hm"]}'
|
||||
# indication semestre si ajout notes:
|
||||
infos = _get_formsemestre_infos_from_news(n)
|
||||
if infos:
|
||||
n["text"] += (
|
||||
' (<a class="stdlink" href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(descr_sem)s</a>)'
|
||||
% infos
|
||||
)
|
||||
n["text"] += (
|
||||
" par " + sco_users.user_info(n["authenticated_user"])["nomcomplet"]
|
||||
)
|
||||
return news
|
||||
|
||||
|
||||
def _get_formsemestre_infos_from_news(n):
|
||||
"""Informations sur le semestre concerné par la nouvelle n
|
||||
{} si inexistant
|
||||
"""
|
||||
formsemestre_id = None
|
||||
if n["type"] == NEWS_INSCR:
|
||||
formsemestre_id = n["object"]
|
||||
elif n["type"] == NEWS_NOTE:
|
||||
moduleimpl_id = n["object"]
|
||||
if n["object"]:
|
||||
mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
|
||||
if not mods:
|
||||
return {} # module does not exists anymore
|
||||
mod = mods[0]
|
||||
formsemestre_id = mod["formsemestre_id"]
|
||||
|
||||
if not formsemestre_id:
|
||||
return {}
|
||||
|
||||
try:
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
except ValueError:
|
||||
# semestre n'existe plus
|
||||
return {}
|
||||
|
||||
if sem["semestre_id"] > 0:
|
||||
descr_sem = f'S{sem["semestre_id"]}'
|
||||
else:
|
||||
descr_sem = ""
|
||||
if sem["modalite"]:
|
||||
descr_sem += " " + sem["modalite"]
|
||||
return {"formsemestre_id": formsemestre_id, "sem": sem, "descr_sem": descr_sem}
|
||||
|
||||
|
||||
def scolar_news_summary_html(n=5):
|
||||
"""News summary, formated in HTML"""
|
||||
news = scolar_news_summary(n=n)
|
||||
if not news:
|
||||
return ""
|
||||
H = ['<div class="news"><span class="newstitle">Dernières opérations']
|
||||
H.append('</span><ul class="newslist">')
|
||||
|
||||
for n in news:
|
||||
H.append(
|
||||
'<li class="newslist"><span class="newsdate">%(formatted_date)s</span><span class="newstext">%(text)s</span></li>'
|
||||
% n
|
||||
)
|
||||
H.append("</ul>")
|
||||
|
||||
# Informations générales
|
||||
H.append(
|
||||
"""<div>
|
||||
Pour être informé des évolutions de ScoDoc,
|
||||
vous pouvez vous
|
||||
<a class="stdlink" href="%s">
|
||||
abonner à la liste de diffusion</a>.
|
||||
</div>
|
||||
"""
|
||||
% scu.SCO_ANNONCES_WEBSITE
|
||||
)
|
||||
|
||||
H.append("</div>")
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def _send_news_by_mail(n):
|
||||
"""Notify by email"""
|
||||
infos = _get_formsemestre_infos_from_news(n)
|
||||
formsemestre_id = infos.get("formsemestre_id", None)
|
||||
prefs = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
|
||||
destinations = prefs["emails_notifications"] or ""
|
||||
destinations = [x.strip() for x in destinations.split(",")]
|
||||
destinations = [x for x in destinations if x]
|
||||
if not destinations:
|
||||
return
|
||||
#
|
||||
txt = n["text"]
|
||||
if infos:
|
||||
txt += "\n\nSemestre %(titremois)s\n\n" % infos["sem"]
|
||||
txt += (
|
||||
"""<a href="Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(descr_sem)s</a>
|
||||
"""
|
||||
% infos
|
||||
)
|
||||
txt += "\n\nEffectué par: %(nomcomplet)s\n" % n["user_info"]
|
||||
|
||||
txt = (
|
||||
"\n"
|
||||
+ txt
|
||||
+ """\n
|
||||
--- Ceci est un message de notification automatique issu de ScoDoc
|
||||
--- vous recevez ce message car votre adresse est indiquée dans les paramètres de ScoDoc.
|
||||
"""
|
||||
)
|
||||
|
||||
# Transforme les URL en URL absolue
|
||||
base = scu.ScoURL()
|
||||
txt = re.sub('href=.*?"', 'href="' + base + "/", txt)
|
||||
|
||||
# Transforme les liens HTML en texte brut: '<a href="url">texte</a>' devient 'texte: url'
|
||||
# (si on veut des messages non html)
|
||||
txt = re.sub(r'<a.*?href\s*=\s*"(.*?)".*?>(.*?)</a>', r"\2: \1", txt)
|
||||
|
||||
subject = "[ScoDoc] " + NEWS_MAP.get(n["type"], "?")
|
||||
sender = prefs["email_from_addr"]
|
||||
|
||||
email.send_email(subject, sender, destinations, txt)
|
@ -1,5 +1,5 @@
|
||||
alembic==1.7.5
|
||||
astroid==2.9.1
|
||||
astroid==2.11.2
|
||||
attrs==21.4.0
|
||||
Babel==2.9.1
|
||||
blinker==1.4
|
||||
@ -35,6 +35,7 @@ isort==5.10.1
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.3
|
||||
lazy-object-proxy==1.7.1
|
||||
lxml==4.8.0
|
||||
Mako==1.1.6
|
||||
MarkupSafe==2.0.1
|
||||
mccabe==0.6.1
|
||||
@ -53,6 +54,7 @@ pyOpenSSL==21.0.0
|
||||
pyparsing==3.0.6
|
||||
pytest==6.2.5
|
||||
python-dateutil==2.8.2
|
||||
python-docx==0.8.11
|
||||
python-dotenv==0.19.2
|
||||
python-editor==1.0.4
|
||||
pytz==2021.3
|
||||
|
Loading…
Reference in New Issue
Block a user