Skip to content
Commits on Source (20)
......@@ -75,8 +75,8 @@ if envtype.upper() != "DEV":
RP_ID = "icnml.unil.ch"
else:
domain = "http://localhost"
RP_ID = "localhost"
domain = os.environ.get( "DOMAIN", "http://localhost" )
RP_ID = os.environ.get( "RPID", "localhost" )
baseurl = os.environ.get( "BASEURL", "" )
fulldomain = domain + baseurl
......
--
-- PostgreSQL database dump
--
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_with_oids = false;
--
-- Name: activities; Type: TABLE; Schema: public; Owner: icnml
--
CREATE TABLE public.activities (
id integer NOT NULL,
name character varying NOT NULL
);
ALTER TABLE public.activities OWNER TO icnml;
--
-- Name: activities_id_seq; Type: SEQUENCE; Schema: public; Owner: icnml
--
CREATE SEQUENCE public.activities_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.activities_id_seq OWNER TO icnml;
--
-- Name: activities_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: icnml
--
ALTER SEQUENCE public.activities_id_seq OWNED BY public.activities.id;
--
-- Name: activities id; Type: DEFAULT; Schema: public; Owner: icnml
--
ALTER TABLE ONLY public.activities ALTER COLUMN id SET DEFAULT nextval('public.activities_id_seq'::regclass);
--
-- PostgreSQL database dump complete
--
......@@ -2,14 +2,17 @@
<div id="icnml_navigation_adminsubmissions">
<a href="{{ url_for( 'submission.admin_submission_list' ) }}">Submissions</a>
</div>
<div id="icnml_navigation_adminsubmissionstable">
<a href="{{ url_for( 'submission.admin_submission_table' ) }}">Tables</a>
</div>
<div id="icnml_navigation_admintenprint">
<a href="{{ url_for( 'submission.admin_tenprint_list', submission_id = 'all' ) }}">Tenprint cards</a>
</div>
<div id="icnml_navigation_adminsubmissionstable">
<a href="{{ url_for( 'submission.admin_submission_table' ) }}">Tables</a>
</div>
<div id="icnml_navigation_exercises">
<a href="{{ url_for( 'trainer.exercises_list' ) }}">Exercises</a>
<a href="{{ url_for( 'trainer.exercises_list' ) }}">Trainer folders</a>
</div>
<div id="icnml_navigation_afis">
<a href="{{ url_for( 'afis.admin_list' ) }}">AFIS</a>
</div>
<div id="icnml_navigation_pianos">
<a href="{{ url_for( 'pianos.pianos_actions' ) }}">PiAnoS</a>
......
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from uuid import uuid4
from flask import Blueprint
from flask import jsonify, request
from utils.decorator import admin_required
from utils.template import my_render_template
import config
from utils.sql import sql_insert_generate
from flask.globals import current_app
afis_view = Blueprint( "afis", __name__, template_folder = "templates" )
@afis_view.route( "/afis/get/list" )
......@@ -12,3 +21,123 @@ def download_list():
return my_render_template(
"afis/search_list.html"
)
@afis_view.route( "/admin/afis/list" )
@admin_required
def admin_list():
sql = """
SELECT
cnm_folder.uuid,
users.username
FROM cnm_folder
INNER JOIN submissions ON submissions.uuid = cnm_folder.donor
INNER JOIN users ON submissions.donor_id = users.id
"""
folders = config.db.query_fetchall( sql )
sql = """
SELECT
submissions.uuid,
users.username
FROM submissions
LEFT JOIN users ON submissions.donor_id = users.id
LEFT JOIN cnm_folder ON submissions.uuid = cnm_folder.donor
WHERE cnm_folder.uuid IS NULL
ORDER BY users.id ASC
"""
donors = config.db.query_fetchall( sql )
return my_render_template(
"afis/admin/list.html",
folders = folders,
donors = donors
)
@afis_view.route( "/admin/afis/new_folder", methods = [ "POST" ] )
@admin_required
def admin_add_new_folder():
try:
donor_uuid = request.form.get( "donor_uuid", False )
sql = "SELECT count( * ) AS nb FROM cnm_folder WHERE donor = %s"
nb = config.db.query_fetchone( sql, ( donor_uuid, ) )[ "nb" ]
if nb == 0:
sql = sql_insert_generate( "cnm_folder", [ "donor", "uuid" ], "id" )
config.db.query_fetchone( sql, ( donor_uuid, str( uuid4() ) ) )
config.db.commit()
return jsonify( {
"error": False
} )
except:
return jsonify( {
"error": True
} )
@afis_view.route( "/admin/afis/<folder_id>" )
@admin_required
def admin_folder_show( folder_id ):
sql = """
SELECT
segments_locations.fpc
FROM segments_locations
INNER JOIN files_v ON segments_locations.tenprint_id = files_v.uuid
INNER JOIN submissions ON files_v.folder = submissions.id
INNER JOIN cnm_folder ON submissions.uuid = cnm_folder.donor
WHERE cnm_folder.uuid = %s
ORDER BY fpc ASC
"""
segment_list = config.db.query_fetchall( sql, ( folder_id, ) )
sql = """
SELECT *
FROM cnm_segments
WHERE folder_uuid = %s
ORDER BY fpc ASC
"""
segment_list_in_folder = config.db.query_fetchall( sql, ( folder_id, ) )
sql = """
SELECT
files_segments_v.tenprint,
files_segments_v.pc
FROM cnm_folder
INNER JOIN submissions ON cnm_folder.donor = submissions.uuid
INNER JOIN files_v ON submissions.id = files_v.folder
INNER JOIN files_segments_v ON files_v.uuid = files_segments_v.tenprint
WHERE cnm_folder.uuid = %s
"""
tpid = config.db.query_fetchall( sql, ( folder_id, ) )
return my_render_template(
"afis/admin/folder.html",
segment_list = segment_list,
segment_list_in_folder = segment_list_in_folder,
tpid = tpid,
folder_id = folder_id
)
@afis_view.route( "/admin/afis/<folder_id>/add/segment", methods = [ "POST" ] )
@admin_required
def admin_add_segment_to_cnmfolder( folder_id ):
try:
fpc = request.form.get( "fpc" )
sql = "SELECT count( * ) AS nb FROM cnm_segments WHERE folder_uuid = %s AND fpc = %s"
nb = config.db.query_fetchone( sql, ( folder_id, fpc, ) )[ "nb" ]
if nb == 0:
sql = sql_insert_generate( "cnm_segments", [ "uuid", "folder_uuid", "fpc" ], "uuid" )
config.db.query_fetchone( sql, ( str( uuid4() ), folder_id, fpc, ) )
config.db.commit()
return jsonify( {
"error": False
} )
except:
return jsonify( {
"error": True
} )
<!DOCTYPE html>
<html>
<head>
{% for src in js %}
<script type="text/javascript" src="{{ src }}"></script>
{% endfor %}
{% for src in css %}
<link type="text/css" rel="stylesheet" href="{{ src }}">
{% endfor %}
<script type="text/javascript" src="{{ url_for( 'files.send_app_files', subpath = 'functions.js' ) }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for( 'files.send_app_files', subpath = 'app.css' ) }}">
<style type="text/css">
#icnml_segments_list {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat( auto-fit, minmax( 200px, max-content ) );
grid-auto-rows: min-content;
}
.icnml_new_segment {
cursor: pointer;
}
.icnml_new_segment > span {
line-height: 175px;
font-size: 4.3em;
}
.icnml_new_segment {
margin-top: 0px;
}
.icnml_cnm_segments {
height: 200px;
width: 200px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
</style>
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var folder_id = "{{ folder_id }}";
var tpid = {};
{% for t in tpid %}
tpid[ "{{ t[ 'pc' ] }}" ] = "{{ t[ 'tenprint' ] }}";
{% endfor %}
var add_new_segment = function()
{
var create_new_segment = function()
{
var selected = $( "#segment_select > option:checked" ).val() || null;
console.log( selected );
$.ajax( {
url: "{{ url_for( 'afis.admin_add_segment_to_cnmfolder', folder_id = folder_id ) }}",
dataType: "json",
method: "POST",
data: {
fpc: selected
},
success: function( data )
{
if( ! data.error )
{
toastr.success( "Segment added" );
setTimeout( function(){ window.location.reload(); }, 500 );
} else {
toastr.error( "Error while adding the segment" );
}
$( "#new_segment" ).remove();
}
} );
}
var segment_select = $( "<select >" )
.attr( "id", "segment_select" )
segment_select.append( $( "<option />" ) );
{% for s in segment_list %}
segment_select.append(
$( "<option />" )
.attr( "value", "{{ s[ 'fpc' ] }}" )
.text( "{{ s[ 'fpc' ] }}" )
);
{% endfor %}
var segment_list = $( "<div />" )
.css( "margin-top", "5px" )
.append( segment_select );
$( "<div />" )
.attr( "id", "new_segment" )
.text( "Add the followigng" )
.append( segment_list )
.dialog( {
title: "new AFIS segment",
modal: true,
buttons: {
"OK": create_new_segment,
"Cancel": function()
{
$( this ).dialog( "close" );
},
},
close: function()
{
$( this ).remove();
}
} );
}
</script>
</head>
<body class="icnml_main_layout">
{% include "header.html" %}
{% include navigation %}
<div class="icnml_content">
<div id="icnml_segments_list">
{% for s in segment_list_in_folder %}
<div>
<div class="ui-widget-header ui-corner-top icnml_box_top">{{ s[ 'fpc' ] }}</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button icnml_cnm_segments" id="segment_{{ s[ 'uuid' ] }}"></div>
</div>
</div>
{% endfor %}
<div style="margin-bottom: 20px">
<div class="ui-widget-header ui-corner-top icnml_box_top">New</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button icnml_cnm_segments icnml_new_segment" id="icnml_new_segment">
<span>+</span>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
{% for s in segment_list_in_folder %}
var url = "{{ url_for( 'image.image_segment_serve', tenprint_id = '<tenprintid>', pc = s[ 'fpc' ] ) }}".replace( "%3Ctenprintid%3E", tpid[ "{{ s[ 'fpc' ] }}" ] );
$( "#segment_{{ s[ 'uuid' ] }}" ).css( "background-image", "url( " + url + " )" );
{% endfor %}
$( "#icnml_new_segment" ).on( "click", add_new_segment );
$( "#icnml_navigation_afis" )
.addClass( "activated" );
$( "#navloc" ).append(
$( "<span />" ).text( "Submissions" )
);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
{% for src in js %}
<script type="text/javascript" src="{{ src }}"></script>
{% endfor %}
{% for src in css %}
<link type="text/css" rel="stylesheet" href="{{ src }}">
{% endfor %}
<script type="text/javascript" src="{{ url_for( 'files.send_app_files', subpath = 'functions.js' ) }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for( 'files.send_app_files', subpath = 'app.css' ) }}">
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var new_folder = function()
{
var create_new_folder = function()
{
var selected = $( "#donor_select > option:checked" ).val() || null;
$.ajax( {
url: "{{ url_for( 'afis.admin_add_new_folder' ) }}",
dataType: "json",
method: "POST",
data: {
donor_uuid: selected
},
success: function( data )
{
if( ! data.error )
{
toastr.success( "Folder added" );
setTimeout( function(){ window.location.reload(); }, 500 );
} else {
toastr.error( "Error while creating the folder" );
}
$( "#new_folder" ).remove();
}
} );
}
var donor_select = $( "<select >" )
.attr( "id", "donor_select" )
donor_select.append( $( "<option />" ) );
{% for d in donors %}
donor_select.append(
$( "<option />" )
.attr( "value", "{{ d[ 'uuid' ] }}" )
.text( "{{ d[ 'username' ] }}" )
);
{% endfor %}
var donor_list = $( "<div />" )
.css( "margin-top", "5px" )
.append( donor_select );
$( "<div />" )
.attr( "id", "new_folder" )
.text( "Link the AFIS search to the following donor:" )
.append( donor_list )
.dialog( {
title: "new AFIS folder",
modal: true,
buttons: {
"OK": create_new_folder,
"Cancel": function()
{
$( this ).dialog( "close" );
},
},
close: function()
{
$( this ).remove();
}
} );
}
var folders = {};
{% for f in folders %}
folders[ "{{ f[ 'uuid' ] }}" ] = {
"uuid": "{{ f[ 'uuid' ] }}",
};
{% endfor %}
</script>
<style type="text/css">
.icnml_next_buttons {
margin-top: 20px;
padding-left: 50px;
padding-right: 50px;
}
.icnml_next_button {
width: 100%;
margin-bottom: 5px;
}
#icnml_folder_list {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat( auto-fit, minmax( 200px, max-content ) );
grid-auto-rows: min-content;
}
.icnml_folder_button {
width: 120px;
height: 120px;
}
.icnml_folder_button > span {
height: 120px;
line-height: 120px;
vertical-align: center;
text-align: center;
}
.icnml_new_folder_button {
cursor: pointer;
}
.icnml_new_folder_button > span {
line-height: 132px;
font-size: 4.3em;
}
.icnml_new_folder_button {
margin-top: 0px;
}
.icnml_search_bar {
margin-bottom: 20px;
width: 500px;
}
</style>
</head>
<body class="icnml_main_layout">
{% include "header.html" %}
{% include navigation %}
<div class="icnml_content">
<div class="icnml_search_bar">
<input id="search_bar" placeholder="Search for a case..."/>
</div>
<div id="icnml_folder_list">
{% for f in folders %}
<div style="margin-bottom: 20px">
<div class="ui-widget-header ui-corner-top icnml_box_top">{{ f[ 'username' ] }}</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only icnml_folder_button" id="edit_{{ f[ 'uuid' ] }}_button" role="button" aria-disabled="false">
<span>View</span>
</a>
</div>
</div>
</div>
{% endfor %}
<div style="margin-bottom: 20px">
<div class="ui-widget-header ui-corner-top icnml_box_top">New</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button icnml_new_folder_button" id="icnml_new_folder_button">
<span>+</span>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
{% for f in folders %}
$( "#edit_{{ f[ 'uuid' ] }}_button" ).on( "click", function(){ window.location = "{{ url_for( 'afis.admin_folder_show', folder_id = f[ 'uuid' ] ) }}"; } );
{% endfor %}
$( "#icnml_new_folder_button" ).on( "click", new_folder );
$( "#icnml_navigation_afis" )
.addClass( "activated" );
$( "#navloc" ).append(
$( "<span />" ).text( "Submissions" )
);
</script>
</body>
</html>
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import base64
from datetime import datetime
from email.mime.text import MIMEText
from uuid import uuid4
import base64
import hashlib
import json
from uuid import uuid4
from flask import Blueprint
from flask import current_app, request, jsonify, session, url_for, redirect
import webauthn
import config
from functions import mySMTP
import utils
import views.pianos
import utils
from utils.decorator import admin_required
from utils.template import my_render_template
from functions import mySMTP
newuser_view = Blueprint( "newuser", __name__, template_folder = "templates" )
......@@ -273,9 +275,7 @@ def do_validate_signin_2():
"error": False
} )
except Exception as e:
print e
except:
return jsonify( {
"error": True,
"message": "Error while sending the email"
......@@ -324,8 +324,7 @@ def config_new_user( uuid ):
next_step = "newuser.do_config_new_user"
)
except Exception as e:
print e
except:
return redirect( url_for( "base.home" ) )
@newuser_view.route( "/do/config", methods = [ "POST" ] )
......
......@@ -2,20 +2,11 @@
# -*- coding: UTF-8 -*-
from flask import Blueprint
from flask import current_app, jsonify
from flask import current_app
from PIL import Image
from PiAnoS import caseExistsInDB
from functions import do_decrypt_dek
from utils.decorator import admin_required
from utils.template import my_render_template
from views.images import str2img
import config
pianos_view = Blueprint( "pianos", __name__, template_folder = "templates" )
@pianos_view.route( "/pianos_api" )
......@@ -28,108 +19,3 @@ def pianos_actions():
return my_render_template( "PiAnoS/actions.html" )
@pianos_view.route( "/pianos_api/add_user/all" )
@admin_required
def pianos_update_all_accounts():
"""
serve the function to update the users in PiAnoS
"""
current_app.logger.info( "Copy all accounts to PiAnoS" )
return jsonify( {
"error": not do_pianos_update_all_accounts()
} )
def do_pianos_update_all_accounts():
"""
Copy/update the credentials for all users.
This function keep the credentials in sync between ICNML and PiAnoS.
"""
try:
sql = """
SELECT users.username, users.password, account_type.name as g
FROM users
LEFT JOIN account_type ON users.type = account_type.id
WHERE users.password IS NOT NULL
"""
nb = 0
for user in config.db.query_fetchall( sql ):
nb += 1
username, h, group_name = user
current_app.logger.debug( "Copy the user '{}' to PiAnoS".format( username ) )
groupid = config.pianosdb.create_group( group_name )
pianos_user_id = config.pianosdb.create_user( username = username, hash = h, groupid = groupid )
config.pianosdb.reset_user( username, hash = h )
config.pianosdb.create_folder( "{}'s folder".format( username ), pianos_user_id, None, pianos_user_id )
config.pianosdb.commit()
current_app.logger.info( "{} users copied to PiAnoS".format( nb ) )
return True
except:
return False
@pianos_view.route( "/pianos_api/add_segments/all" )
@admin_required
def pianos_copy_all_segments():
"""
Route to push all segments to PiAnoS.
"""
current_app.logger.info( "Copy all segments to PiAnoS" )
return jsonify( {
"error": not do_pianos_copy_all_segments()
} )
def do_pianos_copy_all_segments():
"""
Copy all segments images to PiAnoS. If the case already exists, the image is not pushed to PiAnoS.
"""
try:
img = Image.new( "L", ( 200, 200 ), 255 )
empty_img_res = 500
empty_img_id = config.pianosdb.create_image( "PRINT", img, empty_img_res, "empty" )
sql = """
SELECT
files_segments.uuid,
files_segments.data,
files_segments.pc,
files_v.resolution,
submissions.id as submissionid,
submissions.uuid as submissionuuid
FROM files_segments
LEFT JOIN files_v ON files_segments.tenprint = files_v.uuid
LEFT JOIN submissions ON files_v.folder = submissions.id
"""
for segment in config.db.query_fetchall( sql ):
data = do_decrypt_dek( segment[ "data" ], segment[ "submissionuuid" ] )
img = str2img( data )
current_app.logger.debug( "{}: {}".format( segment[ "uuid" ], img ) )
try:
folder_id = config.pianosdb.create_folder( "submission {}".format( segment[ "submissionid" ] ) )
case_name = "submission {} segment {}".format( segment[ "submissionid" ], segment[ "pc" ] )
config.pianosdb.create_exercise(
folder_id,
case_name, "",
img, segment[ "resolution" ],
empty_img_id, empty_img_res
)
except caseExistsInDB:
continue
except:
raise
config.pianosdb.commit()
return True
except:
return False
......@@ -13,65 +13,6 @@
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var update_all = function()
{
$.ajax( {
url: "{{ url_for( 'pianos.pianos_update_all_accounts' ) }}",
dataType: "json",
success: function( data )
{
if( ! data.error )
{
toastr.success( "PiAnoS updated" );
} else {
toastr.error( "Error while updating" );
}
},
error: function()
{
toastr.error( "Network error" );
}
} );
}
var copy_all_segments = function()
{
$( "#pianos_copy_all_segments_button" )
.removeClass( "ui-state-default" )
.addClass( "ui-state-disabled" );
$.ajax( {
url: "{{ url_for( 'pianos.pianos_copy_all_segments' ) }}",
dataType: "json",
success: function( data )
{
if( ! data.error )
{
toastr.success( "Segments copied to PiAnoS" );
$( "#pianos_copy_all_segments_button" )
.addClass( "ui-state-default" )
.removeClass( "ui-state-disabled" );
} else {
toastr.error( "Error while copying the segments" );
$( "#pianos_copy_all_segments_button" )
.addClass( "ui-state-default" )
.removeClass( "ui-state-disabled" );
}
},
error: function()
{
toastr.error( "Network error" );
$( "#pianos_copy_all_segments_button" )
.addClass( "ui-state-default" )
.removeClass( "ui-state-disabled" );
}
} );
}
</script>
</head>
<body class="icnml_main_layout">
......@@ -79,24 +20,19 @@
{% include navigation %}
<div class="icnml_content">
<div id="pianos_update_all_accounts_button_div">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="pianos_update_all_accounts_button" role="button" aria-disabled="false">
<span class="ui-button-text" id="pianos_update_all_accounts_button_span_text">Copy all accounts to PiAnoS</span>
</a>
</div>
<div id="pianos_copy_all_segments_button_div">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="pianos_copy_all_segments_button" role="button" aria-disabled="false">
<span class="ui-button-text" id="pianos_copy_all_segments_button_span_text">Copy all segment images to PiAnoS</span>
<div id="open_pianos_div">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="open_pianos_button" role="button" aria-disabled="false">
<span class="ui-button-text" id="open_pianos_span">Open PiAnoS</span>
</a>
</div>
</div>
<script type="text/javascript">
$( "#pianos_update_all_accounts_button" )
.on( "click", update_all );
$( "#pianos_copy_all_segments_button" )
.on( "click", copy_all_segments )
$( "#open_pianos_button" )
.on( "click", function()
{
window.location = baseurl + "/pianos";
} );
$( "#icnml_navigation_pianos" )
.addClass( "activated" );
......
......@@ -482,8 +482,7 @@ def submission_upload_tenprintmark( submission_id ):
return redirect( url_for( "submission.submission_consent_form", submission_id = submission_id ) )
except Exception as e:
print e
except:
return jsonify( {
"error": True,
"message": "Case not found"
......@@ -802,16 +801,18 @@ def submission_mark( submission_id, mark_id ):
mark[ "filename" ] = do_decrypt_user_session( mark[ "filename" ] )
mark[ "file_type" ] = mark[ "file_type" ].replace( "mark_", "" )
all_detection_tetchnics = []
sql = "SELECT * FROM detection_technics ORDER BY name ASC"
for t in config.db.query_fetchall( sql ):
all_detection_tetchnics.append( dict( t ) )
all_detection_tetchnics = config.db.query_fetchall( sql )
all_surfaces = []
sql = "SELECT * FROM surfaces ORDER BY name ASC"
for t in config.db.query_fetchall( sql ):
all_surfaces.append( dict( t ) )
all_surfaces = config.db.query_fetchall( sql )
sql = "SELECT * FROM activities ORDER BY name ASC"
all_activities = config.db.query_fetchall( sql )
sql = "SELECT * FROM distortion ORDER BY name ASC"
all_distortions = config.db.query_fetchall( sql )
try:
sql = "SELECT detection_technic FROM mark_info WHERE uuid = %s"
detection_technics = config.db.query_fetchone( sql, ( mark_id, ) )[ "detection_technic" ]
......@@ -832,15 +833,39 @@ def submission_mark( submission_id, mark_id ):
except:
surface = []
return my_render_template(
try:
sql = "SELECT activity FROM mark_info WHERE uuid = %s"
activity = config.db.query_fetchone( sql, ( mark_id, ) )[ "activity" ]
for old in "{}'":
activity = activity.replace( old, "" )
activity = activity.split( "," )
except:
activity = []
try:
sql = "SELECT distortion FROM mark_info WHERE uuid = %s"
distortion = config.db.query_fetchone( sql, ( mark_id, ) )[ "distortion" ]
for old in "{}'":
distortion = distortion.replace( old, "" )
distortion = distortion.split( "," )
except:
distortion = []
return my_render_template(
"submission/mark.html",
submission_id = submission_id,
nickname = nickname,
file = mark,
all_detection_technics = all_detection_tetchnics,
all_surfaces = all_surfaces,
all_activities = all_activities,
all_distortions = all_distortions,
detection_technics = detection_technics,
surface = surface
surface = surface,
activity = activity,
distortion = distortion
)
@submission_view.route( "/submission/<submission_id>/mark/<mark_id>/pfsp" )
......@@ -941,10 +966,18 @@ def submission_mark_set_field( submission_id, mark_id, field ):
"""
Set the data related to the <field> (detection technic, surface, ...) for a fingermark.
"""
if field in [ "detection_technic", "surface" ]:
corr = {
"surface": "surface",
"detection": "detection_technic",
"activity": "activity",
"distortion": "distortion"
}
field = corr.get( field, False )
if field != False:
current_app.logger.info( "Save the {} for submission '{}' mark '{}'".format( field, submission_id, mark_id ) )
dt = json.loads( request.form.get( field ) )
dt = json.loads( request.form.get( "value" ) )
sql = "SELECT id FROM mark_info WHERE uuid = %s"
q = config.db.query_fetchone( sql, ( mark_id, ) )
......@@ -968,22 +1001,38 @@ def submission_mark_set_field( submission_id, mark_id, field ):
"error": True
} )
@submission_view.route( "/add_new_surface", methods = [ "POST" ] )
@submission_view.route( "/add_new_field", methods = [ "POST" ] )
@login_required
def add_new_surface():
def add_new_field():
"""
Add a new mark surface to the database.
"""
new_surface = request.form.get( "surface" )
current_app.logger.info( "Add '{}' to the surface database".format( new_surface ) )
new_field = request.form.get( "field" )
new_field_name = request.form.get( "field_name" )
sql = utils.sql.sql_insert_generate( "surfaces", "name", "id" )
surface_id = config.db.query_fetchone( sql, ( new_surface, ) )[ "id" ]
return jsonify( {
"error": False,
"id": surface_id
} )
corr = {
"surface": "surfaces",
"activity": "activities"
}
table = corr.get( new_field_name, False )
if table != False:
current_app.logger.info( "Add '{}' to the {} database".format( new_field, new_field_name ) )
sql = utils.sql.sql_insert_generate( table, "name", "id" )
current_app.logger.info( sql )
field_id = config.db.query_fetchone( sql, ( new_field, ) )[ "id" ]
config.db.commit()
return jsonify( {
"error": False,
"id": field_id
} )
else:
return jsonify( {
"error": True
} )
@submission_view.route( "/submission/<submission_id>/mark/<mark_id>/delete" )
@submission_has_access
......@@ -1501,11 +1550,7 @@ def submission_segment_setcoordinates( submission_id, tenprint_id ):
"id": seg_data_id,
} )
except Exception as e:
print e
from traceback import print_exc
print_exc()
except:
return jsonify( {
"error": True
} )
......
......@@ -28,23 +28,24 @@
var nickname = decrypt( "{{ nickname }}", password_local );
var filename = decrypt( "{{ file[ 'filename' ] }}", password_local );
var new_surface = function()
var new_field = function( field_name )
{
var add_new_surface_to_db = function()
var add_new_field_to_db = function()
{
var surface = $( "#new_surface_confirmation_input" ).val() || null;
var field = $( "#new_field_confirmation_input" ).val() || null;
$.ajax( {
url: "{{ url_for( 'submission.add_new_surface' ) }}",
url: "{{ url_for( 'submission.add_new_field' ) }}",
dataType: "json",
method: "POST",
data: {
surface: surface
field: field,
field_name: field_name
},
success: function( data )
{
$( "#delete_confirmation" ).remove();
window.location.reload()
window.location.reload();
},
error: function( data )
{
......@@ -54,18 +55,18 @@
};
$( "<div />" )
.attr( "id", "new_surface_confirmation" )
.text( "What is the new surface you'd like to add?" )
.attr( "id", "new_field_confirmation" )
.text( "What is the new " + field_name + " you'd like to add?" )
.append(
$( "<input />" )
.attr( "id", "new_surface_confirmation_input" )
.attr( "id", "new_field_confirmation_input" )
.css( "margin-top", "10px" )
)
.dialog( {
title: "New surface",
title: "New " + field_name,
modal: true,
buttons: {
"OK": add_new_surface_to_db,
"OK": add_new_field_to_db,
"Cancel": function()
{
$( this ).dialog( "close" );
......@@ -77,15 +78,15 @@
}
} );
$( "#new_surface_confirmation_input" ).on( "keypress", function( event )
$( "#new_field_confirmation_input" ).on( "keypress", function( event )
{
if( event.keyCode == 13 )
{
add_new_surface_to_db();
add_new_field_to_db();
}
} );
$( "#new_surface_confirmation_input" ).focus();
$( "#new_field_confirmation_input" ).focus();
}
var update_note_db = function()
......@@ -102,38 +103,20 @@
} );
}
var update_detection_db = function()
var update_field_db = function( field )
{
var detection_technic = [];
var field_value = [];
$.each( $( "#file_{{ file[ 'uuid' ] }}_detection > select > option:checked" ), function() {
detection_technic.push( $( this ).val() );
$.each( $( "#file_{{ file[ 'uuid' ] }}_" + field + " > select > option:checked" ), function() {
field_value.push( $( this ).val() );
} );
$.ajax( {
url: "{{ url_for( 'submission.submission_mark_set_field', submission_id = submission_id, mark_id = file[ 'uuid' ], field = 'detection_technic' ) }}",
url: "{{ url_for( 'submission.submission_mark_set_field', submission_id = submission_id, mark_id = file[ 'uuid' ], field = '<field>' ) }}".replace( "%3Cfield%3E", field ),
dataType: "json",
method: "POST",
data: {
detection_technic: JSON.stringify( detection_technic )
}
} );
}
var update_surface_db = function()
{
var surface = [];
$.each( $( "#file_{{ file[ 'uuid' ] }}_surface > select > option:checked" ), function() {
surface.push( $( this ).val() );
} );
$.ajax( {
url: "{{ url_for( 'submission.submission_mark_set_field', submission_id = submission_id, mark_id = file[ 'uuid' ], field = 'surface' ) }}",
dataType: "json",
method: "POST",
data: {
surface: JSON.stringify( surface )
value: JSON.stringify( field_value )
}
} );
}
......@@ -249,6 +232,33 @@
</div>
</div>
<div>Activity</div>
<div>
<div id="file_{{ file[ 'uuid' ] }}_activity">
<select multiple id="file_{{ file[ 'uuid' ] }}_activity_select" data-placeholder="select..." class="chosen-select">
{% for a in all_activities %}
<option value="{{ a[ 'id' ] }}">{{ a[ 'name' ] }}</option>
{% endfor %}
</select>
</div>
<div style="text-align: right;">
<a id="add_a_new_activity" style="cursor: pointer;">Add a new activity</a>
</div>
</div>
<div>Distortion</div>
<div>
<div id="file_{{ file[ 'uuid' ] }}_distortion">
<select multiple id="file_{{ file[ 'uuid' ] }}_distortion_select" data-placeholder="select..." class="chosen-select">
{% for d in all_distortions %}
<option value="{{ d[ 'id' ] }}">{{ d[ 'name' ] }}</option>
{% endfor %}
</select>
</div>
<div style="text-align: right;">
<a id="add_a_new_distortion" style="cursor: pointer;">Add a new distortion</a>
</div>
</div>
<div>Notes</div>
<div><textarea rows="6" id="file_note">{{ file[ 'note' ] }}</textarea></div>
</div>
......@@ -276,27 +286,25 @@
$( "#box_filename" ).text( filename );
$( "#file_note" ).on( "change", update_note_db );
$( "#file_{{ file[ 'uuid' ] }}_detection > select" ).on( "change", update_detection_db )
$( "#file_{{ file[ 'uuid' ] }}_surface > select" ).on( "change", update_surface_db )
_.forEach( [ "detection", "surface", "activity", "distortion" ], function( f ){
$( "#file_{{ file[ 'uuid' ] }}_" + f + " > select" ).on( "change", function(){ update_field_db( f ); } )
} );
$( "#file_{{ file[ 'uuid' ] }}_uploadtime" )
.text( moment.utc( "{{ file[ 'creation_time' ] }}" ).local().format( "MMMM Do YYYY, HH:mm:ss" ) );
$( "#next_button" ).on( "click", next_information );
$( "#delete_button" ).on( "click", delete_mark );
$( "#file_{{ file[ 'uuid' ] }}_detection_select" )
.chosen( {
search_contains: true,
width: "100%"
} );
$( "#file_{{ file[ 'uuid' ] }}_surface_select" )
.chosen( {
search_contains: true,
width: "100%"
} );
_.forEach( [ "detection", "surface", "activity", "distortion" ], function( f )
{
$( "#file_{{ file[ 'uuid' ] }}_" + f + "_select" )
.chosen( {
search_contains: true,
width: "100%"
} );
} );
var detection_technics = []
{% for dt in detection_technics %}
detection_technics.push( "{{ dt }}" );
......@@ -309,7 +317,21 @@
{% endfor %}
$( "#file_{{ file[ 'uuid' ] }}_surface_select" ).val( surface ).trigger( "chosen:updated" );
$( "#add_a_new_surface" ).on( "click", new_surface );
var activity = []
{% for a in activity %}
activity.push( "{{ a }}" );
{% endfor %}
$( "#file_{{ file[ 'uuid' ] }}_activity_select" ).val( activity ).trigger( "chosen:updated" );
var distortion = []
{% for a in distortion %}
distortion.push( "{{ a }}" );
{% endfor %}
$( "#file_{{ file[ 'uuid' ] }}_distortion_select" ).val( distortion ).trigger( "chosen:updated" );
_.forEach( [ "surface", "activity", "distortion" ], function( f ){
$( "#add_a_new_" + f ).on( "click", function(){ new_field( f ); } );
} );
$( "#icnml_navigation_updatedonor" )
.addClass( "activated" );
......
......@@ -373,6 +373,24 @@ def add_users_to_folder( folder_id ):
"error": True
} )
@trainer_view.route( "/exercises/<folder_id>/trainee/remove", methods = [ "POST" ] )
@trainer_has_access
def remove_user_from_folder( folder_id ):
user_id = request.form.get( "user_id", None )
try:
sql = "DELETE FROM exercises_trainee_list WHERE user_id = %s AND folder = %s"
config.db.query( sql, ( user_id, folder_id, ) )
config.db.commit()
return jsonify( {
"error": False,
} )
except:
return jsonify( {
"error": True
} )
@trainer_view.route( "/exercises/get/current_folder_id" )
@trainer_has_access
def get_current_folder_id():
......
......@@ -185,6 +185,28 @@
} );
}
var remove_user = function( user_id )
{
$.ajax( {
url: "{{ url_for( 'trainer.remove_user_from_folder', folder_id = folder ) }}",
dataType: "json",
method: "POST",
data: {
"user_id": user_id
},
success: function( data )
{
if( !data.error )
{
toastr.success( "User deleted from folder" );
setTimeout( function(){ window.location.reload(); }, 1000 );
} else {
toastr.error( "User not deleted", "Server side error" );
}
}
} );
}
</script>
</head>
<body class="icnml_main_layout">
......@@ -202,11 +224,17 @@
<tr>
<th>Username</th>
<th>Email</th>
<th>Actions</th>
</tr>
{% for trainee in trainee_list %}
<tr>
<td>{{ trainee[ 'username' ] }}</td>
<td>{{ trainee[ 'email' ] }}</td>
<td>
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="remove_user_{{ trainee[ 'user_id' ] }}" role="button" aria-disabled="false">
<span class="ui-button-text" id="remove_users_button_text">Remove</span>
</a>
</td>
</tr>
{% endfor %}
</table>
......@@ -215,6 +243,12 @@
<script type="text/javascript">
$( "#add_users" ).on( "click", add_users_dialog );
{% for trainee in trainee_list %}
$( "#remove_user_{{ trainee[ 'user_id' ] }}" ).on( "click", function(){
remove_user( "{{ trainee[ 'user_id' ] }}" )
} );
{% endfor %}
$( "#icnml_navigation_exercises" )
.addClass( "activated" );
......