Skip to content
module.py 51.5 KiB
Newer Older
        js = config.cdnjs,
        css = config.cdncss
    )
################################################################################
#    Data decryption

def do_decrypt( data ):
    return AESCipher( session[ 'password' ] ).decrypt( data )

def do_encrypt( data ):
    return AESCipher( session[ 'password' ] ).encrypt( data )

Marco De Donno's avatar
Marco De Donno committed
################################################################################
#    File upload

@app.route( baseurl + '/upload', methods = [ 'GET', 'POST' ] )
@login_required
def upload_file():
    upload_type = request.form.get( "upload_type", None )
    
    if upload_type == None:
Marco De Donno's avatar
Marco De Donno committed
        return jsonify( {
            'error': True,
            'msg': 'Must specify a file type to upload a file'
        } )
    
    if request.method == 'POST':
        if 'file' not in request.files:
            return jsonify( { 'error': True, 'msg': 'No file in the POST request' } )
        elif 'upload_id' not in request.form:
            return jsonify( { 'error': True, 'msg': 'No upload_id' } )
        else:
            try:
                upload_id = request.form.get( "upload_id" )
                sql = "SELECT id FROM submissions WHERE uuid = %s"
                r = config.db.query( sql, ( upload_id, ) )
                upload_id = r.fetchone()[ 'id' ]
                
            except:
                return jsonify( {
                    'error': True
                } )
            
            file = request.files[ 'file' ]
            filename = do_encrypt( file.filename )
            file_uuid = str( uuid4() )
            
            fp = StringIO()
            
            file.save( fp )
            file_size = fp.tell()
            
            fp.seek( 0 )
            
            if upload_type in [ 'latent', 'tenprint_card_front', 'tenprint_card_back' ]:
                img = Image.open( fp )
                img_format = img.format
                width, height = img.size
                res = int( img.info[ 'dpi' ][ 0 ] )
                img = rotate_image_upon_exif( img )
                
                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' ]:
                    create_thumbnail( file_uuid, img )
            file_data = base64.b64encode( file_data )
            
            if upload_type == "consent_form":
                file_data = gpg.encrypt( file_data, config.gpg_key )
                file_data = str( file_data )
            
            sql = "SELECT id FROM files_type WHERE name = %s"
            upload_type_id = config.db.query( sql, ( upload_type, ) ).fetchone()[ 0 ]
            sql = "INSERT INTO files ( folder, creator, filename, type, format, size, width, height, resolution, uuid, data ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )"
            data = ( upload_id, session[ 'user_id' ], filename, 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,
                'filename': filename,
                'filesize': file_size,
                'uuid': file_uuid
            } )
    
    else:
        return abort( 403 )
