diff --git a/appli/__init__.py b/appli/__init__.py index f37eea31..88d802f7 100644 --- a/appli/__init__.py +++ b/appli/__init__.py @@ -8,7 +8,7 @@ virtualprefix = sys.base_prefix if hasattr(sys, 'real_prefix'): sys.base_prefix = sys.real_prefix - if float(sys.winver) < 3.5: + if float(sys.winver.replace('-32','')) < 3.5: from tkinter import _fix if "TCL_LIBRARY" not in os.environ: # reload module, so that sys.real_prefix be used @@ -307,7 +307,7 @@ def JinjaGetManagerList(sujet=""): sujet="?"+urllib.parse.urlencode({"subject":sujet}).replace('+','%20') return " ".join(["
  • {2} ({1})
  • ".format(sujet,*r) for r in LstUsers ]) -ecotaxa_version="2.0.0" +ecotaxa_version="2.0.1" def JinjaGetEcotaxaVersionText(): return ecotaxa_version+" 2019-01-25" @@ -317,6 +317,8 @@ def JinjaGetEcotaxaVersionText(): """Changelog +2019.02.01 : V 2.0.1 + Fix implementation minor bug 2019.01.25 : V 2.0.0 Integration with EcotaxoServer Handling new display_name child', methods=['GET', 'POST']) -@login_required -def PrjManualClassif(PrjId): - request.form # Force la lecture des données POST sinon il y a une erreur 504 - Changement=[] - Prj=database.Projects.query.filter_by(projid=PrjId).first() - if not Prj.CheckRight(1): # Level 0 = Read, 1 = Annotate, 2 = Admin - return 'You cannot Annotate this project' - - changes={k[8:-1]:v for k,v in request.form.items() if k[0:7]=="changes"} - if len(changes)==0: - return 'No pending change to update' - - sql="""select o.objid,o.classif_auto_id,o.classif_auto_when,o.classif_auto_score,o.classif_id,o.classif_qual,o.classif_when,o.classif_who - from obj_head o - where o.objid in ("""+",".join(changes.keys())+")" - prev={r['objid']:r for r in GetAll(sql,debug=False)} - sql="update obj_head set classif_id=%(classif_id)s,classif_qual=%(classif_qual)s,classif_who=%(classif_who)s,classif_when=now() where objid=%(objid)s " - # sqli="""INSERT INTO objectsclassifhisto (objid, classif_date, classif_type, classif_id, classif_qual, classif_who) - # VALUES (%(objid)s,%(classif_when)s,'M',%(classif_id)s,%(classif_qual)s,%(classif_who)s )""" - # Traitement global de l'historisation afin de réduire les commandes SQL + test qu'il n'y a pas de doublons - sqli="""INSERT INTO objectsclassifhisto(objid, classif_date, classif_type, classif_id, classif_qual, classif_who, classif_score) - SELECT objid, classif_when, 'M' classif_type, classif_id, classif_qual, classif_who, null classif_score - from obj_head oh - where objid= any(%s) - and classif_when is not null - and not exists(select 1 from objectsclassifhisto och where oh.objid=och.objid and oh.classif_when=och.classif_date )""" - try: - params=[[int(x) for x in changes.keys()] ] - ExecSQL(sqli, params, True) - except: - app.logger.warning("Unable to add historical information, non-blocking %s" % (sys.exc_info(),)) - BatchParam=[] - for k,v in changes.items(): - ki=int(k) - if v=="-1" or v=="" : # utilisé dans validate all - v=prev[ki]['classif_id'] - if prev[ki]['classif_qual']!=gvp('qual') or prev[ki]['classif_who']!=current_user.id or prev[ki]['classif_id']!=int(v): - # il y a eu au moins un changement - params={'objid':k,'classif_id':v,'classif_who':current_user.id,'classif_qual':gvp('qual')} - BatchParam.append(params) - # ExecSQL(sql,params,False) - Changement.append({'prev_id':prev[ki]['classif_id'],'prev_qual':prev[ki]['classif_qual'] - ,'id':int(v),'qual':gvp('qual')}) - # params={'objid':k,'classif_id':prev[ki]['classif_id'],'classif_who':prev[ki]['classif_who'] - # ,'classif_qual':prev[ki]['classif_qual'],'classif_when':prev[ki]['classif_when']} - # try: - # if ntcv(params['classif_when']) !="" : # si pas de date, violation PK - # ExecSQL(sqli,params,True) - # except: - # app.logger.warning("Unable to add historical information, non-blocking %s"%(prev,)) - if prev[ki]['classif_id']!=int(v): # il y a eu un changement de classif on maintient la liste des classifs MRU - with app.MRUClassif_lock: - tbl=app.MRUClassif.get(current_user.id,[]) - for i,t in enumerate(tbl): - if t["id"]==int(v): - if i>0: # on met cet item au début pour gérer un MRU - tbl=[t]+tbl[0:i]+tbl[i+1:] - break - else: # si pas trouvé dans la liste des MRU on l'ajoute au début si on trouve bien son nom dans la taxo - Taxon=GetAll("""select tf.name||case when p1.name is not null and tf.name not like '%% %%' then ' ('||p1.name||')' else ' ' end as name - from taxonomy tf - left join taxonomy p1 on tf.parent_id=p1.id - where tf.id=%(id)s """,{"id":v}) - if len(Taxon)==1: - Taxon=Taxon[0].get('name', "") - tbl.insert(0,{"id": int(v), "pr": 0, "text": Taxon}) - if len(tbl)>10: - tbl=tbl[0:10] - app.MRUClassif[current_user.id]=tbl - if len(BatchParam)>0: - upcur = db.engine.raw_connection().cursor() - try: - upcur.executemany(sql, BatchParam) - upcur.connection.commit() - except: - upcur.close() - app.logger.warning("Unable to save changes %s" % (sys.exc_info(),)) - return 'Unable to save changes' - upcur.close() - - app.logger.info("Changement = %s",Changement) - # applique les changements dans projects_taxo_stat - Empty = {'n': 0, 'V': 0, 'P': 0, 'D': 0} - Changes = {} - for c in Changement: - if c['prev_id'] is None: c['prev_id'] = -1 - if c['prev_id'] not in Changes: Changes[c['prev_id']] = Empty.copy() - if c['id'] is None: c['id'] = -1 - if c['id'] not in Changes: Changes[c['id']] = Empty.copy() - Changes[c['prev_id']]['n'] -= 1 - Changes[c['id']]['n'] += 1 - if c['prev_qual'] in ('V', 'P', 'D'): - Changes[c['prev_id']][c['prev_qual']] -= 1 - if c['qual'] in ('V', 'P', 'D'): - Changes[c['id']][c['qual']] += 1 - LstIdInDB=[x[0] for x in database.GetAll("select id from projects_taxo_stat where projid=%s",[PrjId])] - for k,c in Changes.items(): - if k not in LstIdInDB: - database.ExecSQL("insert into projects_taxo_stat(projid, id, nbr, nbr_v, nbr_d, nbr_p) values (%s,%s,0,0,0,0)",[PrjId,k]) - sqlparam={'projid':PrjId,'id':k,'n':c['n'],'v':c['V'],'d':c['D'],'p':c['P']} - database.ExecSQL("""update projects_taxo_stat set - nbr=nbr+%(n)s, nbr_v=nbr_v+%(v)s, nbr_d=nbr_d+%(d)s, nbr_p=nbr_p+%(p)s - where projid=%(projid)s and id=%(id)s""",sqlparam) - - return 'Database update Successfull' - - -@app.route('/prj/UpdateComment/', methods=['GET', 'POST']) -@login_required -def PrjUpdateComment(ObjId): - Obj=database.Objects.query.filter_by(objid=ObjId).first() - if Obj is None: - return "Object doesnt exists" - Prj=database.Projects.query.filter_by(projid=Obj.projid).first() - if not Prj.CheckRight(1): # Level 0 = Read, 1 = Annotate, 2 = Admin - return "You cannot Annotate this project" - - Obj.complement_info=gvp('comment') - db.session.commit() - - return 'Database update Successfull' - +from flask import Blueprint, render_template, g, flash,request,url_for,json +from flask_login import current_user +from appli import app,ObjectToStr,PrintInCharte,database,gvg,gvp,user_datastore,DecodeEqualList,ScaleForDisplay,ntcv +from pathlib import Path +from flask_security import Security, SQLAlchemyUserDatastore +from flask_security import login_required +from flask_security.decorators import roles_accepted +import os,time,math,collections,psycopg2.extras,sys +from appli.database import GetAll,GetClassifQualClass,db,ExecSQL + +@app.route('/prj/ManualClassif/', methods=['GET', 'POST']) +@login_required +def PrjManualClassif(PrjId): + request.form # Force la lecture des données POST sinon il y a une erreur 504 + Changement=[] + Prj=database.Projects.query.filter_by(projid=PrjId).first() + if not Prj.CheckRight(1): # Level 0 = Read, 1 = Annotate, 2 = Admin + return 'You cannot Annotate this project' + + changes={k[8:-1]:v for k,v in request.form.items() if k[0:7]=="changes"} + if len(changes)==0: + return 'No pending change to update' + + sql="""select o.objid,o.classif_auto_id,o.classif_auto_when,o.classif_auto_score,o.classif_id,o.classif_qual,o.classif_when,o.classif_who + from obj_head o + where o.objid in ("""+",".join(changes.keys())+")" + prev={r['objid']:r for r in GetAll(sql,debug=False)} + sql="update obj_head set classif_id=%(classif_id)s,classif_qual=%(classif_qual)s,classif_who=%(classif_who)s,classif_when=now() where objid=%(objid)s " + # sqli="""INSERT INTO objectsclassifhisto (objid, classif_date, classif_type, classif_id, classif_qual, classif_who) + # VALUES (%(objid)s,%(classif_when)s,'M',%(classif_id)s,%(classif_qual)s,%(classif_who)s )""" + # Traitement global de l'historisation afin de réduire les commandes SQL + test qu'il n'y a pas de doublons + sqli="""INSERT INTO objectsclassifhisto(objid, classif_date, classif_type, classif_id, classif_qual, classif_who, classif_score) + SELECT objid, classif_when, 'M' classif_type, classif_id, classif_qual, classif_who, null classif_score + from obj_head oh + where objid= any(%s) + and classif_when is not null + and not exists(select 1 from objectsclassifhisto och where oh.objid=och.objid and oh.classif_when=och.classif_date )""" + try: + params=[[int(x) for x in changes.keys()] ] + ExecSQL(sqli, params, True) + except: + app.logger.warning("Unable to add historical information, non-blocking %s" % (sys.exc_info(),)) + BatchParam=[] + for k,v in changes.items(): + ki=int(k) + if v=="-1" or v=="" : # utilisé dans validate all + v=prev[ki]['classif_id'] + if prev[ki]['classif_qual']!=gvp('qual') or prev[ki]['classif_who']!=current_user.id or prev[ki]['classif_id']!=int(v): + # il y a eu au moins un changement + params={'objid':k,'classif_id':v,'classif_who':current_user.id,'classif_qual':gvp('qual')} + BatchParam.append(params) + # ExecSQL(sql,params,False) + Changement.append({'prev_id':prev[ki]['classif_id'],'prev_qual':prev[ki]['classif_qual'] + ,'id':int(v),'qual':gvp('qual')}) + # params={'objid':k,'classif_id':prev[ki]['classif_id'],'classif_who':prev[ki]['classif_who'] + # ,'classif_qual':prev[ki]['classif_qual'],'classif_when':prev[ki]['classif_when']} + # try: + # if ntcv(params['classif_when']) !="" : # si pas de date, violation PK + # ExecSQL(sqli,params,True) + # except: + # app.logger.warning("Unable to add historical information, non-blocking %s"%(prev,)) + if prev[ki]['classif_id']!=int(v): # il y a eu un changement de classif on maintient la liste des classifs MRU + with app.MRUClassif_lock: + tbl=app.MRUClassif.get(current_user.id,[]) + for i,t in enumerate(tbl): + if t["id"]==int(v): + if i>0: # on met cet item au début pour gérer un MRU + tbl=[t]+tbl[0:i]+tbl[i+1:] + break + else: # si pas trouvé dans la liste des MRU on l'ajoute au début si on trouve bien son nom dans la taxo + Taxon=GetAll("""select tf.display_name as name + from taxonomy tf + left join taxonomy p1 on tf.parent_id=p1.id + where tf.id=%(id)s """,{"id":v}) + if len(Taxon)==1: + Taxon=Taxon[0].get('name', "") + tbl.insert(0,{"id": int(v), "pr": 0, "text": Taxon}) + if len(tbl)>10: + tbl=tbl[0:10] + app.MRUClassif[current_user.id]=tbl + if len(BatchParam)>0: + upcur = db.engine.raw_connection().cursor() + try: + upcur.executemany(sql, BatchParam) + upcur.connection.commit() + except: + upcur.close() + app.logger.warning("Unable to save changes %s" % (sys.exc_info(),)) + return 'Unable to save changes' + upcur.close() + + app.logger.info("Changement = %s",Changement) + # applique les changements dans projects_taxo_stat + Empty = {'n': 0, 'V': 0, 'P': 0, 'D': 0} + Changes = {} + for c in Changement: + if c['prev_id'] is None: c['prev_id'] = -1 + if c['prev_id'] not in Changes: Changes[c['prev_id']] = Empty.copy() + if c['id'] is None: c['id'] = -1 + if c['id'] not in Changes: Changes[c['id']] = Empty.copy() + Changes[c['prev_id']]['n'] -= 1 + Changes[c['id']]['n'] += 1 + if c['prev_qual'] in ('V', 'P', 'D'): + Changes[c['prev_id']][c['prev_qual']] -= 1 + if c['qual'] in ('V', 'P', 'D'): + Changes[c['id']][c['qual']] += 1 + LstIdInDB=[x[0] for x in database.GetAll("select id from projects_taxo_stat where projid=%s",[PrjId])] + for k,c in Changes.items(): + if k not in LstIdInDB: + database.ExecSQL("insert into projects_taxo_stat(projid, id, nbr, nbr_v, nbr_d, nbr_p) values (%s,%s,0,0,0,0)",[PrjId,k]) + sqlparam={'projid':PrjId,'id':k,'n':c['n'],'v':c['V'],'d':c['D'],'p':c['P']} + database.ExecSQL("""update projects_taxo_stat set + nbr=nbr+%(n)s, nbr_v=nbr_v+%(v)s, nbr_d=nbr_d+%(d)s, nbr_p=nbr_p+%(p)s + where projid=%(projid)s and id=%(id)s""",sqlparam) + + return 'Database update Successfull' + + +@app.route('/prj/UpdateComment/', methods=['GET', 'POST']) +@login_required +def PrjUpdateComment(ObjId): + Obj=database.Objects.query.filter_by(objid=ObjId).first() + if Obj is None: + return "Object doesnt exists" + Prj=database.Projects.query.filter_by(projid=Obj.projid).first() + if not Prj.CheckRight(1): # Level 0 = Read, 1 = Annotate, 2 = Admin + return "You cannot Annotate this project" + + Obj.complement_info=gvp('comment') + db.session.commit() + + return 'Database update Successfull' + diff --git a/appli/project/main.py b/appli/project/main.py index 92d26a2c..b1ca90cb 100644 --- a/appli/project/main.py +++ b/appli/project/main.py @@ -64,7 +64,7 @@ def indexProjects(Others=False): if current_user.has_role(database.AdministratorLabel) or current_user.has_role(database.ProjectCreatorLabel): CanCreate=True PDT=database.PersistantDataTable.query.first() - if (datetime.datetime.now()-PDT.lastserverversioncheck_datetime).days>7 : + if PDT is None or PDT.lastserverversioncheck_datetime is None or (datetime.datetime.now()-PDT.lastserverversioncheck_datetime).days>7 : fashtxt="Taxonomy synchronization and Ecotaxa version check wasn’t done during the last 7 days, Ask application administrator to do it." #+str(PDT.lastserverversioncheck_datetime) fashtxt+=" Synchronize to check Ecotaxa version" flash(Markup(fashtxt),'warning') diff --git a/appli/static/ecotaxa.css b/appli/static/ecotaxa.css deleted file mode 100644 index 29686a93..00000000 --- a/appli/static/ecotaxa.css +++ /dev/null @@ -1,58 +0,0 @@ -input.parsley-success, -select.parsley-success, -textarea.parsley-success { - color: #468847; - background-color: #DFF0D8; - border: 1px solid #D6E9C6; -} - -input.parsley-error, -select.parsley-error, -textarea.parsley-error { - color: #B94A48; - background-color: #F2DEDE; - border: 1px solid #EED3D7; -} - -.parsley-errors-list { - margin: 2px 0 3px; - padding: 0; - list-style-type: none; - font-size: 0.9em; - line-height: 0.9em; - opacity: 0; - color: red; - transition: all .3s ease-in; - -o-transition: all .3s ease-in; - -moz-transition: all .3s ease-in; - -webkit-transition: all .3s ease-in; -} - -.parsley-errors-list.filled { - opacity: 1; -} - -/**** Gestion Popup ******/ -.At2PopupMask { - position:absolute; top : 0; left: 0; width: 100%; height: 100%; - background-color:#000; opacity: 0.7; - display:none; -} -.At2PopupWindowContainer { - position:absolute; top : 0; left: 0; width: 100%; height: 100%; - display:none; -} -.At2PopupWindow { - position:relative; - background-color:white; border-radius: 6px; - border: 1px solid rgba(0,0,0,.5); - -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); - box-shadow: 0 3px 9px rgba(0,0,0,.5); - margin: 20px auto; min-width: 300px; width: 700px; -} -.At2PopupHeader {border-top-left-radius: 6px;border-top-right-radius: 6px; padding:6px 10px ; - font-weight: bold; - border-bottom: 1px #eee solid;} -.At2PopupContent { padding:10px; } -.ui-datepicker { z-index: 10000 !important; } /*Requis pour fonctionnement dans une popup*/ -.ui-front { z-index: 10000 !important; } /*Requis pour fonctionnement dans une popup de autocomplete*/ diff --git a/appli/static/style20181025.css b/appli/static/style20190201.css similarity index 82% rename from appli/static/style20181025.css rename to appli/static/style20190201.css index 925a5f9f..00bba95f 100644 --- a/appli/static/style20181025.css +++ b/appli/static/style20190201.css @@ -276,3 +276,66 @@ fieldset[disabled] .btn-gris:hover, .btn-gris.disabled:focus,.btn-gris[disabled] background-color: #89888A; border-color: #5E5E61; } + + + +input.parsley-success, +select.parsley-success, +textarea.parsley-success { + color: #468847; + background-color: #DFF0D8; + border: 1px solid #D6E9C6; +} + +input.parsley-error, +select.parsley-error, +textarea.parsley-error { + color: #B94A48; + background-color: #F2DEDE; + border: 1px solid #EED3D7; +} + +.parsley-errors-list { + margin: 2px 0 3px; + padding: 0; + list-style-type: none; + font-size: 0.9em; + line-height: 0.9em; + opacity: 0; + color: red; + transition: all .3s ease-in; + -o-transition: all .3s ease-in; + -moz-transition: all .3s ease-in; + -webkit-transition: all .3s ease-in; +} + +.parsley-errors-list.filled { + opacity: 1; +} + +/**** Gestion Popup ******/ +.At2PopupMask { + position:absolute; top : 0; left: 0; width: 100%; height: 100%; + background-color:#000; opacity: 0.7; + display:none; +} +.At2PopupWindowContainer { + position:absolute; top : 0; left: 0; width: 100%; height: 100%; + display:none; +} +.At2PopupWindow { + position:relative; + background-color:white; border-radius: 6px; + border: 1px solid rgba(0,0,0,.5); + -webkit-box-shadow: 0 3px 9px rgba(0,0,0,.5); + box-shadow: 0 3px 9px rgba(0,0,0,.5); + margin: 20px auto; min-width: 300px; width: 700px; +} +.At2PopupHeader {border-top-left-radius: 6px;border-top-right-radius: 6px; padding:6px 10px ; + font-weight: bold; + border-bottom: 1px #eee solid;} +.At2PopupContent { padding:10px; } +.ui-datepicker { z-index: 10000 !important; } /*Requis pour fonctionnement dans une popup*/ +.ui-front { z-index: 10000 !important; } /*Requis pour fonctionnement dans une popup de autocomplete*/ + + diff --git a/appli/taxonomy/taxomain.py b/appli/taxonomy/taxomain.py index ee34d967..3e823ecc 100644 --- a/appli/taxonomy/taxomain.py +++ b/appli/taxonomy/taxomain.py @@ -89,6 +89,10 @@ def DoSyncStatUpdate(): flash(j['msgversion'],'warning') if 'msg' in j: PDT = database.PersistantDataTable.query.first() + if PDT is None: # si record manquant + PDT = database.PersistantDataTable() + PDT.id=1 + db.session.add(PDT) PDT.lastserverversioncheck_datetime = datetime.datetime.now() db.session.commit() return j['msg'] diff --git a/appli/templates/common/taxopopup.html b/appli/templates/common/taxopopup.html index 14124b0d..c3be4bed 100644 --- a/appli/templates/common/taxopopup.html +++ b/appli/templates/common/taxopopup.html @@ -6,9 +6,10 @@ else return t.text; } function EnableSelect2Taxolb() { - if(typeof(Restoretaxolb)!=='undefined'){ - Restoretaxolb.appendTo('#taxolbanno'); - Restoretaxolb=undefined; + if((typeof(Restoretaxolbid)!=='undefined')&&(typeof(Restoretaxolbid)!=='')){ + var NewOption=$('