# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2020 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 # ############################################################################## """Gestions des "nouvelles" """ import PyRSS2Gen from cStringIO import StringIO import datetime, re import time from stripogram import html2text, html2safehtml from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText from email.Header import Header from email import Encoders from notesdb import * from notes_log import log import scolars from sco_utils import SCO_ENCODING, SCO_ANNONCES_WEBSITE import sco_formsemestre _scolar_news_editor = EditableTable( "scolar_news", "news_id", ("date", "authenticated_user", "type", "object", "text", "url"), sortkey="date desc", output_formators={"date": DateISOtoDMY}, input_formators={"date": 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 = 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(context, REQUEST, 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. """ authuser_name = str(REQUEST.AUTHENTICATED_USER) cnx = context.GetDBConnexion() args = { "authenticated_user": authuser_name, "user_info": context.Users.user_info(user_name=authuser_name), "type": typ, "object": object, "text": text, "url": url, } log("news: %s" % args) 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 _LAST_NEWS[(authuser_name, typ, object)] = t _send_news_by_mail(context, args) return scolar_news_create(cnx, args, has_uniq_values=False) def scolar_news_summary(context, n=5): """Return last n news. News are "compressed", ie redondant events are joined. """ cnx = context.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ScoDocCursor) cursor.execute("select * from scolar_news order by date desc limit 100") 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 = DateISOtoDMY(r["date"]) # round key = (r["type"], r["object"], dmy) selected_news[key] = r news = selected_news.values() # sort by date, descending news.sort(lambda x, y: cmp(y["date"], x["date"])) news = news[:n] # mimic EditableTable.list output formatting: for n in news: d = n["date"] n["date822"] = n["date"].strftime("%a, %d %b %Y %H:%M:%S %z") # heure n["hm"] = n["date"].strftime("%Hh%M") n["rssdate"] = n["date"].strftime("%d/%m %Hh%M") # pour affichage for k in n.keys(): if n[k] is None: n[k] = "" if _scolar_news_editor.output_formators.has_key(k): n[k] = _scolar_news_editor.output_formators[k](n[k]) # date resumee j, m = n["date"].split("/")[:2] mois = scolars.abbrvmonthsnames[int(m) - 1] n["formatted_date"] = "%s %s %s" % (j, mois, n["hm"]) # indication semestre si ajout notes: infos = _get_formsemestre_infos_from_news(context, n) if infos: n["text"] += ( ' (%(descr_sem)s)' % infos ) n["text"] += ( " par " + context.Users.user_info(user_name=n["authenticated_user"])["nomcomplet"] ) return news def _get_formsemestre_infos_from_news(context, 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"] mods = context.Notes.do_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(context, formsemestre_id) except: # semestre n'existe plus return {} if sem["semestre_id"] > 0: descr_sem = "S%d" % 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(context, n=5, rssicon=None): """News summary, formated in HTML""" news = scolar_news_summary(context, n=n) if not news: return "" H = ['
Dernières opérations'] if rssicon: H.append('' + rssicon + "") H.append('") # Informations générales H.append( """
Pour être informé des évolutions de ScoDoc, vous pouvez vous abonner à la liste de diffusion.
""" % SCO_ANNONCES_WEBSITE ) H.append("
") return "\n".join(H) def scolar_news_summary_rss(context, title, sco_url, n=5): """rss feed for scolar news""" news = scolar_news_summary(context, n=n) items = [] for n in news: text = html2text(n["text"]) items.append( PyRSS2Gen.RSSItem( title=unicode("%s %s" % (n["rssdate"], text), SCO_ENCODING), link=sco_url + "/" + n["url"], pubDate=n["date822"], ) ) rss = PyRSS2Gen.RSS2( title=unicode(title, SCO_ENCODING), link=sco_url, description=unicode(title, SCO_ENCODING), lastBuildDate=datetime.datetime.now(), items=items, ) f = StringIO() rss.write_xml(f, encoding=SCO_ENCODING) f.seek(0) data = f.read() f.close() return data def _send_news_by_mail(context, n): """Notify by email """ infos = _get_formsemestre_infos_from_news(context, n) formsemestre_id = infos.get("formsemestre_id", None) prefs = context.get_preferences(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 %(descr_sem)s)' % 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 = context.ScoURL() txt = re.sub('href=.*?"', 'href="' + base + "/", txt) # Transforme les liens HTML en texte brut: 'texte' devient 'texte: url' # (si on veut des messages non html) txt = re.sub(r'(.*?)', r"\2: \1", txt) msg = MIMEMultipart() msg["Subject"] = Header("[ScoDoc] " + NEWS_MAP.get(n["type"], "?"), SCO_ENCODING) msg["From"] = prefs["email_from_addr"] txt = MIMEText(txt, "plain", SCO_ENCODING) msg.attach(txt) for email_addr in destinations: if email_addr: del msg["To"] msg["To"] = email_addr # log('xxx mail: %s' % msg) context.sendEmail(msg)