Skip to content
module.py 143 KiB
Newer Older
                img_format = img.format
                width, height = img.size
                
                app.logger.debug( str( img ) )
                
                try:
                    res = int( img.info[ "dpi" ][ 0 ] )
                    app.logger.debug( "Resolution: {}".format( res ) )
                except:
                    app.logger.error( "No resolution found in the image" )
                        "message": "No resolution found in the image. Upload not possible at the moment."
                try:
                    img = utils.images.rotate_image_upon_exif( img )
                    app.logger.debug( "Rotation of the image" )
                except:
                    pass
                buff = StringIO()
                img.save( buff, format = img_format )
                buff.seek( 0 )
                file_data = buff.getvalue()
                if upload_type in [ "tenprint_card_front", "tenprint_card_back" ]:
                    app.logger.debug( "Creation of the thumbnail" )
                    create_thumbnail( file_uuid, img, submission_uuid )
            
            else:
                file_data = fp.getvalue()
            
            file_data_r = file_data
            file_data = base64.b64encode( file_data )
            
            sql = "SELECT id FROM files_type WHERE name = %s"
            upload_type_id = config.db.query_fetchone( sql, ( upload_type, ) )[ "id" ]
            
            ####################################################################
            
            if upload_type == "consent_form":
                app.logger.info( "Processing of the consent form" )
                sql = "SELECT email_aes FROM submissions WHERE uuid = %s"
                email = config.db.query_fetchone( sql, ( submission_uuid, ) )[ "email_aes" ]
                email = do_decrypt_user_session( email )
Marco De Donno's avatar
Marco De Donno committed
                sql = """
                    SELECT users.username, users.email
                    FROM users
                    LEFT JOIN account_type ON users.type = account_type.id
                    WHERE account_type.name = 'Donor'
                    ORDER BY users.id DESC
                """
                for username_db, email_db in config.db.query_fetchall( sql ):
                    if utils.hash.pbkdf2( email, email_db ).verify():
                        username = username_db
                        url_hash = hashlib.sha512( email_db ).hexdigest()
                        app.logger.info( "Donor: {}".format( username ) )
                        break
                    app.logger.error( "User not found" )
                    return jsonify( {
                        "error": True,
                        "message": "user not found"
                    } )
                # Check that the PDF contains the QRCODE
                qrcode_checked = False
                
                try:
                    pages = pdf2image.convert_from_bytes( 
                        file_data_r,
                        poppler_path = config.POPPLER_PATH
                    )
                    
                    for page in pages:
                        decoded = pyzbar.decode( page )
                        for d in decoded:
                            if d.data == "ICNML CONSENT FORM":
                                qrcode_checked = True
                
                except:
                    qrcode_checked = False
                
                # Email for the donor
                app.logger.info( "Sending the email to the donor" )
                email_content = utils.template.render_jinja_html( 
                    "templates/email", "donor.html",
                    username = username,
                    url = "https://icnml.unil.ch" + url_for( "config_new_user_donor", h = url_hash )
                )
                msg[ "Subject" ] = "ICNML - You have been added as donor"
                msg[ "From" ] = config.sender
                msg[ "To" ] = email
                msg.attach( MIMEText( email_content, "html" ) )
                
                part = MIMEApplication( file_data_r, Name = "consent_form.pdf" )
                part[ "Content-Disposition" ] = "attachment; filename=consent_form.pdf"
                msg.attach( part )
                 
                try:
                    with mySMTP() as s:
                        s.sendmail( config.sender, [ email ], msg.as_string() )
                    app.logger.info( "Email sended" )
                
                except:
                    app.logger.error( "Can not send the email to the donor" )
                    return jsonify( {
                        "error": True,
                        "message": "Can not send the email to the user"
                    } )
                
                    # Consent form save
                    app.logger.info( "Saving the consent form to the database" )
                    file_data = base64.b64encode( file_data )
                    file_data = gpg.encrypt( file_data, *config.gpg_key )
                    file_data = str( file_data )
                    file_data = base64.b64encode( file_data )
                    email_hash = utils.hash.pbkdf2( email, iterations = config.CF_NB_ITERATIONS ).hash()
                    
                    sql = utils.sql.sql_insert_generate( "cf", [ "uuid", "data", "email", "has_qrcode" ] )
                    data = ( file_uuid, file_data, email_hash, qrcode_checked, )
                    config.db.query( sql , data )
                    
                    sql = "UPDATE submissions SET consent_form = true WHERE uuid = %s"
                    config.db.query( sql, ( submission_uuid, ) )
            else:
                app.logger.info( "Save the file to the databse" )
                
                file_data = do_encrypt_dek( file_data, submission_uuid )
                
                sql = utils.sql.sql_insert_generate( "files", [
                    "folder", "creator",
                    "filename", "type",
                    "format", "size", "width", "height", "resolution",
                    "uuid", "data"
                ] )
                data = ( 
                    submission_id, session[ "user_id" ],
                    file_name, upload_type_id,
                    img_format, file_size, width, height, res,
                    file_uuid, file_data,
                )
                config.db.query( sql, data )
                config.db.commit()
            
            return jsonify( {
                "error": False,
                "uuid": file_uuid
            } )