################################################################################
Marco De Donno's avatar
Marco De Donno committed
#    Submission of a new donor
@app.route( baseurl + '/submission/new' )
def submission_new():
    return render_template( 
        "submission/new.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        session_security_key = session.get( "session_security_key" )
@app.route( baseurl + '/submission/do_new', methods = [ "GET", "POST" ] )
@login_required
def submission_do_new():
    email = request.form.get( "email", False )
    
    if email:
        # Check for duplicate base upon the email data
        sql = "SELECT id, email_hash FROM submissions WHERE submitter_id = %s"
        r = config.db.query( sql, ( session[ 'user_id' ], ) )
        for case in r.fetchall():
            if pbkdf2( email, case[ 'email_hash' ] ):
                return jsonify( {
                    'error': True,
                    'msg': "Email already used"
                } )
                
                break
Marco De Donno's avatar
Marco De Donno committed
            # Insert the new donor
            id = str( uuid4() )
            
            email_aes = do_encrypt( email )
            email_hash = pbkdf2( email, random_data( 50 ), 50000 )
            
            upload_nickname = request.form.get( "upload_nickname", None )
            upload_nickname = do_encrypt( upload_nickname )
            submitter_id = session[ 'user_id' ]
            
            status = "pending"
            
            sql = "INSERT INTO submissions ( uuid, email_aes, email_hash, nickname, status, submitter_id ) VALUES ( %s, %s, %s, %s, %s, %s ) RETURNING id"
            data = ( id, email_aes, email_hash, upload_nickname, status, submitter_id )
            config.db.query( sql, data )
            config.db.commit()
            
            return jsonify( {
                'error': False,
                'id': id
            } )
        
    else:
        return jsonify( {
            'error': True,
            'msg': "Email not provided"
        } )

@app.route( baseurl + '/submission/<id>/update' )
def submission_update( id ):
    try:
        sql = "SELECT email_aes as email, nickname, created_time FROM submissions WHERE submitter_id = %s AND uuid = %s"
        r = config.db.query( sql, ( session[ 'user_id' ], id ) )
        user = r.fetchone()
        
        for key in [ 'email', 'nickname' ]:
            user[ key ] = do_decrypt( user[ key ] )
        
        return render_template( 
            "submission/update.html",
            baseurl = baseurl,
            js = config.cdnjs,
            css = config.cdncss,
            session_timeout = config.session_timeout,
            upload_id = id,
            session_security_key = session.get( "session_security_key" ),
    except:
        return jsonify( {
            'error': True,
            'msg': "Case not found"
        } )
@app.route( baseurl + '/submission/<id>/update/nickname', methods = [ 'POST' ] )
@login_required
def submission_update_nickname( id ):
    nickname = request.form.get( "nickname", None )
    
    if nickname != None and len( nickname ) != 0:
        try:
            nickname = do_encrypt( nickname )
            
            sql = "UPDATE submissions SET nickname = %s WHERE uuid = %s"
            config.db.query( sql, ( nickname, id, ) )
            config.db.commit()
            
            return jsonify( {
                'error': False
            } )
        
        except:
            return jsonify( {
                'error': True,
                'message': "DB error"
            } )
    
    else:
        return jsonify( {
            'error': True,
            'message': "No new nickname in the POST request"
        } )

@app.route( baseurl + '/submission/list' )
@login_required
def submission_list():
    sql = "SELECT * FROM submissions WHERE submitter_id = %s ORDER BY created_time DESC"
    r = config.db.query( sql, ( session[ 'user_id' ], ) )
    q = r.fetchall()
    
Marco De Donno's avatar
Marco De Donno committed
    donors = []
    for donor in q:
        donors.append( {
            'id': donor.get( "id", None ),
            'email': do_decrypt( donor.get( "email_aes", None ) ),
            'nickname': do_decrypt( donor.get( "nickname", None ) ),
            'uuid': donor.get( "uuid", None )
        } )
    
    return render_template( 
        "submission/list.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        donors = donors,
        session_security_key = session.get( "session_security_key" )
Marco De Donno's avatar
Marco De Donno committed
@app.route( baseurl + '/submission/<id>/latent/list' )
Marco De Donno's avatar
Marco De Donno committed
def submission_latent_list( id ):
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s AND submitter_id = %s"
    r = config.db.query( sql, ( id, session[ 'user_id' ], ) )
    case_id, nickname = r.fetchone()
    nickname = do_decrypt( nickname )
    sql = "SELECT uuid, filename, size, creation_time FROM files WHERE folder = %s AND type = 3"
    r = config.db.query( sql, ( case_id, ) )
    files = r.fetchall()
    
    for i, v in enumerate( files ):
        v[ 'filename' ] = do_decrypt( v[ 'filename' ] )
        v[ 'size' ] = round( ( float( v[ 'size' ] ) / ( 1024 * 1024 ) ) * 100 ) / 100
    
    return render_template( 
Marco De Donno's avatar
Marco De Donno committed
        "submission/latent_list.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        files = files,
        nickname = nickname,
        session_security_key = session.get( "session_security_key" )
@app.route( baseurl + '/submission/<id>/latent/<lid>' )
@login_required
def submission_latent( id, lid ):
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
    r = config.db.query( sql, ( id, ) )
    submission_id, nickname = r.fetchone()
    nickname = do_decrypt( nickname )
    
    sql = """
        SELECT
            files.uuid, files.filename, files.format, files.resolution, files.width, files.height, files.size, files.creation_time, files.type
        
        FROM files
        WHERE
            folder = %s AND
            files.uuid = %s
    """
    
    r = config.db.query( sql, ( submission_id, lid, ) )
    latent = r.fetchone()
    latent[ 'size' ] = round( 100 * float( latent[ 'size' ] ) / ( 1024 * 1024 ) ) / 100
    latent[ 'filename' ] = do_decrypt( latent[ 'filename' ] )
    
    return render_template( 
        "submission/latent.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        submission_id = id,
        nickname = nickname,
        latent = latent,
        session_security_key = session.get( "session_security_key" )
################################################################################
#    Image processing

@app.route( baseurl + '/image/info/<id>' )
@login_required
def img_info( id ):
    d = do_img_info( id )
    
    if d != None:
        return jsonify( d )
    else:
        return abort( 404 )

def do_img_info( id ):
    sql = "SELECT size, width, height, resolution, format FROM files WHERE uuid = %s"
    r = config.db.query( sql, ( id, ) )
    d = r.fetchone()
    
    if d != None:
        return dict( d )
    else:
        return None

@app.route( baseurl + '/image/preview/<id>' )
@referer_required
@login_required
def img_preview( id ):
    sql = "SELECT size, data FROM thumbnails WHERE uuid = %s"
    r = config.db.query( sql, ( id, ) )
    img = r.fetchone()
    
    if img == None:
        sql = "SELECT size, data FROM files WHERE uuid = %s"
        r = config.db.query( sql, ( id, ) )
        img = r.fetchone()
        do_thumbnail = True
    else:
        do_thumbnail = False
        
    if img != None:
        img = base64.b64decode( img[ 'data' ] )
        buff = StringIO()
        buff.write( img )
        buff.seek( 0 )
        img = Image.open( buff )
        
        if do_thumbnail:
            img.thumbnail( ( 300, 300 ) )
        
        buff = StringIO()
        img.save( buff, format = 'PNG' )
        buff.seek( 0 )
        
        return send_file( buff, mimetype = "image/png" )
def create_thumbnail( file_uuid, img ):
    img.thumbnail( ( 1000, 1000 ) )
    width, height = img.size
    file_format = img.format
    
    buff = StringIO()
    img.save( buff, format = img.format )
    img_size = buff.tell()
    buff.seek( 0 )
    
    img_data = buff.getvalue()
    img_data = base64.b64encode( img_data )
    
    sql = "INSERT INTO thumbnails ( uuid, width, height, size, format, data ) VALUES ( %s, %s, %s, %s, %s, %s )"
    data = ( file_uuid, width, height, img_size, img.format, img_data, )
    config.db.query( sql, data )
    config.db.commit()
    
    return

################################################################################
Marco De Donno's avatar
Marco De Donno committed
#    Donor tenprints
@app.route( baseurl + '/submission/<id>/tenprint/list' )
def submission_tenprint_list( id ):
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
    submission_id, nickname = r.fetchone()
    nickname = do_decrypt( nickname )
    sql = """
        SELECT
            id, filename, uuid, type, creation_time
        FROM files
        WHERE folder = %s AND ( type = 1 OR type = 2 )
        ORDER BY creation_time DESC
    """
    
    r = config.db.query( sql, ( submission_id, ) )
    q = r.fetchall()
    
    tenprint_cards = {
        '1': [],
        '2': []
    }
        tenprint_cards[ str( tenprint[ 'type' ] ) ].append( {
            'id': tenprint.get( "id", None ),
            'filename': do_decrypt( tenprint.get( "filename", None ) ),
            'uuid': tenprint.get( "uuid", None ),
            'type': tenprint.get( "type", None )
        "submission/tenprint_list.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        tenprint_cards_front = tenprint_cards[ '1' ],
        tenprint_cards_back = tenprint_cards[ '2' ],
        submission_id = id,
        nickname = nickname,
        session_security_key = session.get( "session_security_key" )
@app.route( baseurl + '/submission/<id>/tenprint/<tid>' )
def submission_tenprint( id, tid ):
    sql = "SELECT id, nickname FROM submissions WHERE uuid = %s"
    submission_id, nickname = r.fetchone()
    nickname = do_decrypt( nickname )
    
    sql = """
        SELECT
            files.uuid, files.filename, files.format, files.resolution, files.width, files.height, files.size, files.creation_time, files.type,
            file_template.template
        FROM files
        JOIN file_template ON files.uuid = file_template.file
        WHERE
            folder = %s AND
            files.uuid = %s
    """
    
    r = config.db.query( sql, ( submission_id, tid, ) )
    file = r.fetchone()
    file[ 'size' ] = round( 100 * float( file[ 'size' ] ) / ( 1024 * 1024 ) ) / 100
    file[ 'filename' ] = do_decrypt( file[ 'filename' ] )
    
    if file[ 'type' ] == 1:
        t = 'front'
    elif file[ 'type' ] == 2:
        t = 'back'
    
    ############################################################################
    
    sql = 'SELECT width, height, image_resolution FROM tenprint_cards WHERE id = %s LIMIT 1'
    r = config.db.query( sql, ( file[ 'template' ], ) )
    tmp = r.fetchone()
    
    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' ]
    }
    
    ############################################################################
    
    sql = """
        SELECT
            tenprint_zones.pc, tl_x, tl_y, br_x, br_y, angle, pc.name
        FROM tenprint_zones
        JOIN tenprint_zones_location ON tenprint_zones.pc = tenprint_zones_location.pc
        JOIN pc ON tenprint_zones.pc = pc.id
        WHERE
            card = %s AND
            tenprint_zones_location.side = %s
        ORDER BY pc
    """
    r = config.db.query( sql, ( file[ 'template' ], t, ) ).fetchall()
    
    zones = []
    for pc, tl_x, tl_y, br_x, br_y, angle, pc_name in r:
        tl_x = float_or_null( tl_x )
        tl_y = float_or_null( tl_y )
        br_x = float_or_null( br_x )
        br_y = float_or_null( br_y )
        
        zones.append( {
            "pc": pc,
            "tl_x": tl_x,
            "tl_y": tl_y,
            "br_x": br_x,
            "br_y": br_y,
            "angle": angle,
            "pc_name": pc_name
        } )
    
    datacolumns = [ 'tl_x', 'tl_y', 'br_x', 'br_y', 'angle' ]
    
    ############################################################################
    
    sql = 'SELECT width, height, resolution FROM files WHERE uuid = %s LIMIT 1'
    r = config.db.query( sql, ( tid, ) )
    img_info = r.fetchone()
    svg_hw_factor = float( img_info[ 'width' ] ) / float( img_info[ 'height' ] )
    
    return render_template( 
        "submission/tenprint.html",
        baseurl = baseurl,
        js = config.cdnjs,
        css = config.cdncss,
        session_timeout = config.session_timeout,
        upload_id = id,
        tenprint_id = tid,
        file = file,
        nickname = nickname,
        submission_id = id,
        session_security_key = session.get( "session_security_key" ),
        t = t,
        card_id = file[ 'uuid' ],
        card_info = card_info,
        img_info = img_info,
        svg_hw_factor = svg_hw_factor,
        zones = zones,
        datacolumns = datacolumns
    )

################################################################################
#    Tenprint templates

@app.route( baseurl + '/tenprint_template/<id>/<t>' )
@login_required
def tp_template( id, t ):
    if t in [ 'front', 'back' ]:
        sql = """SELECT
            tenprint_zones.pc, tl_x, tl_y, br_x, br_y, angle, pc.name
            FROM tenprint_zones
            JOIN tenprint_zones_location ON tenprint_zones.pc = tenprint_zones_location.pc
            JOIN pc ON tenprint_zones.pc = pc.id
            WHERE card = %s AND tenprint_zones_location.side = %s ORDER BY pc
        """
        r = config.db.query( sql, ( id, t, ) ).fetchall()
        
        zones = []
        for pc, tl_x, tl_y, br_x, br_y, angle, pc_name in r:
            tl_x = float_or_null( tl_x )
            tl_y = float_or_null( tl_y )
            br_x = float_or_null( br_x )
            br_y = float_or_null( br_y )
            
            zones.append( {
                "pc": pc,
                "tl_x": tl_x,
                "tl_y": tl_y,
                "br_x": br_x,
                "br_y": br_y,
                "angle": angle,
                "pc_name": pc_name
        datacolumns = [ 'tl_x', 'tl_y', 'br_x', 'br_y', 'angle' ]
        sql = 'SELECT id, country_code, width, height, size_display, image_' + t + '_width, image_' + t + '_height, image_resolution FROM tenprint_cards WHERE id = %s LIMIT 1'
        r = config.db.query( sql, ( id, ) )
        img_info = r.fetchone()
        
        card_info = {
            'width': int( round( float( img_info[ 'width' ] ) / 2.54 * img_info[ 'image_resolution' ] ) ),
            'height': int( round( float( img_info[ 'height' ] ) / 2.54 * img_info[ 'image_resolution' ] ) ),
        }
        
        svg_hw_factor = float( img_info[ 'image_' + t + '_width' ] ) / float( img_info[ 'image_' + t + '_height' ] )
        
        return render_template( 
            "tp_template/tp_template.html",
            baseurl = baseurl,
            admin = int( session[ 'account_type' ] ) == 1,
            js = config.cdnjs,
            css = config.cdncss,
            session_timeout = config.session_timeout,
            account_type = session.get( "account_type", None ),
            zones = zones,
            img_info = img_info,
            card_info = card_info,
            card_id = id,
        )
    
    else:
        return abort( 403 )

@app.route( baseurl + '/tenprint_template/image/<id>/<t>' )
@referer_required
@login_required
def tp_template_img( t, id ):
    if t in [ 'front', 'back' ]: 
        sql = "SELECT image_front, image_back FROM tenprint_cards WHERE id = %s LIMIT 1"
        r = config.db.query( sql, ( id, ) )
        img = r.fetchone()
        
        if img != None:
            img = base64.b64decode( img[ 'image_' + t ] )
            
            buff = StringIO()
            buff.write( img )
            buff.seek( 0 )
            
            img = Image.open( buff )
            img.thumbnail( ( 1000, 1000 ) )
            
            buff2 = StringIO()
            img.save( buff2, format = 'PNG' )
            buff2.seek( 0 )
            
            return send_file( buff2, mimetype = "image/png" )
    
    else:
        return abort( 403 )

@app.route( baseurl + '/tenprint_template/zones/update/<card>', methods = [ "GET", "POST" ] )
    card = int( card )
    data = request.form.get( "data" )
    if data != None:
        data = json.loads( data )
        
        for pc, value in data.iteritems():
            pc = int( pc )
            
            for coordinate, v in value.iteritems():
                sql = "UPDATE tenprint_zones SET " + coordinate + " = %s WHERE card = %s AND pc = %s"
                data = ( v, card, pc, )
                config.db.query( sql, data )
################################################################################
#    Home page

@app.route( baseurl + '/' )
def home():
    return render_template( 
        "index.html",
        baseurl = baseurl,
        admin = int( session[ 'account_type' ] ) == 1,
        js = config.cdnjs,
        css = config.cdncss,
Marco De Donno's avatar
Marco De Donno committed
        session_timeout = config.session_timeout,
        account_type = session.get( "account_type", None ),
        session_security_key = session.get( "session_security_key" )
################################################################################
#    Main startup

if __name__ == '__main__':
    gpg = gnupg.GPG()
    
    for file in os.listdir( config.keys_folder ):
        with open( config.keys_folder + "/" + file, "r" ) as fp:
            gpg.import_keys( fp.read() )
    
    app.run( debug = debug, host = "0.0.0.0", threaded = True )