diff --git a/ZAbsences.py b/ZAbsences.py index e10afce5e9..ab194f5ab4 100644 --- a/ZAbsences.py +++ b/ZAbsences.py @@ -666,21 +666,6 @@ class ZAbsences( if self.CountAbs(etudid, jour, jour, matin, moduleimpl_id) == 0: self._AddAbsence(etudid, jour, matin, 0, REQUEST, "", moduleimpl_id) - # - security.declareProtected(ScoView, "CalSelectWeek") - - def CalSelectWeek(self, year=None, REQUEST=None): - "display calendar allowing week selection" - if not year: - year = scu.AnneeScolaire(REQUEST) - sems = sco_formsemestre.do_formsemestre_list(self) - if not sems: - js = "" - else: - js = 'onmouseover="highlightweek(this);" onmouseout="deselectweeks();" onclick="wclick(this);"' - C = sco_abs.YearTable(self, int(year), dayattributes=js) - return C - # --- Misc tools.... ------------------ def _isFarFutur(self, jour): @@ -691,65 +676,6 @@ class ZAbsences( # 6 mois ~ 182 jours: return j - datetime.date.today() > datetime.timedelta(182) - security.declareProtected(ScoView, "is_work_saturday") - - def is_work_saturday(self): - "Vrai si le samedi est travaillé" - return int(self.get_preference("work_saturday")) - - security.declareProtected(ScoView, "day_names") - - def day_names(self): - """Returns week day names. - If work_saturday property is set, include saturday - """ - if self.is_work_saturday(): - return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"] - else: - return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"] - - security.declareProtected(ScoView, "ListMondays") - - def ListMondays(self, year=None, REQUEST=None): - """return list of mondays (ISO dates), from september to june""" - if not year: - year = scu.AnneeScolaire(REQUEST) - d = ddmmyyyy("1/9/%d" % year, work_saturday=self.is_work_saturday()) - while d.weekday != 0: - d = d.next() - end = ddmmyyyy("1/7/%d" % (year + 1), work_saturday=self.is_work_saturday()) - L = [d] - while d < end: - d = d.next(days=7) - L.append(d) - return map(lambda x: x.ISO(), L) - - security.declareProtected(ScoView, "NextISODay") - - def NextISODay(self, date): - "return date after date" - d = ddmmyyyy(date, fmt="iso", work_saturday=self.is_work_saturday()) - return d.next().ISO() - - security.declareProtected(ScoView, "DateRangeISO") - - def DateRangeISO(self, date_beg, date_end, workable=1): - """returns list of dates in [date_beg,date_end] - workable = 1 => keeps only workable days""" - if not date_beg: - raise ScoValueError("pas de date spécifiée !") - if not date_end: - date_end = date_beg - r = [] - cur = ddmmyyyy(date_beg, work_saturday=self.is_work_saturday()) - end = ddmmyyyy(date_end, work_saturday=self.is_work_saturday()) - while cur <= end: - if (not workable) or cur.iswork(): - r.append(cur) - cur = cur.next() - - return map(lambda x: x.ISO(), r) - # ------------ HTML Interfaces security.declareProtected(ScoAbsChange, "SignaleAbsenceGrHebdo") @@ -801,8 +727,8 @@ class ZAbsences( # calcule dates jours de cette semaine # liste de dates iso "yyyy-mm-dd" datessem = [notesdb.DateDMYtoISO(datelundi)] - for _ in self.day_names()[1:]: - datessem.append(self.NextISODay(datessem[-1])) + for _ in sco_abs.day_names(self)[1:]: + datessem.append(sco_abs.next_iso_day(self, datessem[-1])) # if groups_infos.tous_les_etuds_du_sem: gr_tit = "en" @@ -944,12 +870,12 @@ class ZAbsences( sem = sco_formsemestre.do_formsemestre_list( self, {"formsemestre_id": formsemestre_id} )[0] - - jourdebut = ddmmyyyy(datedebut, work_saturday=self.is_work_saturday()) - jourfin = ddmmyyyy(datefin, work_saturday=self.is_work_saturday()) + work_saturday = sco_abs.is_work_saturday(self) + jourdebut = ddmmyyyy(datedebut, work_saturday=work_saturday) + jourfin = ddmmyyyy(datefin, work_saturday=work_saturday) today = ddmmyyyy( time.strftime("%d/%m/%Y", time.localtime()), - work_saturday=self.is_work_saturday(), + work_saturday=work_saturday, ) today.next() if jourfin > today: # ne propose jamais les semaines dans le futur @@ -964,7 +890,7 @@ class ZAbsences( ) # calcule dates dates = [] # ddmmyyyy instances - d = ddmmyyyy(datedebut, work_saturday=self.is_work_saturday()) + d = ddmmyyyy(datedebut, work_saturday=work_saturday) while d <= jourfin: dates.append(d) d = d.next(7) # avance d'une semaine @@ -982,7 +908,7 @@ class ZAbsences( url_link_semaines += "&moduleimpl_id=" + moduleimpl_id # dates = [x.ISO() for x in dates] - dayname = self.day_names()[jourdebut.weekday] + dayname = sco_abs.day_names(self)[jourdebut.weekday] if groups_infos.tous_les_etuds_du_sem: gr_tit = "en" @@ -1119,7 +1045,7 @@ class ZAbsences( odates = [datetime.date(*[int(x) for x in d.split("-")]) for d in dates] # Titres colonnes noms_jours = [] # eg [ "Lundi", "mardi", "Samedi", ... ] - jn = self.day_names() + jn = sco_abs.day_names(self) for d in odates: idx_jour = d.weekday() noms_jours.append(jn[idx_jour]) @@ -1844,7 +1770,7 @@ ou entrez une date pour visualiser les absents un jour donné : # 1-- ajout des absences (et justifs) datedebut = billet["abs_begin"].strftime("%d/%m/%Y") datefin = billet["abs_end"].strftime("%d/%m/%Y") - dates = self.DateRangeISO(datedebut, datefin) + dates = sco_abs.DateRangeISO(self, datedebut, datefin) # commence après-midi ? if dates and billet["abs_begin"].hour > 11: self._AddAbsence( diff --git a/ZEntreprises.py b/ZEntreprises.py index 53d287a0ea..56f16eaacf 100644 --- a/ZEntreprises.py +++ b/ZEntreprises.py @@ -43,7 +43,6 @@ from sco_permissions import ScoEntrepriseView, ScoEntrepriseChange # --------------- -from notesdb import * from notes_log import log from scolog import logdb from sco_utils import SCO_ENCODING @@ -53,169 +52,17 @@ import VERSION from gen_tables import GenTable from TrivialFormulator import TrivialFormulator, TF import scolars +import sco_entreprises -def _format_nom(nom): - "formatte nom (filtre en entree db) d'une entreprise" - if not nom: - return nom - nom = nom.decode(SCO_ENCODING) - return (nom[0].upper() + nom[1:]).encode(SCO_ENCODING) +def entreprise_header(context, REQUEST=None, page_title=""): + "common header for all Entreprises pages" + return context.sco_header(REQUEST, container=context, page_title=page_title) -class EntreprisesEditor(EditableTable): - def delete(self, cnx, oid): - "delete correspondants and contacts, then self" - # first, delete all correspondants and contacts - cursor = cnx.cursor(cursor_factory=ScoDocCursor) - cursor.execute( - "delete from entreprise_contact where entreprise_id=%(entreprise_id)s", - {"entreprise_id": oid}, - ) - cursor.execute( - "delete from entreprise_correspondant where entreprise_id=%(entreprise_id)s", - {"entreprise_id": oid}, - ) - cnx.commit() - EditableTable.delete(self, cnx, oid) - - def list( - self, - cnx, - args={}, - operator="and", - test="=", - sortkey=None, - sort_on_contact=False, - ZEntrepriseInstance=None, - limit="", - offset="", - ): - # list, then sort on date of last contact - R = EditableTable.list( - self, - cnx, - args=args, - operator=operator, - test=test, - sortkey=sortkey, - limit=limit, - offset=offset, - ) - if sort_on_contact: - for r in R: - c = ZEntrepriseInstance.do_entreprise_contact_list( - args={"entreprise_id": r["entreprise_id"]}, disable_formatting=True - ) - if c: - r["date"] = max([x["date"] or datetime.date.min for x in c]) - else: - r["date"] = datetime.date.min - # sort - R.sort(lambda r1, r2: cmp(r2["date"], r1["date"])) - for r in R: - r["date"] = DateISOtoDMY(r["date"]) - return R - - def list_by_etud( - self, cnx, args={}, sort_on_contact=False, disable_formatting=False - ): - "cherche rentreprise ayant eu contact avec etudiant" - cursor = cnx.cursor(cursor_factory=ScoDocCursor) - cursor.execute( - "select E.*, I.nom as etud_nom, I.prenom as etud_prenom, C.date from entreprises E, entreprise_contact C, identite I where C.entreprise_id = E.entreprise_id and C.etudid = I.etudid and I.nom ~* %(etud_nom)s ORDER BY E.nom", - args, - ) - _, res = [x[0] for x in cursor.description], cursor.dictfetchall() - R = [] - for r in res: - r["etud_prenom"] = r["etud_prenom"] or "" - d = {} - for key in r: - v = r[key] - # format value - if not disable_formatting and self.output_formators.has_key(key): - v = self.output_formators[key](v) - d[key] = v - R.append(d) - # sort - if sort_on_contact: - R.sort( - lambda r1, r2: cmp( - r2["date"] or datetime.date.min, r1["date"] or datetime.date.min - ) - ) - for r in R: - r["date"] = DateISOtoDMY(r["date"] or datetime.date.min) - return R - - -_entreprisesEditor = EntreprisesEditor( - "entreprises", - "entreprise_id", - ( - "entreprise_id", - "nom", - "adresse", - "ville", - "codepostal", - "pays", - "contact_origine", - "secteur", - "privee", - "localisation", - "qualite_relation", - "plus10salaries", - "note", - "date_creation", - ), - sortkey="nom", - input_formators={"nom": _format_nom}, -) - -# ----------- Correspondants -_entreprise_correspEditor = EditableTable( - "entreprise_correspondant", - "entreprise_corresp_id", - ( - "entreprise_corresp_id", - "entreprise_id", - "civilite", - "nom", - "prenom", - "fonction", - "phone1", - "phone2", - "mobile", - "fax", - "mail1", - "mail2", - "note", - ), - sortkey="nom", -) - - -# ----------- Contacts -_entreprise_contactEditor = EditableTable( - "entreprise_contact", - "entreprise_contact_id", - ( - "entreprise_contact_id", - "date", - "type_contact", - "entreprise_id", - "entreprise_corresp_id", - "etudid", - "description", - "enseignant", - ), - sortkey="date", - output_formators={"date": DateISOtoDMY}, - input_formators={"date": DateDMYtoISO}, -) - -# --------------- +def entreprise_footer(context, REQUEST): + "common entreprise footer" + return context.sco_footer(REQUEST) class ZEntreprises( @@ -251,42 +98,22 @@ class ZEntreprises( self.id = id self.title = title - security.declareProtected(ScoEntrepriseView, "entreprise_header") - - def entreprise_header(self, REQUEST=None, page_title=""): - "common header for all Entreprises pages" - authuser = REQUEST.AUTHENTICATED_USER - # _read_only is used to modify pages properties (links, buttons) - # Python methods (do_xxx in this class) are also protected individualy) - if authuser.has_permission(ScoEntrepriseChange, self): - REQUEST.set("_read_only", False) - else: - REQUEST.set("_read_only", True) - return self.sco_header(REQUEST, container=self, page_title=page_title) - - security.declareProtected(ScoEntrepriseView, "entreprise_footer") - - def entreprise_footer(self, REQUEST): - "common entreprise footer" - return self.sco_footer(REQUEST) - security.declareProtected(ScoEntrepriseView, "sidebar") def sidebar(self, REQUEST): "barre gauche (overide std sco sidebar)" # rewritten from legacy DTML code - context = self - params = {"ScoURL": context.ScoURL()} - + # XXX rare cas restant d'utilisation de l'acquisition Zope2: à revoir + params = {"ScoURL": self.ScoURL()} H = [ """ """) return "".join(H) @@ -352,12 +181,13 @@ class ZEntreprises( offset = int(offset or 0) if etud_nom: - entreprises = self.do_entreprise_list_by_etud( - args=REQUEST.form, sort_on_contact=True + entreprises = sco_entreprises.do_entreprise_list_by_etud( + self, args=REQUEST.form, sort_on_contact=True ) table_navigation = "" else: - entreprises = self.do_entreprise_list( + entreprises = sco_entreprises.do_entreprise_list( + self, args=REQUEST.form, test="~*", sort_on_contact=True, @@ -390,15 +220,15 @@ class ZEntreprises( # Ajout des liens sur la table: for e in entreprises: e["_nom_target"] = "entreprise_edit?entreprise_id=%(entreprise_id)s" % e - e["correspondants"] = self.do_entreprise_correspondant_list( - args={"entreprise_id": e["entreprise_id"]} + e["correspondants"] = sco_entreprises.do_entreprise_correspondant_list( + self, args={"entreprise_id": e["entreprise_id"]} ) e["nbcorr"] = "%d corr." % len(e["correspondants"]) e["_nbcorr_target"] = ( "entreprise_correspondant_list?entreprise_id=%(entreprise_id)s" % e ) - e["contacts"] = self.do_entreprise_contact_list( - args={"entreprise_id": e["entreprise_id"]} + e["contacts"] = sco_entreprises.do_entreprise_contact_list( + self, args={"entreprise_id": e["entreprise_id"]} ) e["nbcontact"] = "%d contacts." % len(e["contacts"]) e["_nbcontact_target"] = ( @@ -430,12 +260,14 @@ class ZEntreprises( return tab.make_page(self, format=format, REQUEST=REQUEST) else: H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header( + self, REQUEST=REQUEST, page_title="Suivi entreprises" + ), """

Suivi relations entreprises

""", """
""", tab.html(), """
""", - self.entreprise_footer(REQUEST), + entreprise_footer(self, REQUEST), ] return "\n".join(H) @@ -443,17 +275,21 @@ class ZEntreprises( def entreprise_contact_list(self, entreprise_id=None, format="html", REQUEST=None): """Liste des contacts de l'entreprise""" - H = [self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises")] + H = [entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises")] if entreprise_id: - E = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] - C = self.do_entreprise_contact_list(args={"entreprise_id": entreprise_id}) + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": entreprise_id} + )[0] + C = sco_entreprises.do_entreprise_contact_list( + self, args={"entreprise_id": entreprise_id} + ) H.append( """

Listes des contacts avec l'entreprise %(nom)s

""" % E ) else: - C = self.do_entreprise_contact_list(args={}) + C = sco_entreprises.do_entreprise_contact_list(self, args={}) H.append( """

Listes des contacts

""" @@ -465,8 +301,8 @@ class ZEntreprises( REQUEST.URL1, c["entreprise_contact_id"], ) - c["entreprise"] = self.do_entreprise_list( - args={"entreprise_id": c["entreprise_id"]} + c["entreprise"] = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": c["entreprise_id"]} )[0] if c["etudid"]: c["etud"] = self.getEtudInfo(etudid=c["etudid"], filled=1)[0] @@ -502,7 +338,7 @@ class ZEntreprises( H.append(tab.html()) - if not REQUEST["_read_only"]: # portage DTML, à modifier + if REQUEST.AUTHENTICATED_USER.has_permission(ScoEntrepriseChange, self): if entreprise_id: H.append( """

nouveau "contact"

@@ -510,7 +346,7 @@ class ZEntreprises( % E ) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseView, "entreprise_correspondant_list") @@ -522,23 +358,28 @@ class ZEntreprises( REQUEST=None, ): """Liste des correspondants de l'entreprise""" - E = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": entreprise_id} + )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Listes des correspondants dans l'entreprise %(nom)s

