def PrjEditPriv(PrjId): Prj=database.Projects.query.filter_by(projid=PrjId).first() g.headcenter="<h4><a href='/prj/{0}'>{1}</a></h4>".format(Prj.projid,XSSEscape(Prj.title)) if Prj is None: flash("Project doesn't exists",'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not Prj.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot edit settings for this project','error') return PrintInCharte("<a href=/prj/>Select another project</a>") if gvp('save')=="Y": # print(request.form) for m in Prj.projmembers: if gvp('priv_%s_delete'%m.id)=='Y': db.session.delete(m) elif gvp('priv_%s_member'%m.id)!='': # si pas delete c'est update m.member=int(gvp('priv_%s_member'%m.id)) m.privilege=gvp('priv_%s_privilege'%m.id) if gvp('priv_new_member')!='': new=database.ProjectsPriv(member=int(gvp('priv_new_member')),privilege=gvp('priv_new_privilege'),projid=PrjId) db.session.add(new) try: db.session.commit() flash("Project settings Saved successfuly","success") except Exception as E: flash("Database exception : %s"%E,"error") db.session.rollback() g.users=GetAssoc2Col("select id,name from users order by lower(name)",dicttype=collections.OrderedDict) return render_template('project/editprojectpriv.html',data=Prj)
def PrjEdit(PrjId): g.useselect4 = True Prj = database.Projects.query.filter_by(projid=PrjId).first() g.headcenter = "<h4><a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) if Prj is None: flash("Project doesn't exists", 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not Prj.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot edit settings for this project', 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if gvp('save') == "Y": PreviousCNN = Prj.cnn_network_id for f in request.form: if f in dir(Prj): setattr(Prj, f, gvp(f)) if PreviousCNN != Prj.cnn_network_id: database.ExecSQL( "delete from obj_cnn_features where objcnnid in (select objid from obj_head where projid=%s)", [PrjId]) flash("SCN features erased", "success") Prj.visible = True if gvp('visible') == 'Y' else False # print(request.form) for m in Prj.projmembers: if gvp('priv_%s_delete' % m.id) == 'Y': db.session.delete(m) elif gvp('priv_%s_member' % m.id) != '': # si pas delete c'est update m.member = int(gvp('priv_%s_member' % m.id)) m.privilege = gvp('priv_%s_privilege' % m.id) if gvp('priv_new_member') != '': new = database.ProjectsPriv(member=int(gvp('priv_new_member')), privilege=gvp('priv_new_privilege'), projid=PrjId) db.session.add(new) try: db.session.commit() flash("Project settings Saved successfuly", "success") except Exception as E: flash("Database exception : %s" % E, "error") db.session.rollback() if Prj.initclassiflist is None: lst = [] else: lst = [int(x) for x in Prj.initclassiflist.split(",") if x.isdigit()] g.predeftaxo = GetAll( """select t.id,t.display_name as name from taxonomy t left join taxonomy t2 on t.parent_id=t2.id where t.id= any(%s) order by upper(t.display_name) """, (lst, )) g.users = GetAssoc2Col("select id,name from users order by lower(name)", dicttype=collections.OrderedDict) g.maplist = [ 'objtime', 'objdate', 'latitude', 'longitude', 'depth_min', 'depth_max' ] + sorted(DecodeEqualList(Prj.mappingobj).values()) g.scn = GetSCNNetworks() return render_template('project/editproject.html', data=Prj)
def TaskMonitor(TaskID): AddTaskSummaryForTemplate() try: task = LoadTask(TaskID) ProjectID = getattr(task.param, 'ProjectId', None) if ProjectID: Prj = database.Projects.query.filter_by(projid=ProjectID).first() g.headcenter = "<h4>Project : <a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) return render_template('task/monitor.html', TaskID=task.task.id) except: return PrintInCharte( "This task doesn't exists anymore, peraphs it was automaticaly purged" )
def TaskShow(TaskID): AddTaskSummaryForTemplate() try: task = LoadTask(TaskID) except: return PrintInCharte( "This task doesn't exists anymore, peraphs it was automaticaly purged" ) txt = "" if gvg('log') == "Y": WorkingDir = task.GetWorkingDir() # app.send_static_file(os.path.join(WorkingDir,"TaskLog.txt")) return flask.send_from_directory(WorkingDir, "TaskLog.txt") if gvg('CustomDetails') == "Y": return task.ShowCustomDetails() if "GetResultFile" in dir(task): f = task.GetResultFile() if f is None: txt += "Error, final file not available" else: txt += "<a href='/Task/GetFile/%d/%s' class='btn btn-primary btn-sm ' role='button'>Get file %s</a>" % ( TaskID, f, f) CustomDetailsAvail = "ShowCustomDetails" in dir(task) try: decodedsteperrors = json.loads(task.task.inputparam).get("steperrors") except: decodedsteperrors = ["Task Decoding Error"] ProjectID = getattr(task.param, 'ProjectId', None) if ProjectID: Prj = database.Projects.query.filter_by(projid=ProjectID).first() g.headcenter = "<h4>Project : <a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) return render_template('task/show.html', task=task.task, steperror=decodedsteperrors, CustomDetailsAvail=CustomDetailsAvail, extratext=txt)
def StartTask(self, param=None, step=1, FileToSave=None, FileToSaveFileName=None): if param is not None: self.task.inputparam = json.dumps(param.__dict__) self.task.taskstep = step self.task.taskstate = "Running" if self.task.id is None: db.session.add(self.task) db.session.commit() workingdir = self.GetWorkingDir() if not os.path.exists(workingdir): os.mkdir(workingdir) if FileToSave is not None: FileToSave.save( os.path.join(self.GetWorkingDir(), FileToSaveFileName)) # cmd=os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../runtask.py")) cmdfull = app.PythonExecutable + " " + os.path.normpath( os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../runtask.py " + str(self.task.id))) app.logger.info("Start Task process : %s" % (cmdfull, )) # os.spawnv(os.P_NOWAIT, sys.executable, (sys.executable,cmd, str(self.task.id))) # Ne marche pas # system marche, mais c'est un appel bloquant donc on le met dans une thread separé # avec uwsgi il faut specifier l'option close-on-exec=true dans uwsgi.ini sinon c'est un appel bloquant. import _thread _thread.start_new_thread(os.system, (cmdfull, )) flash("Taks %d subprocess Created " % (self.task.id, ), "success") ProjectID = getattr(self.param, 'ProjectId', None) if ProjectID: Prj = database.Projects.query.filter_by(projid=ProjectID).first() g.headcenter = "<h4>Project : <a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) return render_template('task/monitor.html', TaskID=self.task.id, RedirectToMonitor=True)
def GetAll(sql, params=None, debug=False, cursor_factory=psycopg2.extras.DictCursor, doXSSEscape=False): if g.db is None: g.db = db.engine.raw_connection() if doXSSEscape: cursor_factory = psycopg2.extras.RealDictCursor cur = g.db.cursor(cursor_factory=cursor_factory) try: starttime = datetime.datetime.now() cur.execute(sql, params) res = cur.fetchall() if doXSSEscape: for rid, row in enumerate(res): for k, v in row.items(): if isinstance(v, str): res[rid][k] = XSSEscape(v) except psycopg2.InterfaceError: app.logger.debug( "Connection was invalidated!, Try to reconnect for next HTTP request" ) db.engine.connect() raise except: app.logger.debug("GetAll Exception SQL = %s %s", sql, params) cur.connection.rollback() raise finally: if debug or GlobalDebugSQL: app.logger.debug("GetAll (%s) SQL = %s %s", (datetime.datetime.now() - starttime).total_seconds(), sql, params) cur.close() return res
def PrjMerge(PrjId): Prj = database.Projects.query.filter_by(projid=PrjId).first() if Prj is None: flash("Project doesn't exists", 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not Prj.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot edit settings for this project', 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") g.headcenter = "<h4><a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) txt = "<h3>Project Merge / Fusion </h3>" if not gvg('src'): txt += """<ul><li>You are allowed to merge projects that you are allowed to manage <li>User privileges from both projects will be added <li>This tool allow to merge two projects in a single projet (called Current project). The added project will then be automatically deleted. If object data are not consistent between both projects : <ul><li>New data fields are added to the Current project <li>The resulting project will thus contain partially documented datafields. </ul><li>Note : Next screen will indicate compatibility issues (if exists) and allow you to Confirm the merging operation. </ul> """ sql = "select p.projid,title,status,coalesce(objcount,0) objcount,coalesce(pctvalidated,0) pctvalidated,coalesce(pctclassified,0) pctclassified from projects p" if not current_user.has_role(database.AdministratorLabel): sql += " Join projectspriv pp on p.projid = pp.projid and pp.member=%d" % ( current_user.id, ) sql += " where p.projid!=%d order by title" % Prj.projid res = GetAll(sql, doXSSEscape=True) #,debug=True txt += """<table class='table table-bordered table-hover table-verycondensed'> <tr><th width=120>ID</td><th>Title</td><th width=100>Status</th><th width=100>Nbr Obj</th> <th width=100>% Validated</th><th width=100>% Classified</th></tr>""" for r in res: txt += """<tr><td><a class="btn btn-primary" href='/prj/merge/{activeproject}?src={projid}'>Select</a> {projid}</td> <td>{title}</td> <td>{status}</td> <td>{objcount:0.0f}</td> <td>{pctvalidated:0.2f}</td> <td>{pctclassified:0.2f}</td> </tr>""".format(activeproject=Prj.projid, **r) txt += "</table>" return PrintInCharte(txt) PrjSrc = database.Projects.query.filter_by(projid=int(gvg('src'))).first() if PrjSrc is None: flash("Source project doesn't exists", 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not PrjSrc.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot merge for this project', 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") txt += """<h4>Source Project : {0} - {1} (This project will be destroyed)</h4> """.format(PrjSrc.projid, XSSEscape(PrjSrc.title)) if not gvg('merge'): # Ici la src à été choisie et vérifiée if PrjSrc.mappingobj != Prj.mappingobj: flash("Object mapping differ With source project ", "warning") if PrjSrc.mappingsample != Prj.mappingsample: flash("Sample mapping differ With source project ", "warning") if PrjSrc.mappingacq != Prj.mappingacq: flash("Acquisition mapping differ With source project ", "warning") if PrjSrc.mappingprocess != Prj.mappingprocess: flash("Process mapping differ With source project ", "warning") txt += FormatError( """ <span class='glyphicon glyphicon-warning-sign'></span> Warning project {1} - {2}<br> Will be destroyed, its content will be transfered in the target project.<br> This operation is irreversible</p> <br><a class='btn btn-lg btn-warning' href='/prj/merge/{0}?src={1}&merge=Y'>Start Project Fusion</a> """, Prj.projid, PrjSrc.projid, XSSEscape(PrjSrc.title), DoNotEscape=True) return PrintInCharte(txt) if gvg('merge') == 'Y': ExecSQL("update acquisitions set projid={0} where projid={1}".format( Prj.projid, PrjSrc.projid)) ExecSQL("update process set projid={0} where projid={1}".format( Prj.projid, PrjSrc.projid)) ExecSQL("update samples set projid={0} where projid={1}".format( Prj.projid, PrjSrc.projid)) ExecSQL("update obj_head set projid={0} where projid={1}".format( Prj.projid, PrjSrc.projid)) ExecSQL("update part_projects set projid={0} where projid={1}".format( Prj.projid, PrjSrc.projid)) # garde le privilege le plus elevé des 2 projets ExecSQL("""UPDATE projectspriv ppdst set privilege=case when 'Manage' in (ppsrc.privilege,ppdst.privilege) then 'Manage' when 'Annotate' in (ppsrc.privilege,ppdst.privilege) then 'Annotate' else 'View' end from projectspriv ppsrc where ppsrc.projid={1} and ppdst.projid={0} and ppsrc.member=ppdst.member""" .format(Prj.projid, PrjSrc.projid), debug=True) # Transfere les privilege depuis le projet source ExecSQL("""update projectspriv set projid={0} where projid={1} and member not in (select member from projectspriv where projid={0})""" .format(Prj.projid, PrjSrc.projid)) # Efface ceux qui etait des 2 cotés ExecSQL("delete from projectspriv where projid={0}".format( PrjSrc.projid)) ExecSQL("delete from projects where projid={0}".format(PrjSrc.projid)) appli.project.main.RecalcProjectTaxoStat(Prj.projid) appli.project.main.UpdateProjectStat(Prj.projid) txt += "<div class='alert alert-success' role='alert'>Fusion Done successfully</div>" txt += "<br><a class='btn btn-lg btn-primary' href='/prj/%s'>Back to target project</a>" % Prj.projid return PrintInCharte(txt)
def ExploreLoadRightPane(): # récupération des parametres d'affichage Filt = {} for k, v in FilterList.items(): Filt[k] = gvp(k, v) ipp = int(Filt["ipp"]) zoom = int(Filt["zoom"]) t = ["<a name='toppage'/>"] sqlparam = {'projid': gvp("projid")} # sql="""select o.objid,t.name taxoname,o.classif_qual,u.name classifwhoname,i.file_name # ,i.height,i.width,i.thumb_file_name,i.thumb_height,i.thumb_width # ,o.depth_min,o.depth_max,s.orig_id samplename,o.objdate,to_char(o.objtime,'HH24:MI') objtime # ,o.latitude,o.orig_id,o.imgcount # from objects o # left Join images i on o.img0id=i.imgid # left JOIN taxonomy t on o.classif_id=t.id # LEFT JOIN users u on o.classif_who=u.id # LEFT JOIN samples s on o.sampleid=s.sampleid # where o.classif_qual='V' # """ whereclause = "" sql = """select o.objid,o.classif_qual ,o.objdate,to_char(o.objtime,'HH24:MI') objtime ,o.imgcount,o.img0id,o.classif_id,o.classif_who,o.sampleid,random_value,o.projid from obj_head o where o.classif_qual='V' """ if gvp("taxo[]"): taxoids = ",".join( (str(int(x)) for x in request.form.getlist("taxo[]"))) if gvp("taxochild") == "1": # whereclause+=""" and o.classif_id in (WITH RECURSIVE rq(id) as ( select id FROM taxonomy where id in(%s) # union # SELECT t.id FROM rq JOIN taxonomy t ON rq.id = t.parent_id and (t.nbrobjcum>0 or t.nbrobj>0) # ) select id from rq)"""%(taxoids,) # Sur les petits nombres de ref le in est plus performant que la sous requete taxoids = ",".join((str(int(x[0])) for x in GetAll( """WITH RECURSIVE rq(id) as ( select id FROM taxonomy where id in(%s) union SELECT t.id FROM rq JOIN taxonomy t ON rq.id = t.parent_id ) select id from rq """ % (taxoids, )))) whereclause += " and o.classif_id in (" + taxoids + ")" # optimisation qui provoque de faux résultats : and (t.nbrobjcum>0 or t.nbrobj>0) else: whereclause += " and o.classif_id in (" + taxoids + ")" if gvp("MapN") != "" and gvp("MapW") != "" and gvp("MapE") != "" and gvp( "MapS") != "": whereclause += " and o.latitude between %(MapS)s and %(MapN)s and o.longitude between %(MapW)s and %(MapE)s " sqlparam['MapN'] = gvp("MapN") sqlparam['MapW'] = gvp("MapW") sqlparam['MapE'] = gvp("MapE") sqlparam['MapS'] = gvp("MapS") if gvp("depthmin") != "" and gvp("depthmax") != "": whereclause += " and o.depth_min between %(depthmin)s and %(depthmax)s and o.depth_max between %(depthmin)s and %(depthmax)s " sqlparam['depthmin'] = gvp("depthmin") sqlparam['depthmax'] = gvp("depthmax") if gvp("samples") != "": whereclause += " and o.sampleid= any (%(samples)s) " sqlparam['samples'] = [int(x) for x in gvp("samples").split(',')] if gvp("instrum") != "": whereclause += " and o.acquisid in (select acquisid from acquisitions where instrument ilike %(instrum)s " + ( "and projid= any (%(projid)s)" if gvp("projid") != "" else "") + " )" sqlparam['instrum'] = '%' + gvp("instrum") + '%' PageTopProjectLink = MapForProject = None if gvp("projid") != "": whereclause += " and o.projid= any (%(projid)s) " sqlparam['projid'] = [int(x) for x in gvp("projid").split(',')] if len(sqlparam['projid']) == 1: PageTopProjectLink = "<p class='bg-info' style='font-size: larger;font-weight: bold;'>You can explore this project in more details on its <a href='/prj/{0}'>dedicated page</a></p>".format( str(sqlparam['projid'][0])) if gvp("fromdate") != "": whereclause += " and o.objdate>= to_date(%(fromdate)s,'YYYY-MM-DD') " sqlparam['fromdate'] = gvp("fromdate") if gvp("todate") != "": whereclause += " and o.objdate<= to_date(%(todate)s,'YYYY-MM-DD') " sqlparam['todate'] = gvp("todate") if gvp("month") != "": whereclause += " and extract(month from o.objdate) = any (%(month)s) " sqlparam['month'] = [int(x) for x in gvp("month").split(',')] if gvp("daytime") != "": whereclause += " and o.sunpos= any (%(daytime)s) " sqlparam['daytime'] = [x for x in gvp("daytime").split(',')] if gvp("inverttime") == "1": if gvp("fromtime") != "" and gvp("totime") != "": whereclause += " and (o.objtime<= time %(fromtime)s or o.objtime>= time %(totime)s)" sqlparam['fromtime'] = gvp("fromtime") sqlparam['totime'] = gvp("totime") else: if gvp("fromtime") != "": whereclause += " and o.objtime>= time %(fromtime)s " sqlparam['fromtime'] = gvp("fromtime") if gvp("totime") != "": whereclause += " and objtime<= time %(totime)s " sqlparam['totime'] = gvp("totime") sql += whereclause ExtraEndScript = "" if whereclause == "": # si aucune clause, on prend un projet au hasard randomproject = GetAll( "select projid,title from projects where visible=true and pctvalidated>1 order by random() limit 1" ) if randomproject: randomproject = randomproject[0] MapForProject = str(randomproject[0]) sql += " and o.projid= %s " % (randomproject[0]) ExtraEndScript = """$('#headersubtitle').html('Randomly selected project : <a href="?projid={0}">{1}</a>');""".format( randomproject['projid'], XSSEscape(randomproject['title'])) sql += " order by random_value Limit %d" % (2 * ipp, ) # pour de meilleure perf plus de random ici et du coup on prend 20xipp pour créer un peu d'aléa # sql+=" Limit %d"%(20*ipp,) # desactivé suite à split table objects mais pourrait devoir revenir. #filt_fromdate,#filt_todate sql = """select o.*,t.display_name taxoname,u.name classifwhoname,i.file_name,s.orig_id samplename ,i.height,i.width,i.thumb_file_name,i.thumb_height,i.thumb_width,ofi.orig_id from (""" + sql + """)o left Join images i on o.img0id=i.imgid left JOIN taxonomy t on o.classif_id=t.id LEFT JOIN users u on o.classif_who=u.id LEFT JOIN samples s on o.sampleid=s.sampleid left Join obj_field ofi on ofi.objfid=o.objid where o.projid in (select projid from projects where visible=true)""" # if whereclause!="": # on ne tri pas en random global s'il n'y a aucune criteres, impact de perf sql += " order by random_value " # sql+=" order by random() " sql += " Limit %d " % (ipp, ) res = GetAll(sql, sqlparam, debug=False, doXSSEscape=True) trcount = 1 LineStart = "" if (PageTopProjectLink): t.append(PageTopProjectLink) if MapForProject: t.append( render_template("search/explore_inserted_popup.html", Projid=MapForProject)) t.append("<table class=imgtab><tr id=tr1>" + LineStart) WidthOnRow = 0 #récuperation et ajustement des dimensions de la zone d'affichage try: PageWidth = int( gvp("resultwidth" )) - 40 # on laisse un peu de marge à droite et la scroolbar if PageWidth < 200: PageWidth = 200 except: PageWidth = 200 try: WindowHeight = int( gvp("windowheight")) - 100 # on enleve le bandeau du haut if WindowHeight < 200: WindowHeight = 200 except: WindowHeight = 200 #print("PageWidth=%s, WindowHeight=%s"%(PageWidth,WindowHeight)) # Calcul des dimmensions et affichage des images for r in res: filename = r['file_name'] origwidth = r['width'] origheight = r['height'] thumbfilename = r['thumb_file_name'] thumbwidth = r['thumb_width'] if origwidth is None: # pas d'image associé, pas trés normal mais arrive pour les subset sans images width = 80 height = 40 else: width = origwidth * zoom // 100 height = origheight * zoom // 100 if max( width, height ) < 120: # en dessous de 120 px de coté on ne fait plus le scaling if max(origwidth, origheight) < 120: width = origwidth # si l'image originale est petite on l'affiche telle quelle height = origheight elif max(origwidth, origheight) == origwidth: width = 120 height = origheight * 120 // origwidth if height < 1: height = 1 else: height = 120 width = origwidth * 120 // origheight if width < 1: width = 1 # On limite les images pour qu'elles tiennent toujours dans l'écran if width > PageWidth: width = PageWidth height = math.trunc(r['height'] * width / r['width']) if height == 0: height = 1 if height > WindowHeight: height = WindowHeight width = math.trunc(r['width'] * height / r['height']) if width == 0: width = 1 if WidthOnRow != 0 and (WidthOnRow + width) > PageWidth: trcount += 1 t.append("</tr></table><table class=imgtab><tr id=tr%d>%s" % (trcount, LineStart)) WidthOnRow = 0 cellwidth = width + 22 if cellwidth < 80: cellwidth = 80 # on considère au moins 80 car avec les label c'est rarement moins # Met la fenetre de zoon la ou il y plus de place, sachant qu'elle fait 400px et ne peut donc pas être callée à gauche des premieres images. if (WidthOnRow + cellwidth) > (PageWidth / 2): pos = 'left' else: pos = 'right' #Si l'image affiché est plus petite que la miniature, afficher la miniature. if thumbwidth is None or thumbwidth < width or thumbfilename is None: # sinon (si la miniature est plus petite que l'image à afficher ) thumbfilename = filename # la miniature est l'image elle même txt = "<td width={0}>".format(cellwidth) if filename: txt+="<img class='lazy' id=I{3} data-src='/vault/{5}' data-zoom-image='{0}' width={1} height={2} pos={4}>"\ .format(filename,width,height,r['objid'],pos,thumbfilename) else: txt += "No Image" # Génération de la popover qui apparait pour donner quelques détails sur l'image poptitletxt = "%s" % (r['orig_id'], ) # poptxt="%s"%(r['taxoname'],) poptxt = "" if r['classifwhoname'] != "": poptxt += "<em>by</em> %s" % (r['classifwhoname']) poptxt += "<br><em>in</em> " + ntcv(r['samplename']) popattribute = "data-title=\"{0}\" data-content=\"{1}\" data-placement='{2}'".format( poptitletxt, poptxt, 'left' if WidthOnRow > 500 else 'right') txt+="""<div class='subimg {1}' {2}> <div class='taxo'>{0}</div> </div> <div class=ddet><span class=ddets><span class='glyphicon glyphicon-eye-open'></span> {3}</div>"""\ .format(r['taxoname'],"",popattribute ,"(%d)"%(r['imgcount'],) if r['imgcount'] is not None and r['imgcount']>1 else "") txt += "</td>" # WidthOnRow+=max(cellwidth,80) # on ajoute au moins 80 car avec les label c'est rarement moins WidthOnRow += cellwidth + 5 # 5 = border-spacing = espace inter image t.append(txt) t.append("</tr></table>") if len(res) == 0: t.append( """<div class='alert alert-warning' role='alert' style='margin: 10px;font-size:larger;' > <span class='glyphicon glyphicon-remove-sign' style='color:#DD2222;'></span> Your selection does not return any object. <br>It may be too limited by the selected filters. <br>Try again releasing the selection criteria</div>""" ) t.append(""" <script> PostAddImages(); %s </script>""" % ExtraEndScript) return "\n".join(t)
def browsetaxoajax(): sql = """select t.id,t.parent_id,t.display_name as name,t.taxotype,t.taxostatus,t.creator_email,t.id_source ,to_char(t.creation_datetime,'yyyy-mm-dd hh24:mi') creation_datetime,to_char(t.lastupdate_datetime,'yyyy-mm-dd hh24:mi') lastupdate_datetime,{} from taxonomy t {} where 1=1 """.format(SQLTreeSelect, SQLTreeJoin) params = {} sqlcrit = "" start = 0 length = 200 if gvp('start').isdigit(): start = int(gvp('start')) if gvp('length').isdigit(): length = int(gvp('length')) if gvp('columns[0][search][value]').isdigit(): sqlcrit += " and t.id = %(id)s" params['id'] = int(gvp('columns[0][search][value]')) if gvp('columns[1][search][value]').isdigit(): if int(gvp('columns[1][search][value]')) == 0: sqlcrit += " and t.parent_id is null " else: sqlcrit += " and (t.parent_id = %(parent_id)s or t.id=%(parent_id)s ) " # or id pour faciliter la navigation params['parent_id'] = int(gvp('columns[1][search][value]')) if gvp('columns[2][search][value]'): sqlcrit += " and t.display_name ilike %(name)s" params['name'] = '%' + gvp('columns[2][search][value]') + '%' if gvp('columns[3][search][value]'): sqlcrit += " and t.taxotype = %(taxotype)s" params['taxotype'] = gvp('columns[3][search][value]') if gvp('columns[4][search][value]'): sqlcrit += " and t.taxostatus = %(taxostatus)s" params['taxostatus'] = gvp('columns[4][search][value]') if gvp('columns[5][search][value]'): sqlcrit += " and t.creator_email ilike %(creator_email)s" params['creator_email'] = '%' + gvp('columns[5][search][value]') + '%' if gvp('columns[6][search][value]'): sqlcrit += " and t.id_source ilike %(id_source)s" params['id_source'] = '%' + gvp('columns[6][search][value]') + '%' if gvp('columns[8][search][value]').isdigit(): sqlcrit += " and lastupdate_datetime like %(lastupdate)s" params['lastupdate'] = int(gvp('columns[8][search][value]')) if gvp('columns[9][search][value]'): sqlcrit += """ and tree ilike %(tree)s""" params['tree'] = '%' + gvp('columns[9][search][value]') + '%' sqlcount = "select count(*) from taxonomy t where 1=1 " if 'tree' in params: sqlcount = "select count(*) from ({}) t where 1=1 ".format(sql) sql = "select t.* from ({}) t where 1=1 ".format( sql) # permet de mettre des critères sur la colonne calculée tree orderclause = "" if gvp('order[0][column]').isdigit(): orderclause = " order by {} {}".format( int(gvp('order[0][column]')) + 1, 'desc' if gvp('order[0][dir]') == 'desc' else 'asc') sql += sqlcrit + orderclause + (" offset {} limit {}".format( start, length)) lst = GetAll(sql, params) for lstitem in lst: # Post traitement sur les chaines lstitem['tree'] = PackTreeTxt(lstitem['tree']) lstitem['name'] = XSSEscape(lstitem['name']) if lstitem['parent_id'] is None: lstitem['parent_id'] = "" sqlcount += sqlcrit if len(lst) >= length or start != 0: recordsFiltered = GetAll(sqlcount, params)[0][0] else: recordsFiltered = len(lst) nbrtaxon = GetAll("select count(*) from taxonomy")[0][0] res = { 'draw': int(gvp('draw')), 'recordsTotal': nbrtaxon, 'recordsFiltered': recordsFiltered, 'data': lst } return json.dumps(res)
def QuestionProcess(self): Prj=database.Projects.query.filter_by(projid=gvg("projid")).first() if not Prj.CheckRight(1): return PrintInCharte("ACCESS DENIED for this project<br>") g.prjtitle=Prj.title for k in sharedfilter.FilterList: self.param.filtres[k] = gvg(k, "") g.headcenter="<h4><a href='/prj/{0}'>{1}</a></h4>".format(Prj.projid,XSSEscape(Prj.title)) txt="" errors=[] # Le projet de base est choisi second écran ou validation du second ecran if gvp('starttask')=="Y": # validation du second ecran self.param.ProjectId=gvg("projid") if gvg("src",gvp("src",""))!="": self.param.BaseProject=database.CSVIntStringToInClause(gvg("src",gvp("src",""))) self.param.CritVar=gvp("CritVar") self.param.Perimeter=gvp("Perimeter") self.param.usemodel_foldername = gvp('modeldir', '') if gvp('ReadPostTaxoMappingFromLB') =="Y": self.param.PostTaxoMapping = ",".join((x[6:] + ":" + gvp(x) for x in request.form if x[0:6] == "taxolb")) else: self.param.PostTaxoMapping = gvp("PostTaxoMapping") self.param.learninglimit = gvp("learninglimit") self.param.keeplog=gvp("keeplog") self.param.savemodel_foldername = gvp("savemodel_foldername") self.param.savemodel_title = gvp("savemodel_title") self.param.savemodel_comments = gvp("savemodel_comments") self.param.usescn=gvp("usescn","") # self.param.Taxo=",".join( (x[4:] for x in request.form if x[0:4]=="taxo") ) self.param.Taxo =gvp('Taxo') self.param.CustSettings=DecodeEqualList(gvp("TxtCustSettings")) g.TxtCustSettings=gvp("TxtCustSettings") # Verifier la coherence des données if self.param.usemodel_foldername=='': if self.param.CritVar=='' and self.param.usescn=="": errors.append("You must select some variable") if self.param.Taxo=='' : errors.append("You must select some category") if len(errors)>0: for e in errors: flash(e,"error") else: # Pas d'erreur, on memorize les parametres dans le projet et on lance la tache # On ajoute les valeurs dans CustSettings pour les sauver dans le ClassifSettings du projet PrjCS = DecodeEqualList(Prj.classifsettings) d=self.param.CustSettings.copy() if gvg("src", gvp("src", "")) != "": # on écrase que si les données sont saisies, sinon on prend dans le projet d['critvar']=self.param.CritVar d['baseproject']=self.param.BaseProject d['seltaxo'] = self.param.Taxo if "usemodel_foldername" in PrjCS: d["usemodel_foldername"]=PrjCS["usemodel_foldername"] else: d['usemodel_foldername']=self.param.usemodel_foldername if "critvar" in PrjCS: d["critvar"]=PrjCS["critvar"] if "baseproject" in PrjCS: d["baseproject"]=PrjCS["baseproject"] if "seltaxo" in PrjCS: d["seltaxo"] = PrjCS["seltaxo"] d['posttaxomapping'] =self.param.PostTaxoMapping Prj.classifsettings=EncodeEqualList(d) return self.StartTask(self.param) else: # valeurs par default if gvp('frommodel', gvg('frommodel')) == "Y": if gvp('modeldir')=='': return self.QuestionProcessScreenSelectModel(Prj) elif gvp('displaytaxomap')=='Y': return self.QuestionProcessScreenSelectModelTaxo(Prj) else: if gvp('src', gvg('src')) == "": return self.QuestionProcessScreenSelectSource(Prj) elif gvp('seltaxo', gvg('seltaxo')) == "": return self.QuestionProcessScreenSelectSourceTaxo(Prj) d=DecodeEqualList(Prj.classifsettings) # Certaines variable on leur propre zone d'edition, les autres sont dans la zone texte custom settings self.param.CritVar=d.get("critvar","") self.param.Taxo=d.get("seltaxo","") self.param.Perimeter="nmc" self.param.learninglimit = int(gvp("learninglimit","5000")) if "critvar" in d : del d["critvar"] if "perimeter" in d : del d["perimeter"] if "methode" in d: del d["methode"] if "learninglimit" in d: del d["learninglimit"] if "seltaxo" in d : del d["seltaxo"] if "PostTaxoMapping" in d: del d["PostTaxoMapping"] if "baseproject" in d : del d["baseproject"] g.TxtCustSettings=EncodeEqualList(d) self.param.Taxo = ",".join((x[4:] for x in request.form if x[0:4] == "taxo" and x[0:6] != "taxolb")) self.param.PostTaxoMapping = ",".join((x[6:]+":"+gvp(x) for x in request.form if x[0:6] == "taxolb")) # Determination des criteres/variables utilisées par l'algo de learning revobjmap = self.GetReverseObjMap(Prj) PrjListInClause=database.CSVIntStringToInClause(gvp("src",gvg("src"))) LstPrjSrc=GetAll("select projid,mappingobj from projects where projid in({0})".format(PrjListInClause)) revobjmapbaseByProj={} CommonKeys = set(revobjmap.keys()) for PrjBase in LstPrjSrc: revobjmapbaseByProj[PrjBase['projid']] = self.GetReverseObjMap(PrjBase) CommonKeys = CommonKeys.intersection(set(revobjmapbaseByProj[PrjBase['projid']].keys())) # critlist[NomCol] 0:NomCol , 1:LS % validé rempli , 2:LS Nbr distincte ,3:Cible % rempli ,4:Cible % NV Rempli Inutile ? critlist={k:[k,0,0,0,0] for k in CommonKeys} # Calcul des stat des projets du LearningSet sql="select count(*) nbrtot" for k in CommonKeys: case="case " for PrjBase in LstPrjSrc: case +=" when projid={0} then {1} ".format(PrjBase['projid'],revobjmapbaseByProj[PrjBase['projid']][k])
def PrjEditDataMass(PrjId): request.form # Force la lecture des données POST sinon il y a une erreur 504 Prj = database.Projects.query.filter_by(projid=PrjId).first() if Prj is None: flash("Project doesn't exists", 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not Prj.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot edit settings for this project', 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") g.headcenter = "<h4><a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) txt = "<h3>Project Mass data edition </h3>" sqlparam = {} filtres = {} for k in sharedfilter.FilterList: if gvg(k): filtres[k] = gvg(k, "") field = gvp('field') if field and gvp('newvalue'): tables = { 'f': 'obj_field', 'h': 'obj_head', 's': 'samples', 'a': 'acquisitions', 'p': 'process' } tablecode = field[0] table = tables[ tablecode] # on extrait la table à partir de la premiere lettre de field field = field[ 1:] # on supprime la premiere lettre qui contenait le nom de la table sql = "update " + table + " set " + field + "=%(newvalue)s " if field == 'classif_id': sql += " ,classif_when=current_timestamp,classif_who=" + str( current_user.id) sql += " where " if tablecode == "h": sql += " objid in ( select objid from objects o " elif tablecode == "f": sql += " objfid in ( select objid from objects o " elif tablecode == "s": sql += " sampleid in ( select distinct sampleid from objects o " elif tablecode == "a": sql += " acquisid in ( select distinct acquisid from objects o " elif tablecode == "p": sql += " processid in ( select distinct processid from objects o " sql += " where projid=" + str(Prj.projid) sqlparam['newvalue'] = gvp('newvalue') if len(filtres): sql += " " + sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id)) sql += ")" if field == 'classif_id': sqlhisto = """insert into objectsclassifhisto(objid,classif_date,classif_type,classif_id,classif_qual,classif_who) select objid,classif_when,'M', classif_id,classif_qual,classif_who from objects o where projid=""" + str( Prj.projid) + " and classif_when is not null " sqlhisto += sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id)) ExecSQL(sqlhisto, sqlparam) ExecSQL(sql, sqlparam) flash('Data updated', 'success') if field == 'latitude' or field == 'longitude' or gvp('recompute') == 'Y': ExecSQL( """update samples s set latitude=sll.latitude,longitude=sll.longitude from (select o.sampleid,min(o.latitude) latitude,min(o.longitude) longitude from obj_head o where projid=%(projid)s and o.latitude is not null and o.longitude is not null group by o.sampleid) sll where s.sampleid=sll.sampleid and projid=%(projid)s """, {'projid': Prj.projid}) flash('sample latitude and longitude updated', 'success') sql = "select objid FROM objects o where projid=" + str(Prj.projid) if len(filtres): sql += sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id)) ObjList = GetAll(sql, sqlparam) ObjListTxt = "\n".join((str(r['objid']) for r in ObjList)) txt += "<span style='color:red;font-weight:bold;font-size:large;'>USING Active Project Filters, {0} objects</span>".format( len(ObjList)) else: txt += "<span style='color:red;font-weight:bold;font-size:large;'>Apply to ALL OBJETS OF THE PROJECT (NO Active Filters)</span>" Lst = GetFieldList(Prj) # txt+="%s"%(Lst,) return PrintInCharte( render_template("project/prjeditdatamass.html", Lst=Lst, header=txt))
def PrjResetToPredicted(PrjId): request.form # Force la lecture des données POST sinon il y a une erreur 504 Prj = database.Projects.query.filter_by(projid=PrjId).first() if Prj is None: flash("Project doesn't exists", 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") if not Prj.CheckRight(2): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot edit settings for this project', 'error') return PrintInCharte("<a href=/prj/>Select another project</a>") g.headcenter = "<h4><a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) txt = "<h3>Reset status to predicted</h3>" sqlparam = {} filtres = {} for k in sharedfilter.FilterList: if gvg(k): filtres[k] = gvg(k, "") process = gvp('process') if process == 'Y': sqlhisto = """insert into objectsclassifhisto(objid,classif_date,classif_type,classif_id,classif_qual,classif_who) select objid,classif_when,'M', classif_id,classif_qual,classif_who from objects o where projid=""" + str( Prj.projid ) + """ and classif_when is not null and classif_qual in ('V','D') and not exists(select 1 from objectsclassifhisto och where och.objid=o.objid and och.classif_date=o.classif_when) """ sqlhisto += sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id)) ExecSQL(sqlhisto, sqlparam) sqlhisto = """update obj_head set classif_qual='P' where projid={0} and objid in (select objid from objects o where projid={0} and classif_qual in ('V','D') {1}) """.format( Prj.projid, sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id))) ExecSQL(sqlhisto, sqlparam) # flash('Data updated', 'success') txt += "<a href='/prj/%s' class='btn btn-primary'>Back to project</a> " % ( Prj.projid) appli.project.main.RecalcProjectTaxoStat(Prj.projid) appli.project.main.UpdateProjectStat(Prj.projid) return PrintInCharte(txt) sql = "select objid FROM objects o where projid=" + str(Prj.projid) if len(filtres): sql += sharedfilter.GetSQLFilter(filtres, sqlparam, str(current_user.id)) ObjList = GetAll(sql, sqlparam) ObjListTxt = "\n".join((str(r['objid']) for r in ObjList)) txt += "<span style='color:red;font-weight:bold;font-size:large;'>USING Active Project Filters, {0} objects</span>".format( len(ObjList)) else: txt += "<span style='color:red;font-weight:bold;font-size:large;'>Apply to ALL OBJETS OF THE PROJECT (NO Active Filters)</span>" Lst = GetFieldList(Prj) # txt+="%s"%(Lst,) return PrintInCharte( render_template("project/prjresettopredicted.html", Lst=Lst, header=txt))
def QuestionProcess(self): Prj=database.Projects.query.filter_by(projid=gvg("projid")).first() txt="<a href='/prj/%d'>Back to project</a>"%Prj.projid if not Prj.CheckRight(1): return PrintInCharte("ACCESS DENIED for this project<br>"+txt) txt+="<h3>Text export Task creation</h3>" txt+="<h5>Exported Project : #%d - %s</h5>"%(Prj.projid,XSSEscape(Prj.title)) errors=[] self.param.filtres = {} for k in sharedfilter.FilterList: if gvg(k, "") != "": self.param.filtres[k] = gvg(k, "") if len(self.param.filtres) > 0: TxtFiltres = ",".join([k + "=" + v for k, v in self.param.filtres.items() if v != ""]) else: TxtFiltres="" if self.task.taskstep==0: # Le projet de base est choisi second écran ou validation du second ecran if gvp('starttask')=="Y": # validation du second ecran self.param.ProjectId=gvg("projid") self.param.what=gvp("what") self.param.samplelist=gvp("samplelist") self.param.objectdata=gvp("objectdata") self.param.processdata=gvp("processdata") self.param.acqdata=gvp("acqdata") self.param.sampledata=gvp("sampledata") self.param.histodata=gvp("histodata") self.param.commentsdata=gvp("commentsdata") self.param.usecomasepa=gvp("usecomasepa") self.param.sumsubtotal=gvp("sumsubtotal") self.param.internalids = gvp("internalids") self.param.use_internal_image_name = gvp("use_internal_image_name") self.param.exportimagesbak = gvp("exportimagesbak") self.param.exportimagesdoi = gvp("exportimagesdoi") self.param.typeline = gvp("typeline") self.param.splitcsvby = gvp("splitcsvby") self.param.putfileonftparea = gvp("putfileonftparea") if self.param.splitcsvby=='sample': # si on splitte par sample, il faut les données du sample self.param.sampledata='1' # Verifier la coherence des données # errors.append("TEST ERROR") if self.param.what=='' : errors.append("You must select What you want to export") if len(errors)>0: for e in errors: flash(e,"error") else: # Pas d'erreur, on lance la tache return self.StartTask(self.param) else: # valeurs par default self.param.what ="TSV" self.param.objectdata = "1" self.param.processdata = "1" self.param.acqdata = "1" self.param.sampledata = "1" self.param.splitcsvby="" #recupere les samples sql="""select sampleid,orig_id from samples where projid =%(projid)s order by orig_id""" g.SampleList=GetAll(sql,{"projid":gvg("projid")},cursor_factory=None) g.headcenter="<h4>Project : <a href='/prj/{0}'>{1}</a></h4>".format(Prj.projid,XSSEscape(Prj.title)); if TxtFiltres!="": g.headcenter = "<h4>Project : <a href='/prj/{0}?{2}'>{1}</a></h4>".format(Prj.projid, Prj.title, "&".join([k + "=" + v for k, v in self.param.filtres.items() if v != ""])) LstUsers = database.GetAll("""select distinct u.email,u.name,Lower(u.name) FROM users_roles ur join users u on ur.user_id=u.id where ur.role_id=2 and u.active=TRUE and email like '%@%' order by Lower(u.name)""") g.LstUser = "******".join(["<a href='mailto:{0}'>{0}</a></li> ".format(*r) for r in LstUsers]) return render_template('task/textexport_create.html',header=txt,data=self.param,TxtFiltres=TxtFiltres)
def QuestionProcess(self): self.param.ProjectId = gvg("p") Prj = database.Projects.query.filter_by( projid=self.param.ProjectId).first() if not Prj.CheckRight(1): return PrintInCharte("ACCESS DENIED for this project<br>" + Prj.title) txt = "" g.headcenter = "<h4><a href='/prj/{0}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title)) errors = [] if self.task.taskstep == 0: if gvg('eps') == "": # eps = Extra Project selected # Premier écran de configuration, choix du projet de base txt = "<h3>SUBSET PROJECT SELECTION Page (1/2)</h3>" txt += """<h4>SELECT ADDITIONAL PROJECT TO INCLUDE IN THE SUBSET</h4> <form action="?p={0}&eps=y" method=post> """.format(Prj.projid) from flask_login import current_user sql = "select projid,title from projects " if not current_user.has_role(database.AdministratorLabel): sql += " where projid in (select projid from projectspriv where member=%d and privilege='Manage')" % current_user.id sql += " order by title" ProjList = database.GetAll(sql) txt += """<select name=extraprojects id= extraprojects multiple>""" for r in ProjList: txt += "<option value='{0}'>{1} ({0})</option>".format(*r) txt += """</select><br><br> <input class="btn btn-primary" type=submit value="Go to the next page"> <script> $(document).ready(function() { $("#extraprojects").select2(); }); </script> </form> <br><br> <div class='panel panel-default' style="width:600px;margin-left:10px;"> A SUBSET is a selection of images (objects) randomly copied from one or more source projects<br> A SUBSET can have different usages:<br> <ul> <li>It can be utilized as a Learning Set for the automatic classification <li>It can be utilized as a Test Set to evaluate the quality of the prediction or the validation </ul> </div> """ return PrintInCharte(txt) self.param.filtres = {} for k in sharedfilter.FilterList: if gvg(k, "") != "": self.param.filtres[k] = gvg(k, "") filtertxt = "" if len(self.param.filtres) > 0: filtertxt += ",".join([ k + "=" + v for k, v in self.param.filtres.items() if v != "" ]) g.headcenter = "<h4><a href='/prj/{0}?{2}'>{1}</a></h4>".format( Prj.projid, XSSEscape(Prj.title), "&".join([ k + "=" + v for k, v in self.param.filtres.items() if v != "" ])) # Le projet de base est choisi second écran ou validation du second ecran if gvp('starttask') == "Y": # validation du second ecran self.param.extraprojects = gvp("extraprojects") self.param.samplelist = gvp("samplelist") self.param.withimg = gvp("withimg") self.param.subsetprojecttitle = gvp("subsetprojecttitle") self.param.valtype = gvp("valtype") if self.param.valtype == 'V': try: self.param.valeur = int(gvp("vvaleur")) if self.param.valeur <= 0: errors.append("Absolute value not in range") except: errors.append("Invalid Absolute value") if self.param.valtype == 'P': try: self.param.valeur = int(gvp("pvaleur")) if self.param.valeur <= 0 or self.param.valeur > 100: errors.append("% value not in range") except: errors.append("Invalid % value") tmp = [] if gvp('what_v'): tmp.append('V') if gvp('what_d'): tmp.append('D') if gvp('what_p'): tmp.append('P') if gvp('what_n'): tmp.append('N') self.param.what = ",".join(tmp) # Verifier la coherence des données # errors.append("TEST ERROR") # if self.param.what=='' : errors.append("You must select at least one Flag") if self.param.valtype == '': errors.append( "You must select the object selection parameter '% of values' or '# of objects'" ) if len(errors) > 0: for e in errors: flash(e, "error") else: # Pas d'erreur, on lance la tache return self.StartTask(self.param) else: # valeurs par default self.param.what = "V" self.param.subsetprojecttitle = ( Prj.title + " - Subset created on " + (datetime.date.today().strftime('%Y-%m-%d')))[0:255] self.param.extraprojects = ",".join( request.form.getlist('extraprojects')) if filtertxt != "": # s'il y a un filtre on coche toutes les cases et on masque la zone à l'affichage self.param.what = "V,D,P,N" txt = "<h3>SUBSET SETTINGS Page (2/2)</h3>" if self.param.extraprojects: ExtraPrj = database.Projects.query.filter( text("projid in (%s)" % self.param.extraprojects)).all() g.dispextraprojects = " ".join([ "<br>- {1} ({0}) ".format(r.projid, r.title) for r in ExtraPrj ]) for p in ExtraPrj: if p.mappingobj != Prj.mappingobj: flash( "Object variables and metadata differ on project %d (%s). The subset should not be utilized as a Learning Set" % (p.projid, p.title), "warning") if p.mappingsample != Prj.mappingsample: flash( "Sample variables mapping differ on project %d (%s)." % (p.projid, p.title), "warning") if p.mappingacq != Prj.mappingacq: flash( "Acquisition variables mapping differ on project %d (%s)" % (p.projid, p.title), "warning") if p.mappingprocess != Prj.mappingprocess: flash( "Process variables mapping differ on project %d (%s)" % (p.projid, p.title), "warning") else: g.dispextraprojects = "None" #recupere les samples sql = """select sampleid,orig_id from samples where projid =%(projid)s order by orig_id""" g.SampleList = GetAll(sql, {"projid": gvg("p")}, cursor_factory=None) return render_template('task/subset_create.html', header=txt, data=self.param, prevpost=request.form, filtertxt=filtertxt)
def objectdetails(objid): #récuperation et ajustement des dimensions de la zone d'affichage try: PageWidth = int(gvg( "w")) - 40 # on laisse un peu de marge à droite et la scroolbar if PageWidth < 200: PageWidth = 20000 WindowHeight = int(gvg("h")) - 40 # on laisse un peu de marge en haut if WindowHeight < 200: WindowHeight = 20000 except: PageWidth = 20000 WindowHeight = 20000 obj = database.Objects.query.filter_by(objid=objid).first() t = list() # Dans cet écran on utilise ElevateZoom car sinon en mode popup il y a conflit avec les images sous la popup t.append("<script src='/static/jquery.elevatezoom.js'></script>") Prj = obj.project if Prj.visible == False and not Prj.CheckRight( 0): # Level 0 = Read, 1 = Annotate, 2 = Admin flash('You cannot view this project', 'error') return PrintInCharte("<a href=/>Back to home</a>") g.Projid = Prj.projid PrjManager = [(m.memberrel.email, m.memberrel.name) for m in Prj.projmembers if m.privilege == 'Manage'] t.append("<p>Project: <b><a href='/prj/%d'>%s</a></b> (managed by : %s)" % (Prj.projid, XSSEscape(Prj.title), ",".join( ("<a href ='mailto:%s'>%s</a>" % m for m in PrjManager)))) if len(PrjManager) > 0: t.append( "<br>To report a mistake, contact <a href ='mailto:{0}?subject=Ecotaxa%20mistake%20notification&body={2}'>{1}</a>" .format( PrjManager[0][0], PrjManager[0][1], urllib.parse.quote( "Hello,\n\nI have discovered a mistake on this page " + request.base_url + "\n"))) # //window.location="mailto:?subject=Ecotaxa%20page%20share&body="+encodeURIComponent("Hello,\n\nAn Ecotaxa user want share this page with you \n"+url); t.append("</p><p>Classification :") if obj.classif: t.append("<br> <b>%s</b>" % XSSEscape(obj.classif.display_name)) TaxoHierarchie = (r[0] for r in GetAll( """WITH RECURSIVE rq(id,name,parent_id) as ( select id,name,parent_id FROM taxonomy where id =%(taxoid)s union SELECT t.id,t.name,t.parent_id FROM rq JOIN taxonomy t ON t.id = rq.parent_id ) select name from rq""", {"taxoid": obj.classif.id})) t.append("<br> " + (" < ".join(TaxoHierarchie)) + " (id=%s)" % obj.classif_id) else: t.append("<br> <b>Unknown</b>") if obj.classiffier is not None: t.append( "<br> %s " % (database.ClassifQual.get(obj.classif_qual, "To be classified"))) t.append(" by %s (%s) " % (obj.classiffier.name, obj.classiffier.email)) if obj.classif_when is not None: t.append(" on %s " % (obj.classif_when.strftime("%Y-%m-%d %H:%M"))) t.append("</p>") if obj.objfrel.object_link is not None: t.append("<p>External link :<a href='{0}' target=_blank> {0}</a></p>". format(obj.objfrel.object_link)) t.append( "<table><tr><td valign=top>Complementaty information <a href='javascript:gotocommenttab();' > ( edit )</a>: </td><td> <span id=spancomplinfo> {0}</span></td></tr></table>" .format(ntcv(obj.complement_info).replace('\n', '<br>\n'))) # On affiche la liste des images, en selectionnant une image on changera le contenu de l'image Img1 + Redim # l'approche avec des onglets de marchait pas car les images sont superposées obj.images.sort(key=lambda x: x.imgrank) t.append("""<p>Image list : """) for img in obj.images: (width, height) = ComputeLimitForImage(img.width, img.height, PageWidth, WindowHeight) if img.thumb_file_name: minifile = img.thumb_file_name (miniwidth, miniheight) = ComputeLimitForImage(img.thumb_width, img.thumb_height, 30, 30) else: minifile = img.file_name (miniwidth, miniheight) = ComputeLimitForImage(img.width, img.height, 30, 30) t.append( """<a href="javascript:SwapImg1('{1}',{2},{3});" >{0} <img src=/vault/{4} width={5} height={6}></a> """ .format(img.imgrank + 1, img.file_name, width, height, minifile, miniwidth, miniheight)) # Ajout de la 1ère image (width, height) = ComputeLimitForImage(obj.images[0].width, obj.images[0].height, PageWidth, WindowHeight) t.append( "</p><p><img id=img1 src=/vault/{1} data-zoom-image=/vault/{1} width={2} height={0}></p>" .format(height, obj.images[0].file_name, width)) # Affichage de l'onglet de classification if Prj.CheckRight(1): t.append(""" <table><tr><td>Set a new classification :</td> <td style="width: 230px;"> <div class="input-group"> <select id="taxolbpop" name="taxolbpop" style="width: 200px" class='taxolb' > </select>""" ) if gvg("ajax", "0") == "0": t.append("""<span class="input-group-btn"> <button class="btn btn-default btn-sm" type="button" data-toggle="modal" data-target="#TaxoModal" data-mytargetid="taxolbpop" title="Search on Taxonomy Tree"> <span id=OpenTaxoLB class="glyphicon glyphicon-th-list" aria-hidden="true"/></button> </span>""") else: t.append("<br>") t.append("""</div><!-- /input-group --> <span id=PendingChangesPop></span></td><td width=30px></td><td valign=top> <button type="button" class="btn btn-success btn-xs" onclick="Save1Object('V');">Save as Validated</button> <button type="button" class="btn btn-warning btn-xs" onclick="Save1Object('D');">Save as dubious</button> <button id=btenableedit type="button" class="btn btn-gris btn-xs" onclick="EnableEdit();">Enable Editing</button> <button type="button" class="btn btn-default btn-xs" onclick="$('#PopupDetails').modal('hide');">Close</button> </td></tr></table> """) # Ajout des Onglets sous l'image t.append("""<br><div><ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"><a href="#tabdobj" aria-controls="tabdobj" role="tab" data-toggle="tab"> Object details</a></li> <li role="presentation" ><a href="#tabdsample" aria-controls="tabdsample" role="tab" data-toggle="tab"> Sample details</a></li> <li role="presentation" ><a href="#tabdacquis" aria-controls="tabdacquis" role="tab" data-toggle="tab"> Acquisition details</a></li> <li role="presentation" ><a href="#tabdprocessrel" aria-controls="tabdprocess" role="tab" data-toggle="tab"> Processing details</a></li> <li role="presentation" ><a href="#tabdclassiflog" aria-controls="tabdclassiflog" role="tab" data-toggle="tab">Classification change log</a></li> <li role="presentation" ><a href="#tabdmap" aria-controls="tabdmap" role="tab" data-toggle="tab" id=atabdmap style="background: #5CB85C;color:white;">Map</a></li> """) if Prj.CheckRight(1): t.append( """<li role="presentation" ><a id=linktabdaddcomments href="#tabdaddcomments" aria-controls="tabdaddcomments" role="tab" data-toggle="tab">Edit complementary informations</a></li>""" ) if obj.classif_auto: classif_auto_name = obj.classif_auto.name if obj.classif_auto_score: classif_auto_name += " (%0.3f)" % (obj.classif_auto_score, ) else: classif_auto_name = '' t.append("""</ul> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="tabdobj"> <table class='table table-bordered table-condensed' data-table='object'><tr> <td style=' background-color: #f2f2f2;' data-edit='longitude'><b>longitude</td><td>{0}</td> <td style=' background-color: #f2f2f2;' data-edit='latitude'><b>latitude</td><td>{1}</td> <td style=' background-color: #f2f2f2;' data-edit='objdate'><b>Date</td><td>{2}</td> <td style=' background-color: #f2f2f2;'><b>Time (daytime)</td><td>{3} ({10})</td> </tr><tr><td style=' background-color: #f2f2f2;' data-edit='depth_min'><b>Depth min</td><td>{4}</td> <td style=' background-color: #f2f2f2;' data-edit='depth_max'><b>Depth max</td><td>{5}</td> <td><b>Classif auto</td><td>{6}</td><td><b>Classif auto when</td><td>{7}</td> </tr><tr><td><b>Object #</td><td>{8}</td> <td data-edit='orig_id'><b>Original Object ID</td><td colspan=5>{9}</td></tr><tr>""" .format(nonetoformat(obj.longitude, '.5f'), nonetoformat(obj.latitude, '.5f'), obj.objdate, obj.objtime, obj.depth_min, obj.depth_max, classif_auto_name, obj.classif_auto_when, objid, obj.objfrel.orig_id, database.DayTimeList.get(obj.sunpos, '?'))) cpt = 0 # Insertion des champs object for k, v in collections.OrderedDict( sorted(DecodeEqualList(Prj.mappingobj).items())).items(): if cpt > 0 and cpt % 4 == 0: t.append("</tr><tr>") cpt += 1 t.append("<td data-edit='{2}'><b>{0}</td><td>{1}</td>".format( v, ScaleForDisplay(getattr(obj.objfrel, k, "???")), k)) t.append("</tr></table></div>") # insertion des champs Sample, Acquisition & Processing dans leurs onglets respectifs for r in (("Sample", "mappingsample", "sample"), ("Acquisition", "mappingacq", "acquis"), ("Processing", "mappingprocess", "processrel")): t.append( '<div role="tabpanel" class="tab-pane" id="tabd' + r[2] + '">' + r[0] + " details :<table class='table table-bordered table-condensed' data-table='" + r[2] + "'><tr>") cpt = 0 if getattr(obj, r[2]): if r[2] == "sample": t.append( "<td data-edit='orig_id'><b>{0}</td><td colspan=3>{1}</td><td data-edit='longitude'><b>{2}</td><td>{3}</td><td data-edit='latitude'><b>{4}</td><td>{5}</td></tr><tr>" .format( "Original ID", ScaleForDisplay(obj.sample.orig_id), "longitude", ScaleForDisplay(obj.sample.longitude), "latitude", ScaleForDisplay(obj.sample.latitude), )) elif r[2] == "acquis": t.append( "<td data-edit='orig_id'><b>{0}</td><td colspan=3>{1}</td><td data-edit='instrument'><b>{2}</td><td>{3}</td></tr><tr>" .format( "Original ID", ScaleForDisplay(obj.acquis.orig_id), "Instrument", ScaleForDisplay(obj.acquis.instrument), )) else: t.append( "<td data-edit='orig_id'><b>{0}</td><td>{1}</td></tr><tr>". format( "Original ID.", ScaleForDisplay( getattr(getattr(obj, r[2]), "orig_id", "???")))) for k, v in collections.OrderedDict( sorted(DecodeEqualList(getattr(Prj, r[1])).items())).items(): if cpt > 0 and cpt % 4 == 0: t.append("</tr><tr>") cpt += 1 t.append("<td data-edit='{2}'><b>{0}</td><td>{1}</td>".format( v, ScaleForDisplay(getattr(getattr(obj, r[2]), k, "???")), k)) if r[2] == "sample": t.append( "</tr><tr><td><b>{0}</td><td colspan=7>{1}</td></tr><tr>". format( "Dataportal Desc.", ScaleForDisplay( html.escape(ntcv( obj.sample.dataportal_descriptor))))) else: t.append("<td>No {0}</td>".format(r[0])) t.append("</tr></table></div>") # Affichage de l'historique des classification t.append("""<div role="tabpanel" class="tab-pane" id="tabdclassiflog"> Current Classification : Quality={} , date={} <table class='table table-bordered table-condensed'><tr> <td>Date</td><td>Type</td><td>Taxo</td><td>Author</td><td>Quality</td></tr>""" .format(obj.classif_qual, obj.classif_when)) Histo = GetAll( """SELECT to_char(classif_date,'YYYY-MM-DD HH24:MI:SS') datetxt,classif_type ,t.display_name as name,u.name username,classif_qual from objectsclassifhisto h left join taxonomy t on h.classif_id=t.id LEFT JOIN users u on u.id = h.classif_who WHERE objid=%(objid)s order by classif_date desc""", {"objid": objid}, doXSSEscape=True) for r in Histo: t.append("<tr><td>" + ("</td><td>".join([ str(r[x]) if r[x] else "-" for x in ("datetxt", "classif_type", "name", "username", "classif_qual") ])) + "</td></tr>") t.append("</table></div>") if Prj.CheckRight(1): t.append("""<div role="tabpanel" class="tab-pane" id="tabdaddcomments"> <textarea id=compinfo rows=5 cols=120 autocomplete=off>%s</textarea><br> <button type="button" class='btn btn-primary' onclick="UpdateComment();">Save additional comment</button> <span id=ajaxresultcomment></span> """ % (ntcv(obj.complement_info), )) t.append("</div>") # Affichage de la carte t.append(""" <div role="tabpanel" class="tab-pane" id="tabdmap"> <div id="map2" class="map2" style="width: 100%; height: 450px;"> Displaying Map requires Internet Access to load map from https://server.arcgisonline.com </div>""") t.append("</table></div>") t.append( render_template("common/objectdetailsscripts.html", Prj=Prj, objid=objid, obj=obj)) # En mode popup ajout en haut de l'écran d'un hyperlien pour ouvrir en fenete isolée # Sinon affichage sans lien dans la charte. if gvg("ajax", "0") == "1": return """<table width=100% style='margin: 3px'><tr><td><a href='/objectdetails/{0}?w={1}&h={2}' target=_blank><b>Open in a separate window</b> (right click to copy link)</a> </td><td align='right'><button type="button" class="btn btn-default" onclick="$('#PopupDetails').modal('hide');">Close</button> </td></tr></table><div style='margin: 0 5px;'>""".format( objid, gvg("w"), gvg("h")) + "\n".join(t) return PrintInCharte("<div style='margin-left:10px;'>" + "\n".join(t) + render_template('common/taxopopup.html'))