Skip to content
Commits on Source (12)
......@@ -58,6 +58,21 @@ CREATE SEQUENCE public.username_trainer_seq
ALTER TABLE public.username_trainer_seq OWNER TO icnml;
ALTER SEQUENCE public.username_trainer_seq OWNED BY NONE;
--
-- Name: username_trainee_seq; Type: SEQUENCE; Schema: public; Owner: icnml
--
CREATE SEQUENCE public.username_trainee_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.username_trainee_seq OWNER TO icnml;
ALTER SEQUENCE public.username_trainee_seq OWNED BY NONE;
--
-- Name: username_afis_seq; Type: SEQUENCE; Schema: public; Owner: icnml
--
......@@ -87,3 +102,5 @@ CREATE SEQUENCE public.username_selection_seq
ALTER TABLE public.username_selection_seq OWNER TO icnml;
ALTER SEQUENCE public.username_selection_seq OWNED BY NONE;
--
-- 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: exercises_trainee_list; Type: TABLE; Schema: public; Owner: icnml
--
CREATE TABLE public.exercises_trainee_list (
id integer NOT NULL,
"user" integer NOT NULL,
folder uuid NOT NULL
);
ALTER TABLE public.exercises_trainee_list OWNER TO icnml;
--
-- Name: exercises_trainee_list_id_seq; Type: SEQUENCE; Schema: public; Owner: icnml
--
CREATE SEQUENCE public.exercises_trainee_list_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.exercises_trainee_list_id_seq OWNER TO icnml;
--
-- Name: exercises_trainee_list_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: icnml
--
ALTER SEQUENCE public.exercises_trainee_list_id_seq OWNED BY public.exercises_trainee_list.id;
--
-- Name: exercises_trainee_list id; Type: DEFAULT; Schema: public; Owner: icnml
--
ALTER TABLE ONLY public.exercises_trainee_list ALTER COLUMN id SET DEFAULT nextval('public.exercises_trainee_list_id_seq'::regclass);
--
-- Name: exercises_trainee_list_id_seq; Type: SEQUENCE SET; Schema: public; Owner: icnml
--
SELECT pg_catalog.setval('public.exercises_trainee_list_id_seq', 1, true);
--
-- PostgreSQL database dump complete
--
......@@ -2,6 +2,9 @@
<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>
......
......@@ -1572,12 +1572,7 @@ def admin_submission_list():
LEFT JOIN users ON submissions.donor_id = users.id
ORDER BY created_time DESC
"""
q = config.db.query_fetchall( sql )
donors = []
for donor in q:
donors.append( donor )
current_app.logger.debug( "uuid: {}".format( donor[ "uuid" ] ) )
donors = config.db.query_fetchall( sql )
current_app.logger.info( "{} submissions found".format( len( donors ) ) )
......@@ -1586,6 +1581,51 @@ def admin_submission_list():
donors = donors
)
@submission_view.route( "/admin/submission/table" )
@admin_required
def admin_submission_table():
sql = """
SELECT
submissions.id,
submissions.uuid,
users.username
FROM submissions
LEFT JOIN users ON submissions.donor_id = users.id
ORDER BY created_time DESC
"""
donors = config.db.query_fetchall( sql )
sql = """
SELECT
submissions.uuid,
files_type.name,
count( files.uuid ) AS nb
FROM files
INNER JOIN submissions ON files.folder = submissions.id
INNER JOIN files_type ON files.type = files_type.id
GROUP BY submissions.uuid, files_type.name
"""
counts = config.db.query_fetchall( sql )
sql = """
SELECT
submissions.uuid,
count( * ) AS nb
FROM files_segments
INNER JOIN files ON files_segments.tenprint = files.uuid
INNER JOIN submissions ON files.folder = submissions.id
GROUP BY submissions.uuid
"""
segments = config.db.query_fetchall( sql )
current_app.logger.info( "{} submissions found".format( len( donors ) ) )
return my_render_template(
"admin/submission_table.html",
donors = donors,
counts = counts,
segments = segments
)
@submission_view.route( "/admin/<submission_id>/tenprint/list" )
@admin_required
def admin_tenprint_list( submission_id = "all" ):
......
<!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>
#submissions_list > table {
width: 100%;
}
tr:nth-child( even ) {
background: rgb( 200, 200, 200 );
}
tr:nth-child( odd ) {
background: rgb( 222, 222, 222 );
}
</style>
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var files_type_counts = {};
{% for donor in donors %}
files_type_counts[ "{{ donor[ 'uuid' ] }}" ] = {};
{% endfor %}
{% for c in counts %}
files_type_counts[ "{{ c[ 'uuid' ] }}" ][ "{{ c[ 'name' ] }}" ] = "{{ c[ 'nb' ] }}";
{% endfor %}
{% for s in segments %}
files_type_counts[ "{{ s[ 'uuid' ] }}" ][ "segments" ] = "{{ s[ 'nb' ] }}";
{% endfor %}
</script>
</head>
<body class="icnml_main_layout">
{% include "header.html" %}
{% include navigation %}
<div class="icnml_content">
<div id="submissions_list">
<table>
<tr>
<th>Donor</th>
<th>UUID</th>
<th>Marks</th>
<th>TP finger</th>
<th>TP palms</th>
<th>Segments</th>
</tr>
{% for donor in donors %}
<tr>
<td>{{ donor[ 'username' ] }}</td>
<td>{{ donor[ 'uuid' ] }}</td>
{% for c in [ 'mark_target', 'tenprint_card_front', 'tenprint_card_back', 'segments' ] %}
<td><span id="c_{{ donor[ 'uuid' ] }}_{{ c }}"></span></td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</div>
<script type="text/javascript">
{% for donor in donors %}
{% for c in [ 'mark_target', 'tenprint_card_front', 'tenprint_card_back', 'segments' ] %}
$( "#c_{{ donor[ 'uuid' ] }}_{{ c }}" ).text( files_type_counts[ "{{ donor[ 'uuid' ] }}" ][ "{{ c }}" ] );
{% endfor %}
{% endfor %}
$( "#icnml_navigation_adminsubmissionstable" )
.addClass( "activated" );
$( "#navloc" ).append(
$( "<span />" ).text( "Submissions" )
);
</script>
</body>
</html>
......@@ -253,9 +253,34 @@ def show_folder_content( folder_id ):
"""
mark_list = config.db.query_fetchall( sql, ( folder_id, ) )
sql = "SELECT name FROM exercises WHERE uuid = %s"
folder_name = config.db.query_fetchone( sql, ( folder_id, ) )[ "name" ]
return my_render_template(
"trainer/folder_show.html",
folder = folder_id,
folder_name = folder_name,
mark_list = mark_list
)
@trainer_view.route( "/exercises/<folder_id>/trainee" )
@trainer_has_access
def show_folder_trainee( folder_id ):
sql = "SELECT name FROM exercises WHERE uuid = %s"
folder_name = config.db.query_fetchone( sql, ( folder_id, ) )[ "name" ]
sql = """
SELECT *
FROM users
INNER JOIN exercises_trainee_list ON users.id = exercises_trainee_list.user
WHERE exercises_trainee_list.folder = %s
"""
trainee_list = config.db.query_fetchall( sql, ( folder_id, ) )
return my_render_template(
"trainer/trainee.html",
folder = folder_id,
folder_name = folder_name,
trainee_list = trainee_list
)
......@@ -182,7 +182,8 @@
ex_list[ "{{ ex[ 'uuid' ] }}" ] = {
"uuid": "{{ ex[ 'uuid' ] }}",
"name": "{{ ex[ 'name' ] }}",
"creation": "{{ ex[ 'creationtime' ] }}"
"creation": "{{ ex[ 'creationtime' ] }}",
"username": "{{ ex[ 'username' ] | default( "-", true ) }}"
}
{% endfor %}
</script>
......@@ -241,6 +242,9 @@
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="show_case_{{ ex[ 'uuid' ] }}" role="button" aria-disabled="false">
<span class="ui-button-text" id="show_case_{{ ex[ 'uuid' ] }}_button_text">Show</span>
</a>
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="trainee_case_{{ ex[ 'uuid' ] }}" role="button" aria-disabled="false">
<span class="ui-button-text" id="trainee_case_{{ ex[ 'uuid' ] }}_button_text">Users</span>
</a>
</td>
</tr>
{% endfor %}
......@@ -262,6 +266,11 @@
{
window.location = "{{ url_for( 'trainer.show_folder_content', folder_id = ex[ 'uuid' ] ) }}";
} )
$( "#trainee_case_{{ ex[ 'uuid' ] }}" ).on( "click", function()
{
window.location = "{{ url_for( 'trainer.show_folder_trainee', folder_id = ex[ 'uuid' ] ) }}";
} )
{% endfor %}
$( "#new_exercise_button" ).on( "click", create_new_exercise );
......
......@@ -57,7 +57,7 @@
{% include navigation %}
<div class="icnml_content">
<div style="margin-bottom: 20px;">Folder '{{ folder }}'</div>
<div style="margin-bottom: 20px;">Folder '{{ folder_name }}'</div>
<div class="mark_outer">
{% for mark in mark_list %}
<div>
......@@ -89,6 +89,18 @@
$( "#icnml_navigation_exercises" )
.addClass( "activated" );
$( "#navloc" ).append(
$( "<a />" )
.attr( "href", "{{ url_for( 'trainer.exercises_list' ) }}" )
.text( "Folder" )
)
.append(
$( "<span />" ).text( ">" )
)
.append(
$( "<span />" ).text( "{{ folder_name }}" )
);
</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' ) }}">
<style>
.left_button {
position: absolute;
left: 20px;
}
.icnml_search_bar {
margin-bottom: 20px;
width: 500px;
}
#trainee_list > table {
width: 100%;
}
tr:nth-child( even ) {
background: rgb( 200, 200, 200 );
}
tr:nth-child( odd ) {
background: rgb( 222, 222, 222 );
}
textarea {
padding: 10px;
}
</style>
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var get_help_add_new_users = function()
{
$( "<div />" )
.attr( "id", "new_users_help" )
.append(
$( "<div />" )
.css( "margin-bottom", "10px" )
.text( "To add a list of users to a particular list of cases, simply paste in the field a list of emails addresses separated by a new line, as follows:" )
)
.append(
$( "<textarea />" )
.css( "margin-bottom", "10px" )
.attr( "rows", "5" )
.text( "user1@police.state\nsecond.user@police.state\n" )
)
.append(
$( "<div />" )
.text( "By clicking the 'Add new users' button, a set of users account will be generated into ICNML. On the page describing the exercise, you will be able to send the invitations emails to all users. It's recommended to add yourself to the self of users, even if you dont want to complete the set of cases, to check that the reception of invitations email has been done correctly. You can always add new users." )
)
.dialog( {
title: "Add new users",
width: $( window ).width() * 0.5,
height: "auto",
modal: true,
buttons: {
"Got it!": function()
{
$( this ).dialog( "close" );
},
},
close: function()
{
$( this ).remove();
}
} );
}
var process_new_users = function()
{
var users_list = $( "#new_users_confirmation_area" ).val() || "";
users_list = users_list.split( "\n" );
console.log( "process new users" );
console.log( users_list );
}
var count_new_users = function()
{
var users_list = $( "#new_users_confirmation_area" ).val() || "";
users_list = users_list.split( "\n" );
nb = 0;
_.forEach( users_list, function( user )
{
if( user !== "" )
nb += 1;
} );
if( nb == 0 )
{
$( "#add_new_users_button" ).hide();
} else {
$( "#add_new_users_button" ).show();
if( nb == 1 )
$( "#add_new_users_button" ).text( "Add " + nb + " new user" );
else if( nb >= 2 )
$( "#add_new_users_button" ).text( "Add " + nb + " new users" );
}
}
var add_users_dialog = function()
{
$( "<div />" )
.attr( "id", "new_users_confirmation" )
.append(
$( "<div />" )
.text( "List of users to add to this folder:" )
)
.append(
$( "<textarea />" )
.attr( "id", "new_users_confirmation_area" )
.attr( "rows", "15" )
.css( "margin-top", "10px" )
.css( "width", "calc( 100% - 20px )" )
)
.dialog( {
title: "Add new users",
width: $( window ).width() * 0.5,
height: "auto",
modal: true,
buttons: {
help: {
class: "left_button",
text: "Get help!",
click: get_help_add_new_users
},
newusers: {
id: "add_new_users_button",
text: "Add new users",
click: process_new_users
},
"Cancel": function()
{
$( this ).dialog( "close" );
},
},
open: function()
{
$( "#new_users_confirmation_area" ).focus();
$( "#new_users_confirmation_area" ).keyup( count_new_users );
$( "#add_new_users_button" ).hide();
},
close: function()
{
$( this ).remove();
}
} );
}
</script>
</head>
<body class="icnml_main_layout">
{% include "header.html" %}
{% include navigation %}
<div class="icnml_content">
<div id="actions" style="margin-bottom: 10px;">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="add_users" role="button" aria-disabled="false">
<span class="ui-button-text" id="add_users_button_text">Add new users</span>
</a>
</div>
<div id="trainee_list">
<table>
<tr>
<th>Username</th>
<th>Email</th>
</tr>
{% for trainee in trainee_list %}
<tr>
<td>{{ trainee[ 'username' ] }}</td>
<td>{{ trainee[ 'email' ] }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<script type="text/javascript">
$( "#add_users" ).on( "click", add_users_dialog );
$( "#icnml_navigation_exercises" )
.addClass( "activated" );
$( "#navloc" ).append(
$( "<a />" )
.attr( "href", "{{ url_for( 'trainer.exercises_list' ) }}" )
.text( "Folder" )
)
.append(
$( "<span />" ).text( ">" )
)
.append(
$( "<span />" ).text( "{{ folder_name }}" )
)
.append(
$( "<span />" ).text( ">" )
)
.append(
$( "<span />" ).text( "Trainee list" )
);
</script>
</body>
</html>