""" % E, ] - correspondants = self.do_entreprise_correspondant_list( - args={"entreprise_id": entreprise_id} + correspondants = sco_entreprises.do_entreprise_correspondant_list( + self, args={"entreprise_id": entreprise_id} ) for c in correspondants: c["nomprenom"] = c["nom"].upper() + " " + c["nom"].capitalize() - c["_nomprenom_target"] = ( - "%s/entreprise_correspondant_edit?entreprise_corresp_id=%s" - % (REQUEST.URL1, c["entreprise_corresp_id"]), + c[ + "_nomprenom_target" + ] = "%s/entreprise_correspondant_edit?entreprise_corresp_id=%s" % ( + REQUEST.URL1, + c["entreprise_corresp_id"], ) + c["nom_entreprise"] = E["nom"] l = [] if c["phone1"]: @@ -559,11 +400,11 @@ class ZEntreprises( ] ) c["modifier"] = ( - 'modifier' + 'modifier' % c["entreprise_corresp_id"] ) c["supprimer"] = ( - 'supprimer' + 'supprimer' % c["entreprise_corresp_id"] ) tab = GenTable( @@ -604,34 +445,36 @@ class ZEntreprises( H.append(tab.html()) - if not REQUEST["_read_only"]: # portage DTML, à modifier + if REQUEST.AUTHENTICATED_USER.has_permission(ScoEntrepriseChange, self): H.append( """