################################################################################
Marco De Donno's avatar
Marco De Donno committed
#    Submission of a new donor
@app.route( baseurl + "/submission/new" )
def submission_new():
    """
        Serve the page to start a new submission (new donor).
    """
    app.logger.info( "Serve the new donor form" )
    return my_render_template( "submission/new.html" )
@app.route( baseurl + "/submission/do_new", methods = [ "POST" ] )
def submission_do_new():
    """
        Check the new donor data, and store the new submission process in the database.
    """
    app.logger.info( "Process the new donor form" )
    
    email = request.form.get( "email", False )
    email = email.lower()
        # Check for duplicate base upon the email data
        sql = "SELECT id, email_hash FROM submissions WHERE submitter_id = %s"
        for case in config.db.query_fetchall( sql, ( session[ "user_id" ], ) ):
            if utils.hash.pbkdf2( email, case[ "email_hash" ] ).verify():
                app.logger.error( "Email already used for an other submission ({}) by this submitter".format( case[ "id" ] ) )
                return jsonify( {
                    "error": True,
                    "message": "Email already used for an other submission. Check the list of submissions to update the corresponding one."
            app.logger.info( "Insertion of the donor to the databse" )
Marco De Donno's avatar
Marco De Donno committed
            # Insert the new donor
            app.logger.debug( "Donor uuid: {}".format( donor_uuid ) )
            email_hash = utils.hash.pbkdf2( email, iterations = config.EMAIL_NB_ITERATIONS ).hash()
            
            upload_nickname = request.form.get( "upload_nickname", None )
            upload_nickname = do_encrypt_user_session( upload_nickname )
            submitter_id = session[ "user_id" ]
            
            status = "pending"
            
Marco De Donno's avatar
Marco De Donno committed
            userid = config.db.query_fetchone( "SELECT nextval( 'username_donor_seq' ) as id" )[ "id" ]
            username = "donor_{}".format( userid )
            sql = utils.sql.sql_insert_generate( "users", [ "username", "email", "type" ], "id" )
            donor_user_id = config.db.query_fetchone( sql, data )[ "id" ]
            
            app.logger.debug( "Username: {}".format( username ) )
            
            dek_salt, dek, dek_check = dek_generate( email = email, username = username )
            app.logger.debug( "DEK salt: {}...".format( dek_salt[ 0:10 ] ) )
            app.logger.debug( "DEK:      {}...".format( dek[ 0:10 ] ) )
            
            sql = utils.sql.sql_insert_generate( "donor_dek", [ "donor_name", "salt", "dek", "dek_check", "iterations", "algo", "hash" ], "id" )
            data = ( username, dek_salt, dek, dek_check, config.DEK_NB_ITERATIONS, "pbkdf2", "sha512", )
            config.db.query_fetchone( sql, data )
            
            sql = utils.sql.sql_insert_generate( "submissions", [ "uuid", "email_aes", "email_hash", "nickname", "donor_id", "status", "submitter_id" ] )
            data = ( donor_uuid, email_aes, email_hash, upload_nickname, donor_user_id, status, submitter_id, )
            config.db.query( sql, data )
            config.db.commit()
            
            return jsonify( {
                "error": False,
        app.logger.error( "No email provided for the submission folder" )
        return jsonify( {
            "error": True,
            "message": "Email not provided"
@app.route( baseurl + "/submission/<submission_id>/add_files" )
def submission_upload_tplp( submission_id ):
        Serve the page to upload tenprint and mark images files.
        This page is not accessible if a consent form is not available in the
        database for this particular donor.
    """
    app.logger.info( "Upload a new file in the submission {}".format( submission_id ) )
        sql = """
            SELECT email_aes as email, nickname, created_time, consent_form
            FROM submissions
            WHERE submitter_id = %s AND uuid = %s
        """
        user = config.db.query_fetchone( sql, ( session[ "user_id" ], submission_id ) )
        if user[ "consent_form" ]:
            app.logger.debug( "The donor has a consent form" )
            app.logger.info( "Serving the add new file page" )
            
            for key in [ "email", "nickname" ]:
                user[ key ] = do_decrypt_user_session( user[ key ] )
Marco De Donno's avatar
Marco De Donno committed
            return my_render_template( 
                submission_id = submission_id,
            app.logger.debug( "The donor dont have a consent form in the database" )
            app.logger.info( "Serving the consent form upload page" )
            
            return redirect( url_for( "submission_consent_form", submission_id = submission_id ) )
        
    except:
        return jsonify( {
            "error": True,
            "message": "Case not found"
@app.route( baseurl + "/submission/<submission_id>/consent_form" )
def submission_consent_form( submission_id ):
    """
        Serve the page to upload the consent form for the user.
    """
    app.logger.info( "Serve the consent form upload page" )
    
Marco De Donno's avatar
Marco De Donno committed
    sql = """
        SELECT email_aes as email, nickname, created_time
        FROM submissions
        WHERE submitter_id = %s AND uuid = %s
    """
    user = config.db.query_fetchone( sql, ( session[ "user_id" ], submission_id ) )
        for key in [ "email", "nickname" ]:
            user[ key ] = do_decrypt_user_session( user[ key ] )
Marco De Donno's avatar
Marco De Donno committed
        return my_render_template( 
            "submission/consent_form.html",
            submission_id = submission_id,
        app.logger.error( "Submission not found" )
@app.route( baseurl + "/submission/<submission_id>/set/nickname", methods = [ "POST" ] )
def submission_update_nickname( submission_id ):
    """
        Change the nickname of the donor in the database.

        THIS INFORMATION SHALL BE ENCRYPTED ON THE CLIENT SIDE FIRST WITH A UNIQUE
        ENCRYPTION KEY NOT TRANSMETTED TO THE SERVER!
    """
    app.logger.info( "Save the donor nickname to the database" )
    
    nickname = request.form.get( "nickname", None )
    
    if nickname != None and len( nickname ) != 0:
        try:
            
            sql = "UPDATE submissions SET nickname = %s WHERE uuid = %s"
            config.db.query( sql, ( nickname, submission_id, ) )
                "error": False
            app.logger.error( "Database error" )
                "error": True,
                "message": "DB error"
        app.logger.error( "No nickname in the post request" )
            "error": True,
            "message": "No new nickname in the POST request"
@app.route( baseurl + "/submission/list" )
def submission_list():
    """
        Get the list of all submissions folder for the currently logged submitter.
    """
    app.logger.info( "Get all submissions for '{}'".format( session[ "username" ] ) )
    
    sql = "SELECT * FROM submissions WHERE submitter_id = %s ORDER BY created_time DESC"
    q = config.db.query_fetchall( sql, ( session[ "user_id" ], ) )
Marco De Donno's avatar
Marco De Donno committed
    donors = []
    for donor in q:
        donors.append( {
            "id": donor.get( "id", None ),
            "email": do_decrypt_user_session( donor.get( "email_aes", None ) ),
            "nickname": do_decrypt_user_session( donor.get( "nickname", None ) ),
            "uuid": donor.get( "uuid", None )
        app.logger.debug( "uuid: {}".format( donor[ "uuid" ] ) )
    
    app.logger.info( "{} submissions found".format( len( donors ) ) )
Marco De Donno's avatar
Marco De Donno committed
    return my_render_template( 
        "submission/list.html",
@app.route( baseurl + "/submission/<submission_id>/mark/list" )
@app.route( baseurl + "/submission/<submission_id>/mark/list/<mark_type>" )
def submission_mark_list( submission_id, mark_type = "all" ):
        Get the list of mark for a particular submission folder.
    app.logger.info( "Get the list of mark for the submission '{}'".format( submission_id ) )
    app.logger.debug( "mark_type: {}".format( mark_type ) )
    if mark_type in [ "target", "incidental", "all" ]:
        sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
        case_id, nickname = config.db.query_fetchone( sql, ( submission_id, ) )
        
        sql = """
            SELECT files.uuid, files.filename, files.size, files.creation_time
            FROM files
            LEFT JOIN files_type ON files.type = files_type.id
            WHERE folder = %s AND
        """
        if mark_type == "target":
            sql += " files_type.name = 'mark_target'"
        elif mark_type == "incidental":
            sql += " files_type.name = 'mark_incidental'"
        elif mark_type == "all":
            sql += " ( files_type.name = 'mark_target' OR files_type.name = 'mark_incidental' )"
        
        sql += " ORDER BY files.id DESC"
        files = config.db.query_fetchall( sql, ( case_id, ) )
        for _, v in enumerate( files ):
            v[ "filename" ] = do_decrypt_user_session( v[ "filename" ] )
            v[ "size" ] = round( ( float( v[ "size" ] ) / ( 1024 * 1024 ) ) * 100 ) / 100
        app.logger.debug( "{} marks for '{}'".format( len( files ), submission_id ) )
Marco De Donno's avatar
Marco De Donno committed
        return my_render_template( 
            "submission/mark_list.html",
            submission_id = submission_id,
            mark_type = mark_type,
@app.route( baseurl + "/submission/<submission_id>/mark/<mark_id>" )
def submission_mark( submission_id, mark_id ):
        Serve the page to edit a particular mark image.
    app.logger.info( "Serve the mark page edit" )
    app.logger.debug( "submission {}".format( submission_id ) )
    app.logger.debug( "mark {}".format( mark_id ) )
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
    submission_folder_id, nickname = config.db.query_fetchone( sql, ( submission_id, ) )
            files.uuid, files.filename, files.note,
            files.format, files.resolution, files.width, files.height, files.size,
            files.creation_time, files.type,
            files_type.name as file_type
        LEFT JOIN files_type ON files.type = files_type.id
    mark = config.db.query_fetchone( sql, ( submission_folder_id, mark_id, ) )
    mark[ "size" ] = round( 100 * float( mark[ "size" ] ) / ( 1024 * 1024 ) ) / 100
    mark[ "filename" ] = do_decrypt_user_session( mark[ "filename" ] )
    mark[ "note" ] = do_decrypt_user_session( mark[ "note" ] )
    mark[ "file_type" ] = mark[ "file_type" ].replace( "mark_", "" )
Marco De Donno's avatar
Marco De Donno committed
    return my_render_template( 
        "submission/mark.html",
        submission_id = submission_id,
        nickname = nickname,
        file = mark
@app.route( baseurl + "/submission/<submission_id>/mark/<mark_id>/pfsp" )
def submission_mark_pfsp( submission_id, mark_id ):
    """
        Serve the page to set the PFSP information (location on the finger
        or the palm print) for the mark.
    app.logger.info( "Serve the PFSP edit page" )
    app.logger.debug( "submission {}".format( submission_id ) )
    app.logger.debug( "mark {}".format( mark_id ) )
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
    submission_folder_id, nickname = config.db.query_fetchone( sql, ( submission_id, ) )
    
    sql = """
        SELECT
            files.uuid, files.filename, files.note,
            files.format, files.resolution, files.width, files.height, files.size,
            files.creation_time, files.type,
            files_type.name as file_type
        
        FROM files
        LEFT JOIN files_type ON files.type = files_type.id
        WHERE
            folder = %s AND
            files.uuid = %s
    """
    
    mark = config.db.query_fetchone( sql, ( submission_folder_id, mark_id, ) )
    mark[ "size" ] = round( 100 * float( mark[ "size" ] ) / ( 1024 * 1024 ) ) / 100
    mark[ "filename" ] = do_decrypt_user_session( mark[ "filename" ] )
    mark[ "note" ] = do_decrypt_user_session( mark[ "note" ] )
    mark[ "file_type" ] = mark[ "file_type" ].replace( "mark_", "" )
    app.logger.debug( "file size: {}Mo".format( mark[ "size" ] ) )
    sql = "SELECT pfsp FROM mark_info WHERE uuid = %s"
        current_pfsp = config.db.query_fetchone( sql, ( mark_id, ) )[ "pfsp" ]
    app.logger.debug( "Current PFSP: {}".format( current_pfsp ) )
    
        if z[ "desc" ] == current_pfsp:
            current_pfsp = ",".join( z[ "sel" ] )
Marco De Donno's avatar
Marco De Donno committed
    return my_render_template( 
        "submission/mark_pfsp.html",
        submission_id = submission_id,
        nickname = nickname,
        file = mark,
        pfsp_zones = pfsp.zones,
@app.route( baseurl + "/submission/<submission_id>/mark/<mark_id>/set/pfsp", methods = [ "POST" ] )
def submission_mark_pfsp_set( submission_id, mark_id ):
        Save the PFSP information relative to a mark.
    app.logger.info( "Save the PFSP for submission '{}' mark '{}'".format( submission_id, mark_id ) )
    try:
        pfsp = request.form.get( "pfsp" )
        
        sql = "SELECT id FROM mark_info WHERE uuid = %s"
        q = config.db.query_fetchone( sql, ( mark_id, ) )
        
        if q == None:
            sql = utils.sql.sql_insert_generate( "mark_info", [ "uuid", "pfsp" ] )
            config.db.query( sql, ( mark_id, pfsp, ) )
        
        else:
            sql = "UPDATE mark_info SET pfsp = %s WHERE uuid = %s"
            config.db.query( sql, ( pfsp, mark_id, ) )
        
        config.db.commit()
        
        return jsonify( {
            "error": False
        } )
    except:
        return jsonify( {
            "error": True
        } )
@app.route( baseurl + "/submission/<submission_id>/mark/<mark_id>/delete" )
def submission_mark_delete( submission_id, mark_id ):
        Delete a mark from the database.
    app.logger.info( "Delete mark '{}' from submission '{}'".format( mark_id, submission_id ) )
    sql = "SELECT id FROM submissions WHERE submitter_id = %s AND uuid = %s"
    q = config.db.query( sql, ( session[ "user_id" ], submission_id, ) )
    
    if q != None:
        sql = "DELETE FROM files WHERE creator = %s AND uuid = %s"
        config.db.query( sql, ( session[ "user_id" ], mark_id, ) )
            "error": False
            "error": True
################################################################################
#    Submission deletion

@app.route( baseurl + "/submission/<submission_id>/delete" )
Marco De Donno's avatar
Marco De Donno committed
    """
        Delete the empty submission. A submission can not be deleted after the upload
        of the consent form and the creation of the donor user.
    """
    app.logger.info( "Delete the submission '{}' for user '{}'".format( submission_id, session[ "username" ] ) )
    
    sql = "SELECT consent_form FROM submissions WHERE submitter_id = %s AND uuid = %s"
    cf = config.db.query_fetchone( sql, ( session[ "user_id" ], submission_id, ) )[ "consent_form" ]
    if not cf:
        sql = "DELETE FROM submissions WHERE submitter_id = %s AND uuid = %s"
        config.db.query( sql, ( session[ "user_id" ], submission_id, ) )
        config.db.commit()
        return jsonify( {
            "error": False
        } )
        app.logger.error( "Can  not delete a submission with consent form" )
            "error": True,
            "message": "Can not delete if a consent form is already uploaded"
################################################################################
#    Admin submission

@app.route( baseurl + "/admin/submission/list" )
def admin_submission_list():
    """
        Get the list of all submissions folder.
    """
    app.logger.info( "Get all submissions" )
    
    sql = """
        SELECT submissions.id, submissions.uuid, users.username
        FROM submissions
        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 )
        app.logger.debug( "uuid: {}".format( donor[ "uuid" ] ) )
    
    app.logger.info( "{} submissions found".format( len( donors ) ) )
    
    return my_render_template( 
        "admin/list.html",
        donors = donors
    )

@app.route( baseurl + "/admin/<submission_id>/tenprint/list" )
@admin_required
def admin_tenprint_list( submission_id = "all" ):
        Get the list of all tenprints.
    app.logger.info( "Get all tenprints cards" )
        SELECT files.id, files.uuid, files.folder, users.username, submissions.uuid as submission_uuid
        FROM files
        LEFT JOIN submissions ON files.folder = submissions.id
        LEFT JOIN users ON submissions.email_hash = users.email 
        WHERE ( files.type = 1 OR files.type = 2 OR files.type = 5 )
    """
    data = ()
    
    if submission_id != "all":
        sql += " AND submissions.uuid = %s"
        data = ( submission_id, )
    
    sql += """
        ORDER BY users.id ASC, files.type ASC
    
    tenprint_cards = config.db.query_fetchall( sql, data )
    app.logger.info( "{} tenprints cards found".format( len( tenprint_cards ) ) )
    
    return my_render_template( 
        "admin/tenprint_list.html",
        tenprint_cards = tenprint_cards
    )

@app.route( baseurl + "/admin/<submission_id>/tenprint/<tenprint_id>" )
@admin_required
def admin_tenprint( submission_id, tenprint_id ):
    """
        Serve the page to see and edit a tenprint file.
    """
    app.logger.info( "Serve tenprint edit page for '{}', submission '{}'".format( tenprint_id, submission_id ) )
    sql = """
        SELECT
            files.uuid,
            files.format, files.resolution, files.width, files.height, files.size,
            files.creation_time, files.type,
            file_template.template, files.quality,
            users.username
        FROM files
        LEFT JOIN submissions ON files.folder = submissions.id
        LEFT JOIN users ON submissions.donor_id = users.id
        LEFT JOIN file_template ON files.uuid = file_template.file
        WHERE
            submissions.uuid = %s AND
            files.uuid = %s
    """
    tenprint_file = config.db.query_fetchone( sql, ( submission_id, tenprint_id, ) )
    
    app.logger.debug( "tenprint type: {}".format( tenprint_file[ "type" ] ) )
    
    if tenprint_file[ "type" ] == 5:
        app.logger.debug( "Redirect to the segments list page" )
        return redirect( url_for( "submission_tenprint_segments_list", submission_id = submission_id, tenprint_id = tenprint_id ) )
    
    else:
        tenprint_file[ "size" ] = round( 100 * float( tenprint_file[ "size" ] ) / ( 1024 * 1024 ) ) / 100
        
        if tenprint_file[ "type" ] == 1:
            side = "front"
        elif tenprint_file[ "type" ] == 2:
            side = "back"
        
        ############################################################################
        
        try:
            sql = "SELECT width, height, image_resolution FROM tenprint_cards WHERE id = %s LIMIT 1"
            tmp = config.db.query_fetchone( sql, ( tenprint_file[ "template" ], ) )
            
            card_info = {
                "width": int( round( float( tmp[ "width" ] ) / 2.54 * tmp[ "image_resolution" ] ) ),
                "height": int( round( float( tmp[ "height" ] ) / 2.54 * tmp[ "image_resolution" ] ) ),
                "width_cm": tmp[ "width" ],
                "height_cm": tmp[ "height" ]
            }
        except:
            card_info = {
                "width": 0,
                "height": 0,
                "width_cm": 0,
                "height_cm": 0
            }
        
        ############################################################################
        
        sql = "SELECT id, country_code, name, width, height, size_display FROM tenprint_cards ORDER BY name"
        tenprint_templates = config.db.query_fetchall( sql )
        
        ############################################################################
        
        sql = "SELECT id, name FROM quality_type"
        quality_type = config.db.query_fetchall( sql )
        
        ############################################################################
        
        zones = get_tenprint_template_zones( tenprint_file[ "template" ], side )
        datacolumns = [ "tl_x", "tl_y", "br_x", "br_y", "angle" ]
        
        ############################################################################
        
        sql = "SELECT width, height, resolution FROM files WHERE uuid = %s LIMIT 1"
        img_info = config.db.query_fetchone( sql, ( tenprint_id, ) )
        svg_hw_factor = float( img_info[ "width" ] ) / float( img_info[ "height" ] )
        
        return my_render_template( 
            "admin/tenprint.html",
            submission_id = submission_id,
            file = tenprint_file,
            card_info = card_info,
            img_info = img_info,
            svg_hw_factor = svg_hw_factor,
            zones = zones,
            datacolumns = datacolumns,
            tenprint_templates = tenprint_templates,
            quality_type = quality_type
        )

################################################################################
#    Image processing

def get_submission_uuid_for_file( file_uuid ):
Marco De Donno's avatar
Marco De Donno committed
    """
        Get the related submission uuid for a file uuid.
    """
    sql = """
        SELECT submissions.uuid
        FROM submissions
        LEFT JOIN files ON submissions.id = files.folder
        WHERE files.uuid = %s
    """
    return config.db.query_fetchone( sql, ( file_uuid, ) )[ "uuid" ]

@app.route( baseurl + "/image/file/<file_id>/preview" )
def image_file_serve( file_id ):
    """
        Function to get an image from the database and return it as PNG preview image.
    """
    app.logger.info( "Serve a preview for the file '{}'".format( file_id ) )
    
        submission_id = get_submission_uuid_for_file( file_id )
        app.logger.debug( "submission id: '{}'".format( submission_id ) )
        
        img, _ = image_serve( "thumbnails", file_id, submission_id )
            app.logger.debug( "No image in the 'thumnnails' database. Recreating the thumbnail" )
            
            img, _ = image_serve( "files", file_id, submission_id )
            app.logger.debug( "Image from the 'files' table: {}".format( img ) )
            img = create_thumbnail( file_id, img, submission_id )
            app.logger.debug( "Thumbnail image: {}".format( img ) )
        buff = utils.images.pil2buffer( img, "PNG" )
        return send_file( buff, mimetype = "image/png" )
        app.logger.error( "Error while creating the thumbnail. Serving a 'no preview' image" )
        return send_file( no_preview_image(), mimetype = "image/png" )
@app.route( baseurl + "/image/segment/<tenprint_id>/<pc>" )
def image_segment_serve( tenprint_id, pc ):
    """
        Serve a preview for a segment image.
    """
    app.logger.info( "Serving a segment image for the tenprint '{}', pc '{}'".format( tenprint_id, pc ) )
    
    try:
        submission_id = get_submission_uuid_for_file( tenprint_id )
        
        app.logger.debug( "submission id: {}".format( submission_id ) )
        
        img, file_segment_id = image_serve( "files_segments", ( tenprint_id, pc ), submission_id )
        img = create_thumbnail( file_segment_id, img, submission_id )
        
        app.logger.debug( "image: {}".format( img ) )
        
        buff = utils.images.pil2buffer( img, "PNG" )
        return send_file( buff, mimetype = "image/png" )
        app.logger.error( "Error while creating the thumbnail. Serving a 'no preview' image" )
        return send_file( no_preview_image(), mimetype = "image/png" )
@app.route( baseurl + "/image/template/<tenprint_id>/<side>" )
@app.route( baseurl + "/image/template/<tenprint_id>/<side>/<action>" )
@login_required
def image_tp_template( tenprint_id, side, action = "full" ):
    """
        Serve a template image, full-resolution or preview.
    """
    app.logger.info( "Serve the tenprint template image for {}, {}".format( tenprint_id, side ) )
    
    if side in [ "front", "back" ]:
        img, _ = image_serve( "tenprint_cards", ( tenprint_id, side ), None )
        
        if img == None:
            return send_file( no_preview_image(), mimetype = "image/png" )
        
        if action == "preview":
            img.thumbnail( ( 500, 500 ) )
        
        app.logger.debug( "image: {}".format( img ) )
        
        buff = utils.images.pil2buffer( img, "PNG" )
        return send_file( buff, mimetype = "image/png" )
    
    else:
        return abort( 403 )

def image_serve( table, image_id, submission_id ):
    """
        Backend function to get the image from the database.
    """
    app.logger.info( "Serve the image '{}' for submission '{}' from table '{}'".format( image_id, submission_id, table ) )
    if table == "files_segments":
        if isinstance( image_id, tuple ):
            tp, pc = image_id
            sql = "SELECT data, uuid FROM {} WHERE tenprint = %s AND pc = %s".format( table )
            sql = "SELECT data, uuid FROM {} WHERE uuid = %s".format( table )
    elif table in [ "files", "thumbnails" ]:
        sql = "SELECT data, uuid FROM {} WHERE uuid = %s".format( table )
    elif table == "tenprint_cards":
        sql = "SELECT image_{}, id FROM {} WHERE id = %s".format( t, table )
        app.logger.error( "table '{}' not authorized".format( table ) )
        raise Exception( "table not authorized" )
    
    app.logger.debug( "sql:    {}".format( sql ) )
    app.logger.debug( "params: {}".format( p ) )
    
    data = config.db.query_fetchone( sql, p )
    if data == None:
        return None, None
    
        img, rid = data
        app.logger.debug( "image: {}...".format( img[ 0:20 ] ) )
Marco De Donno's avatar
Marco De Donno committed
        app.logger.debug( "need_to_decrypt: {}".format( need_to_decrypt ) )
        if need_to_decrypt:
            img = do_decrypt_dek( img, submission_id )
        
        img = str2img( img )
        app.logger.debug( "image: {}".format( img ) )
        
        return img, rid
    """
        Convert a base64 string image to a PIL image.
    """
    app.logger.info( "Convert string image to PIL format" )
    
    if data == None:
        return None
    
    else:
        img = base64.b64decode( data )
        buff = StringIO()
        buff.write( img )
        buff.seek( 0 )
        img = Image.open( buff )
        
        app.logger.debug( "string: {}".format( data[ 0:20 ] ) )
        app.logger.debug( "image: {}".format( img ) )
        
@app.route( baseurl + "/image/file/<image_id>/info" )
    """
        Get and return the metadata for a particular image.
        See do_img_info() for more informations.
    """
    app.logger.info( "Serve image informations for image '{}'".format( image_id ) )
    
    
    if d != None:
        return jsonify( d )
    else:
        return abort( 404 )

    """
        Retrieve the metadata for a particular image from the database.
    """
    app.logger.debug( "Get image information from database for '{}'".format( image_id ) )
    
    sql = "SELECT size, width, height, resolution, format FROM files WHERE uuid = %s"
    d = config.db.query_fetchone( sql, ( image_id, ) )
    for key, value in d.iteritems():
        app.logger.debug( "{}: {}".format( key, value ) )
    
    if d != None:
        return dict( d )
    else:
        return None

def create_thumbnail( file_uuid, img, submission_id ):
    """
        Generate a thumbnail image for a PIL image passed in argument.
    """
    app.logger.info( "Creating a thumbnail for the file '{}', submission '{}'".format( file_uuid, submission_id ) )
    app.logger.debug( "Input image: {}".format( img ) )