Ajouter un correspondant dans l'entreprise %(nom)s

""" % E ) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseView, "entreprise_contact_edit") def entreprise_contact_edit(self, entreprise_contact_id, REQUEST=None): """Form edit contact""" - c = self.do_entreprise_contact_list( - args={"entreprise_contact_id": entreprise_contact_id} + c = sco_entreprises.do_entreprise_contact_list( + self, args={"entreprise_contact_id": entreprise_contact_id} )[0] link_create_corr = ( 'créer un nouveau correspondant' % (REQUEST.URL1, c["entreprise_id"]) ) - E = self.do_entreprise_list(args={"entreprise_id": c["entreprise_id"]})[0] - correspondants = self.do_entreprise_correspondant_listnames( - args={"entreprise_id": c["entreprise_id"]} + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": c["entreprise_id"]} + )[0] + correspondants = sco_entreprises.do_entreprise_correspondant_listnames( + self, args={"entreprise_id": c["entreprise_id"]} ) + [("inconnu", "")] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Suivi entreprises

Contact avec entreprise %(nom)s

""" % E, @@ -708,12 +551,14 @@ class ZEntreprises( cancelbutton="Annuler", initvalues=c, submitlabel="Modifier les valeurs", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) - if not REQUEST["_read_only"]: # portage DTML, à modifier + if REQUEST.AUTHENTICATED_USER.has_permission(ScoEntrepriseChange, self): H.append( """

Supprimer ce contact

""" % entreprise_contact_id @@ -721,30 +566,31 @@ class ZEntreprises( elif tf[0] == -1: REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - etudok = self.do_entreprise_check_etudiant(tf[2]["etudiant"]) + etudok = sco_entreprises.do_entreprise_check_etudiant( + self, tf[2]["etudiant"] + ) if etudok[0] == 0: H.append("""

%s

""" % etudok[1]) else: tf[2].update({"etudid": etudok[1]}) - self.do_entreprise_contact_edit(tf[2]) + sco_entreprises.do_entreprise_contact_edit(self, tf[2]) REQUEST.RESPONSE.redirect( REQUEST.URL1 + "/entreprise_contact_list?entreprise_id=" + str(c["entreprise_id"]) ) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseView, "entreprise_correspondant_edit") def entreprise_correspondant_edit(self, entreprise_corresp_id, REQUEST=None): """Form édition d'un correspondant""" - # F -> c - c = self.do_entreprise_correspondant_list( - args={"entreprise_corresp_id": entreprise_corresp_id} + c = sco_entreprises.do_entreprise_correspondant_list( + self, args={"entreprise_corresp_id": entreprise_corresp_id} )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Édition contact entreprise

""", ] tf = TrivialFormulator( @@ -829,7 +675,9 @@ class ZEntreprises( cancelbutton="Annuler", initvalues=c, submitlabel="Modifier les valeurs", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) @@ -839,21 +687,23 @@ class ZEntreprises( % (REQUEST.URL1, c["entreprise_id"]) ) else: - self.do_entreprise_correspondant_edit(tf[2]) + sco_entreprises.do_entreprise_correspondant_edit(self, tf[2]) REQUEST.RESPONSE.redirect( "%s/entreprise_correspondant_list?entreprise_id=%s" % (REQUEST.URL1, c["entreprise_id"]) ) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseChange, "entreprise_contact_create") def entreprise_contact_create(self, entreprise_id, REQUEST=None): """Form création contact""" - E = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] - correspondants = self.do_entreprise_correspondant_listnames( - args={"entreprise_id": entreprise_id} + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": entreprise_id} + )[0] + correspondants = sco_entreprises.do_entreprise_correspondant_listnames( + self, args={"entreprise_id": entreprise_id} ) if not correspondants: correspondants = [("inconnu", "")] @@ -863,7 +713,7 @@ class ZEntreprises( % (REQUEST.URL1, entreprise_id) ) H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Nouveau "contact" avec l'entreprise %(nom)s

""" % E, ] @@ -934,32 +784,36 @@ class ZEntreprises( ), cancelbutton="Annuler", submitlabel="Ajouter ce contact", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) elif tf[0] == -1: REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - etudok = self.do_entreprise_check_etudiant(tf[2]["etudiant"]) + etudok = sco_entreprises.do_entreprise_check_etudiant( + self, tf[2]["etudiant"] + ) if etudok[0] == 0: H.append("""

%s

""" % etudok[1]) else: tf[2].update({"etudid": etudok[1]}) - self.do_entreprise_contact_create(tf[2]) + sco_entreprises.do_entreprise_contact_create(self, tf[2]) REQUEST.RESPONSE.redirect(REQUEST.URL1) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseChange, "entreprise_contact_delete") def entreprise_contact_delete(self, entreprise_contact_id, REQUEST=None): """Form delete contact""" - c = self.do_entreprise_contact_list( - args={"entreprise_contact_id": entreprise_contact_id} + c = sco_entreprises.do_entreprise_contact_list( + self, args={"entreprise_contact_id": entreprise_contact_id} )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Suppression du contact

""", ] tf = TrivialFormulator( @@ -969,25 +823,31 @@ class ZEntreprises( initvalues=c, submitlabel="Confirmer la suppression", cancelbutton="Annuler", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) elif tf[0] == -1: REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - self.do_entreprise_contact_delete(c["entreprise_contact_id"]) + sco_entreprises.do_entreprise_contact_delete( + self, c["entreprise_contact_id"] + ) REQUEST.RESPONSE.redirect(REQUEST.URL1) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseChange, "entreprise_correspondant_create") def entreprise_correspondant_create(self, entreprise_id, REQUEST=None): """Form création correspondant""" - E = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": entreprise_id} + )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Nouveau correspondant l'entreprise %(nom)s

""" % E, ] @@ -1070,27 +930,29 @@ class ZEntreprises( ), cancelbutton="Annuler", submitlabel="Ajouter ce correspondant", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) elif tf[0] == -1: REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - self.do_entreprise_correspondant_create(tf[2]) + sco_entreprises.do_entreprise_correspondant_create(self, tf[2]) REQUEST.RESPONSE.redirect(REQUEST.URL1) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseChange, "entreprise_correspondant_delete") def entreprise_correspondant_delete(self, entreprise_corresp_id, REQUEST=None): """Form delete correspondant""" - c = self.do_entreprise_correspondant_list( - args={"entreprise_corresp_id": entreprise_corresp_id} + c = sco_entreprises.do_entreprise_correspondant_list( + self, args={"entreprise_corresp_id": entreprise_corresp_id} )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Suppression du correspondant %(nom)s %(prenom)s

""" % c, ] tf = TrivialFormulator( @@ -1100,32 +962,38 @@ class ZEntreprises( initvalues=c, submitlabel="Confirmer la suppression", cancelbutton="Annuler", - readonly=REQUEST["_read_only"], + readonly=not REQUEST.AUTHENTICATED_USER.has_permission( + ScoEntrepriseChange, self + ), ) if tf[0] == 0: H.append(tf[1]) elif tf[0] == -1: REQUEST.RESPONSE.redirect(REQUEST.URL1) else: - self.do_entreprise_correspondant_delete(c["entreprise_corresp_id"]) + sco_entreprises.do_entreprise_correspondant_delete( + self, c["entreprise_corresp_id"] + ) REQUEST.RESPONSE.redirect(REQUEST.URL1) - H.append(self.entreprise_footer(REQUEST)) + H.append(entreprise_footer(self, REQUEST)) return "\n".join(H) security.declareProtected(ScoEntrepriseChange, "entreprise_delete") def entreprise_delete(self, entreprise_id, REQUEST=None): """Form delete entreprise""" - E = self.do_entreprise_list(args={"entreprise_id": entreprise_id})[0] + E = sco_entreprises.do_entreprise_list( + self, args={"entreprise_id": entreprise_id} + )[0] H = [ - self.entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"), + entreprise_header(self, REQUEST=REQUEST, page_title="Suivi entreprises"), """

Suppression de l'entreprise %(nom)s

Attention: supression définitive de l'entreprise, de ses correspondants et contacts.

""" % E, ] - Cl = self.do_entreprise_correspondant_list( - args={"entreprise_id": entreprise_id} + Cl = sco_entreprises.do_entreprise_correspondant_list( + self, args={"entreprise_id": entreprise_id} ) if Cl: H.append( @@ -1135,7 +1003,9 @@ class ZEntreprises( H.append("""
  • %(nom)s %(prenom)s (%(fonction)s)
  • """ % c) H.append("""""") - Cts = self.do_entreprise_contact_list(args={"entreprise_id": entreprise_id}) + Cts = sco_entreprises.do_entreprise_contact_list( + self, args={"entreprise_id": entreprise_id} + ) if Cts: H.append( """

    Contacts avec l'entreprise qui seront supprimés:

    ") - return "\n".join(H) + context.entreprise_footer(REQUEST) + return "\n".join(H) + entreprise_footer(self, REQUEST) elif tf[0] == -1: return REQUEST.RESPONSE.redirect(REQUEST.URL1 + "?start=" + start) else: - self.do_entreprise_edit(tf[2]) + sco_entreprises.do_entreprise_edit(self, tf[2]) return REQUEST.RESPONSE.redirect(REQUEST.URL1 + "?start=" + start) diff --git a/ZScolar.py b/ZScolar.py index c39d8bdc57..8d43078093 100644 --- a/ZScolar.py +++ b/ZScolar.py @@ -599,7 +599,7 @@ UE11 Découverte métiers (code UCOD46, 16 ECTS, Apo ') FA.append('") diff --git a/sco_abs.py b/sco_abs.py index 8d97d949e8..21538e677a 100644 --- a/sco_abs.py +++ b/sco_abs.py @@ -43,6 +43,11 @@ import sco_formsemestre import sco_compute_moy +def is_work_saturday(context): + "Vrai si le samedi est travaillé" + return int(context.get_preference("work_saturday")) + + def MonthNbDays(month, year): "returns nb of days in month" if month > 7: @@ -173,6 +178,39 @@ class ddmmyyyy: # d = ddmmyyyy( '21/12/99' ) +def DateRangeISO(context, date_beg, date_end, workable=1): + """returns list of dates in [date_beg,date_end] + workable = 1 => keeps only workable days""" + if not date_beg: + raise ScoValueError("pas de date spécifiée !") + if not date_end: + date_end = date_beg + r = [] + work_saturday = is_work_saturday(context) + cur = ddmmyyyy(date_beg, work_saturday=work_saturday) + end = ddmmyyyy(date_end, work_saturday=work_saturday) + while cur <= end: + if (not workable) or cur.iswork(): + r.append(cur) + cur = cur.next() + + return map(lambda x: x.ISO(), r) + + +def day_names(context): + """Returns week day names. + If work_saturday property is set, include saturday + """ + if is_work_saturday(context): + return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"] + else: + return ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"] + + +def next_iso_day(context, date): + "return date after date" + d = ddmmyyyy(date, fmt="iso", work_saturday=is_work_saturday(context)) + return d.next().ISO() def YearTable( @@ -207,7 +245,7 @@ def YearTable( events, halfday, dayattributes, - context.is_work_saturday(), + is_work_saturday(context), pad_width=pad_width, ) ) diff --git a/sco_abs_views.py b/sco_abs_views.py index 9ede1d384f..a76e225a64 100644 --- a/sco_abs_views.py +++ b/sco_abs_views.py @@ -28,12 +28,16 @@ """Pages HTML gestion absences (la plupart portées du DTML) """ +import datetime from stripogram import html2text, html2safehtml + from gen_tables import GenTable -from notesdb import * -from sco_utils import * +from notesdb import DateISOtoDMY +import sco_utils as scu +from sco_exceptions import ScoValueError +from sco_permissions import ScoAbsChange from notes_log import log import sco_groups import sco_find_etud @@ -58,7 +62,7 @@ def doSignaleAbsence( etudid = etud["etudid"] description_abs = description - dates = context.DateRangeISO(datedebut, datefin) + dates = sco_abs.DateRangeISO(context, datedebut, datefin) nbadded = 0 for jour in dates: if demijournee == "2": @@ -248,7 +252,7 @@ def doJustifAbsence( etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etudid = etud["etudid"] description_abs = description - dates = context.DateRangeISO(datedebut, datefin) + dates = sco_abs.DateRangeISO(context, datedebut, datefin) nbadded = 0 for jour in dates: if demijournee == "2": @@ -371,7 +375,7 @@ def doAnnuleAbsence( etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etudid = etud["etudid"] - dates = context.DateRangeISO(datedebut, datefin) + dates = sco_abs.DateRangeISO(context, datedebut, datefin) nbadded = 0 for jour in dates: if demijournee == "2": @@ -506,7 +510,7 @@ def doAnnuleJustif( """Annulation d'une justification""" etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etudid = etud["etudid"] - dates = context.DateRangeISO(datedebut0, datefin0) + dates = sco_abs.DateRangeISO(context, datedebut0, datefin0) nbadded = 0 for jour in dates: # Attention: supprime matin et après-midi @@ -571,7 +575,7 @@ def EtatAbsences(context, REQUEST=None): """ - % (AnneeScolaire(REQUEST), datetime.datetime.now().strftime("%d/%m/%Y")), + % (scu.AnneeScolaire(REQUEST), datetime.datetime.now().strftime("%d/%m/%Y")), context.sco_footer(REQUEST), ] return "\n".join(H) @@ -610,7 +614,7 @@ def CalAbs(context, REQUEST=None): # etud implied # crude portage from 1999 DTML etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0] etudid = etud["etudid"] - anneescolaire = int(AnneeScolaire(REQUEST)) + anneescolaire = int(scu.AnneeScolaire(REQUEST)) datedebut = str(anneescolaire) + "-08-31" datefin = str(anneescolaire + 1) + "-07-31" nbabs = context.CountAbs(etudid=etudid, debut=datedebut, fin=datefin) @@ -693,7 +697,7 @@ def ListeAbsEtud( En format 'text': texte avec liste d'absences (pour mails). """ absjust_only = int(absjust_only) # si vrai, table absjust seule (export xls ou pdf) - datedebut = "%s-08-31" % AnneeScolaire(REQUEST) + datedebut = "%s-08-31" % scu.AnneeScolaire(REQUEST) etud = context.getEtudInfo(etudid=etudid, filled=True)[0] @@ -714,7 +718,7 @@ def ListeAbsEtud( html_class="table_leftalign", table_id="tab_absnonjust", base_url=base_url_nj, - filename="abs_" + make_filename(etud["nomprenom"]), + filename="abs_" + scu.make_filename(etud["nomprenom"]), caption="Absences non justifiées de %(nomprenom)s" % etud, preferences=context.get_preferences(), ) @@ -725,7 +729,7 @@ def ListeAbsEtud( html_class="table_leftalign", table_id="tab_absjust", base_url=base_url_j, - filename="absjust_" + make_filename(etud["nomprenom"]), + filename="absjust_" + scu.make_filename(etud["nomprenom"]), caption="Absences justifiées de %(nomprenom)s" % etud, preferences=context.get_preferences(), ) @@ -830,7 +834,7 @@ def absences_index_html(context, REQUEST=None): % REQUEST.URL0, formChoixSemestreGroupe(context), "

    ", - context.CalSelectWeek(REQUEST=REQUEST), + cal_select_week(context, REQUEST=REQUEST), """

    Sélectionner le groupe d'étudiants, puis cliquez sur une semaine pour saisir les absences de toute cette semaine.

    """, @@ -843,3 +847,16 @@ saisir les absences de toute cette semaine.

    H.append(context.sco_footer(REQUEST)) return "\n".join(H) + + +def cal_select_week(context, year=None, REQUEST=None): + "display calendar allowing week selection" + if not year: + year = scu.AnneeScolaire(REQUEST) + sems = sco_formsemestre.do_formsemestre_list(context) + if not sems: + js = "" + else: + js = 'onmouseover="highlightweek(this);" onmouseout="deselectweeks();" onclick="wclick(this);"' + C = sco_abs.YearTable(context, int(year), dayattributes=js) + return C diff --git a/sco_entreprises.py b/sco_entreprises.py new file mode 100644 index 0000000000..fdbef5e55b --- /dev/null +++ b/sco_entreprises.py @@ -0,0 +1,326 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2021 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 +# +############################################################################## + +"""Fonctions sur les entreprises +""" +# codes anciens déplacés de ZEntreprise +import datetime + +import sco_utils as scu +from notesdb import ScoDocCursor, EditableTable, DateISOtoDMY, DateDMYtoISO + + +def _format_nom(nom): + "formatte nom (filtre en entree db) d'une entreprise" + if not nom: + return nom + nom = nom.decode(scu.SCO_ENCODING) + return (nom[0].upper() + nom[1:]).encode(scu.SCO_ENCODING) + + +class EntreprisesEditor(EditableTable): + def delete(self, cnx, oid): + "delete correspondants and contacts, then self" + # first, delete all correspondants and contacts + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "delete from entreprise_contact where entreprise_id=%(entreprise_id)s", + {"entreprise_id": oid}, + ) + cursor.execute( + "delete from entreprise_correspondant where entreprise_id=%(entreprise_id)s", + {"entreprise_id": oid}, + ) + cnx.commit() + EditableTable.delete(self, cnx, oid) + + def list( + self, + cnx, + args={}, + operator="and", + test="=", + sortkey=None, + sort_on_contact=False, + context=None, + limit="", + offset="", + ): + # list, then sort on date of last contact + R = EditableTable.list( + self, + cnx, + args=args, + operator=operator, + test=test, + sortkey=sortkey, + limit=limit, + offset=offset, + ) + if sort_on_contact: + for r in R: + c = do_entreprise_contact_list( + context, + args={"entreprise_id": r["entreprise_id"]}, + disable_formatting=True, + ) + if c: + r["date"] = max([x["date"] or datetime.date.min for x in c]) + else: + r["date"] = datetime.date.min + # sort + R.sort(lambda r1, r2: cmp(r2["date"], r1["date"])) + for r in R: + r["date"] = DateISOtoDMY(r["date"]) + return R + + def list_by_etud( + self, cnx, args={}, sort_on_contact=False, disable_formatting=False + ): + "cherche rentreprise ayant eu contact avec etudiant" + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select E.*, I.nom as etud_nom, I.prenom as etud_prenom, C.date from entreprises E, entreprise_contact C, identite I where C.entreprise_id = E.entreprise_id and C.etudid = I.etudid and I.nom ~* %(etud_nom)s ORDER BY E.nom", + args, + ) + _, res = [x[0] for x in cursor.description], cursor.dictfetchall() + R = [] + for r in res: + r["etud_prenom"] = r["etud_prenom"] or "" + d = {} + for key in r: + v = r[key] + # format value + if not disable_formatting and self.output_formators.has_key(key): + v = self.output_formators[key](v) + d[key] = v + R.append(d) + # sort + if sort_on_contact: + R.sort( + lambda r1, r2: cmp( + r2["date"] or datetime.date.min, r1["date"] or datetime.date.min + ) + ) + for r in R: + r["date"] = DateISOtoDMY(r["date"] or datetime.date.min) + return R + + +_entreprisesEditor = EntreprisesEditor( + "entreprises", + "entreprise_id", + ( + "entreprise_id", + "nom", + "adresse", + "ville", + "codepostal", + "pays", + "contact_origine", + "secteur", + "privee", + "localisation", + "qualite_relation", + "plus10salaries", + "note", + "date_creation", + ), + sortkey="nom", + input_formators={"nom": _format_nom}, +) + +# ----------- Correspondants +_entreprise_correspEditor = EditableTable( + "entreprise_correspondant", + "entreprise_corresp_id", + ( + "entreprise_corresp_id", + "entreprise_id", + "civilite", + "nom", + "prenom", + "fonction", + "phone1", + "phone2", + "mobile", + "fax", + "mail1", + "mail2", + "note", + ), + sortkey="nom", +) + + +# ----------- Contacts +_entreprise_contactEditor = EditableTable( + "entreprise_contact", + "entreprise_contact_id", + ( + "entreprise_contact_id", + "date", + "type_contact", + "entreprise_id", + "entreprise_corresp_id", + "etudid", + "description", + "enseignant", + ), + sortkey="date", + output_formators={"date": DateISOtoDMY}, + input_formators={"date": DateDMYtoISO}, +) + + +def do_entreprise_create(context, args): + "entreprise_create" + cnx = context.GetDBConnexion() + r = _entreprisesEditor.create(cnx, args) + return r + + +def do_entreprise_delete(context, oid): + "entreprise_delete" + cnx = context.GetDBConnexion() + _entreprisesEditor.delete(cnx, oid) + + +def do_entreprise_list(context, **kw): + "entreprise_list" + cnx = context.GetDBConnexion() + kw["context"] = context + return _entreprisesEditor.list(cnx, **kw) + + +def do_entreprise_list_by_etud(context, **kw): + "entreprise_list_by_etud" + cnx = context.GetDBConnexion() + return _entreprisesEditor.list_by_etud(cnx, **kw) + + +def do_entreprise_edit(context, *args, **kw): + "entreprise_edit" + cnx = context.GetDBConnexion() + _entreprisesEditor.edit(cnx, *args, **kw) + + +def do_entreprise_correspondant_create(context, args): + "entreprise_correspondant_create" + cnx = context.GetDBConnexion() + r = _entreprise_correspEditor.create(cnx, args) + return r + + +def do_entreprise_correspondant_delete(context, oid): + "entreprise_correspondant_delete" + cnx = context.GetDBConnexion() + _entreprise_correspEditor.delete(cnx, oid) + + +def do_entreprise_correspondant_list(context, **kw): + "entreprise_correspondant_list" + cnx = context.GetDBConnexion() + return _entreprise_correspEditor.list(cnx, **kw) + + +def do_entreprise_correspondant_edit(context, *args, **kw): + "entreprise_correspondant_edit" + cnx = context.GetDBConnexion() + _entreprise_correspEditor.edit(cnx, *args, **kw) + + +def do_entreprise_correspondant_listnames(context, args={}): + "-> liste des noms des correspondants (pour affichage menu)" + C = do_entreprise_correspondant_list(context, args=args) + return [(x["prenom"] + " " + x["nom"], str(x["entreprise_corresp_id"])) for x in C] + + +def do_entreprise_contact_delete(context, oid): + "entreprise_contact_delete" + cnx = context.GetDBConnexion() + _entreprise_contactEditor.delete(cnx, oid) + + +def do_entreprise_contact_list(context, **kw): + "entreprise_contact_list" + cnx = context.GetDBConnexion() + return _entreprise_contactEditor.list(cnx, **kw) + + +def do_entreprise_contact_edit(context, *args, **kw): + "entreprise_contact_edit" + cnx = context.GetDBConnexion() + _entreprise_contactEditor.edit(cnx, *args, **kw) + + +def do_entreprise_contact_create(context, args): + "entreprise_contact_create" + cnx = context.GetDBConnexion() + r = _entreprise_contactEditor.create(cnx, args) + return r + + +def do_entreprise_check_etudiant(context, etudiant): + """Si etudiant est vide, ou un ETUDID valide, ou un nom unique, + retourne (1, ETUDID). + Sinon, retourne (0, 'message explicatif') + """ + etudiant = etudiant.strip().translate( + None, "'()" + ) # suppress parens and quote from name + if not etudiant: + return 1, None + cnx = context.GetDBConnexion() + cursor = cnx.cursor(cursor_factory=ScoDocCursor) + cursor.execute( + "select etudid, nom, prenom from identite where upper(nom) ~ upper(%(etudiant)s) or etudid=%(etudiant)s", + {"etudiant": etudiant}, + ) + r = cursor.fetchall() + if len(r) < 1: + return 0, 'Aucun etudiant ne correspond à "%s"' % etudiant + elif len(r) > 10: + return ( + 0, + "%d etudiants correspondent à ce nom (utilisez le code)" % len(r), + ) + elif len(r) > 1: + e = ['") + return ( + 0, + "Les étudiants suivants correspondent: préciser le nom complet ou le code\n" + + "\n".join(e), + ) + else: # une seule reponse ! + return 1, r[0][0].strip() \ No newline at end of file diff --git a/sco_groups_view.py b/sco_groups_view.py index 152ceb98ee..fbc6222053 100644 --- a/sco_groups_view.py +++ b/sco_groups_view.py @@ -30,8 +30,16 @@ """ # Re-ecriture en 2014 (re-organisation de l'interface, modernisation du code) +import datetime +import cgi +import urllib +import time +import collections +import operator -from sco_utils import * +import sco_utils as scu +from sco_permissions import ScoEtudInscrit, ScoEtudAddAnnotations, ScoAbsChange +from sco_exceptions import ScoValueError import html_sco_header from gen_tables import GenTable import scolars @@ -441,8 +449,6 @@ def groups_table( # "enter groups_table %s: %s" # % (groups_infos.members[0]["nom"], groups_infos.members[0].get("etape", "-")) # ) - authuser = REQUEST.AUTHENTICATED_USER - with_codes = int(with_codes) with_paiement = int(with_paiement) with_archives = int(with_archives) @@ -891,7 +897,7 @@ def form_choix_jour_saisie_hebdo( FA.append("""