default.py 17.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
# -*- coding: utf-8 -*-
# this file is released under public domain and you can use without limitations

#########################################################################
## This is a sample controller
## - index is the default action of any application
## - user is required for authentication and authorization
## - download is for downloading files uploaded in the db (does streaming)
## - call exposes all registered services (none by default)
#########################################################################

12
import gluon.contrib.simplejson, time, datetime
13 14 15 16
if request.env.http_origin:
    response.headers['Access-Control-Allow-Origin'] = request.env.http_origin  
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    response.headers['Access-Control-Max-Age'] = 86400
17

18 19
#########################################################################
##return the default index page for vidjil (empty)
20 21 22 23 24 25 26 27 28 29 30
def index():
    """
    example action using the internationalization operator T and flash
    rendered by views/default/index.html or views/generic.html

    if you need a simple wiki simply replace the two lines below with:
    return auth.wiki()
    """
    response.flash = T("Welcome to Vidjil!")
    return dict(message=T('hello world'))

31 32
#########################################################################
##return the view default/help.html
Marc Duez's avatar
Marc Duez committed
33 34 35
def help():
    return dict(message=T('help i\'m lost'))

36
#########################################################################
37
## add a scheduller task to run vidjil on a specific sequence file
38 39
# need sequence_file_id, config_id
# need patient admin permission 
40
def run_request():
41 42 43 44 45 46 47
    error = ""

    ##TODO check  
    if not "sequence_file_id" in request.vars :
        error += "id sequence file needed, "
    if not "config_id" in request.vars:
        error += "id config needed, "
48
    if not auth.has_permission("run", "results_file") :
49 50
        error += "permission needed"
    
51 52 53
    id_patient = db.sequence_file[request.vars["sequence_file_id"]].patient_id
    
    if not auth.has_permission('admin', 'patient', id_patient) :
54
        error += "you do not have permission to launch process for this patient ("+str(id_patient)+"), "
55 56

    if error == "" :
57
        res = schedule_run(request.vars["sequence_file_id"], request.vars["config_id"])
58 59 60
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))

    else :
61
        res = {"success" : "false",
62
               "message" : "default/run_request : " + error}
63
        log.error(res)
64 65 66
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
    
    
67
    
68 69 70 71
#########################################################################
## return .data file
# need patient_id, config_id
# need patient admin or read permission 
72
def get_data():
73 74 75
    import time
    from subprocess import Popen, PIPE, STDOUT

76
    error = ""
77

78 79 80 81
    if not "patient_id" in request.vars :
        error += "id patient file needed, "
    if not "config_id" in request.vars:
        error += "id config needed, "
82 83
    if not auth.has_permission('admin', 'patient', request.vars["patient_id"]) and \
    not auth.has_permission('read', 'patient', request.vars["patient_id"]):
84
        error += "you do not have permission to consult this patient ("+id_patient+")"
85
        
86 87 88 89 90 91 92
    query = db( ( db.fused_file.patient_id == request.vars["patient_id"] )
               & ( db.fused_file.config_id == request.vars["config_id"] )
               ).select() 
    for row in query :
        fused_file = "applications/vidjil/uploads/"+row.fused_file

    if error == "" :
93
        
94
        f = open(fused_file, "r")
95
        data = gluon.contrib.simplejson.loads(f.read())
96
        f.close()
97 98 99
        
        ## récupération des infos stockées sur la base de données 
        query = db( ( db.patient.id == db.sequence_file.patient_id )
100
                   & ( db.results_file.sequence_file_id == db.sequence_file.id )
101
                   & ( db.patient.id == request.vars["patient_id"] )
102
                   & ( db.results_file.config_id == request.vars["config_id"]  )
103 104 105 106 107
                   ).select( orderby=db.sequence_file.sampling_date ) 

        data["samples"]["original_names"] = []
        data["samples"]["info"] = []
        for row in query :
108
            filename = row.sequence_file.filename
109 110
            data["samples"]["original_names"].append(filename)
            data["samples"]["info"].append(row.sequence_file.info) 
111

112
        log.debug("get_data: %s -> %s" % (request.vars["patient_id"], fused_file))
113
        return gluon.contrib.simplejson.dumps(data, separators=(',',':'))
114

115 116 117
    else :
        res = {"success" : "false",
               "message" : "default/get_data : " + error}
118
        log.error(res)
119
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
120
    
121 122 123 124
#########################################################################
## return .analysis file
# need patient_id, config_id
# need patient admin or read permission 
125 126 127 128 129 130 131
def get_analysis():
    error = ""

    if not "patient_id" in request.vars :
        error += "id patient file needed, "
    if not "config_id" in request.vars:
        error += "id config needed, "
132 133
    if not auth.has_permission('admin', 'patient', request.vars["patient_id"]) and \
    not auth.has_permission('read', 'patient', request.vars["patient_id"]):
134
        error += "you do not have permission to consult this patient ("+id_patient+")"
135 136
    
    ## empty analysis file
137 138 139 140 141 142 143 144
    res = {"samples": {"number": 0,
                      "original_names": [],
                      "order": [],
                      "info_sequence_file" : []
                       },
           "custom": [],
           "clones" : [],
           "tags": [],
145
           "vidjil_json_version" : "2014.09"
146 147
           }
    
148
    
149
    if error == "" :
150
        
151
        res["info_patient"] = db.patient[request.vars["patient_id"]].info
152 153
        res["patient"] = db.patient[request.vars["patient_id"]].first_name + " " + db.patient[request.vars["patient_id"]].last_name + " (" + db.config[request.vars["config_id"]].name + ")"
        
154 155 156
        ## récupération des infos se trouvant dans le fichier .analysis
        analysis_query = db(  (db.analysis_file.patient_id == request.vars["patient_id"])
                   & (db.analysis_file.config_id == request.vars["config_id"] )  )
157

158 159 160 161 162
        if not analysis_query.isempty() :
            row = analysis_query.select().first()
            f = open('applications/vidjil/uploads/'+row.analysis_file, "r")
            analysis = gluon.contrib.simplejson.loads(f.read())
            f.close()
163
            res["clusters"] = analysis["clusters"]
164
            res["clones"] = analysis["clones"]
165
            res["tags"] = analysis["tags"]
166
            res["samples"]["order"] = analysis["samples"]["order"]
167

168 169 170 171
        res["info_patient"] = db.patient[request.vars["patient_id"]].info
        res["patient"] = db.patient[request.vars["patient_id"]].first_name + " " + db.patient[request.vars["patient_id"]].last_name + " (" + db.config[request.vars["config_id"]].name + ")"
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
        
172 173 174
    else :
        res = {"success" : "false",
               "message" : "default/get_analysis : " + error}
175
        log.error(res)
176 177
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
        
178
    
179 180 181 182
#########################################################################
## upload .analysis file and store it on the database
# need patient_id, config_id, fileToUpload
# need patient admin permission 
183 184 185 186 187 188 189
def save_analysis():
    error = ""

    if not "patient_id" in request.vars :
        error += "id patient file needed, "
    if not "config_id" in request.vars:
        error += "id config needed, "
190
    if not auth.has_permission('admin', 'patient', request.vars['patient_id']) :
191
        error += "you do not have permission to save changes on this patient"
192 193
        
    if error == "" :
Marc Duez's avatar
Marc Duez committed
194 195
        analysis_query = db(  (db.analysis_file.patient_id == request.vars['patient_id'])
                            & (db.analysis_file.config_id == request.vars['config_id'] )  )
196

197 198
        f = request.vars['fileToUpload']
        
199
        ts = time.time()
200 201
        if not analysis_query.isempty() :
            analysis_id = analysis_query.select().first().id
202 203 204 205 206
            db.analysis_file[analysis_id] = dict(analysis_file = db.analysis_file.analysis_file.store(f.file, f.filename),
                                                 analyze_date = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
                                                 )
        else:     
            
207
            analysis_id = db.analysis_file.insert(analysis_file = db.analysis_file.analysis_file.store(f.file, f.filename),
208 209 210 211
                                                  config_id = request.vars['config_id'],
                                                  patient_id = request.vars['patient_id'],
                                                  analyze_date = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
                                                  )
212
        
213
        patient_name = db.patient[request.vars['patient_id']].first_name + " " + db.patient[request.vars['patient_id']].last_name
214
        
215
        res = {"success" : "true",
216
               "message" : patient_name+": analysis saved"}
217
        log.info(res)
218 219
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
    else :
220 221
        res = {"success" : "false",
               "message" : error}
222
        log.error(res)
223 224
        return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
        
225
        
226

227 228
#########################################################################
## TODO make custom download for .data et .analysis
229 230 231 232 233 234
@cache.action()
def download():
    """
    allows downloading of uploaded files
    http://..../[app]/default/download/[filename]
    """
235
    return response.download(request, db, download_filename=request.vars.filename)
236 237


238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
#########################################################################
## 
def create_self_signed_cert(cert_dir):
    """
    create a new self-signed cert and key and write them to disk
    """
    from OpenSSL import crypto, SSL
    from socket import gethostname
    from pprint import pprint
    from time import gmtime, mktime
    from os.path import exists, join
 
    CERT_FILE = "ssl_certificate.crt"    
    KEY_FILE = "ssl_self_signed.key"
    ssl_created = False
    if not exists(join(cert_dir, CERT_FILE)) \
            or not exists(join(cert_dir, KEY_FILE)):
        ssl_created = True    
        # create a key pair
        k = crypto.PKey()
        k.generate_key(crypto.TYPE_RSA, 4096)
 
        # create a self-signed cert
        cert = crypto.X509()
        cert.get_subject().C = "AQ"
        cert.get_subject().ST = "State"
        cert.get_subject().L = "City"
        cert.get_subject().O = "Company"
        cert.get_subject().OU = "Organization"
        cert.get_subject().CN = gethostname()
        cert.set_serial_number(1000)
        cert.gmtime_adj_notBefore(0)
        cert.gmtime_adj_notAfter(10*365*24*60*60)
        cert.set_issuer(cert.get_subject())
        cert.set_pubkey(k)
        cert.sign(k, 'sha1')
 
        open(join(cert_dir, CERT_FILE), "wt").write(
            crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
        open(join(cert_dir, KEY_FILE), "wt").write(
            crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
 
        create_self_signed_cert('.')
        
    return(ssl_created, cert_dir, CERT_FILE, KEY_FILE)


Vidjil Team's avatar
Vidjil Team committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
#########################################################################
def handle_error():
    """
    Custom error handler that returns correct status codes,
    adapted from http://www.web2pyslices.com/slice/show/1529/custom-error-routing
    """
    
    code = request.vars.code
    request_url = request.vars.request_url
    ticket = request.vars.ticket
 
    log.error("[%s] %s" % (code, ticket))

    if code is not None and request_url != request.url:# Make sure error url is not current url to avoid infinite loop.
        response.status = int(code) # Assign the error status code to the current response. (Must be integer to work.)
 
    if code == '403':
        return "Not authorized"
    elif code == '404':
        return "Not found"
    elif code == '500':
        # Get ticket URL:
        ticket_url = "<a href='%(scheme)s://%(host)s/admin/default/ticket/%(ticket)s' target='_blank'>%(ticket)s</a>" % {'scheme':'https','host':request.env.http_host,'ticket':ticket}
 
        # Email a notice, etc:
        mail.send(to=['contact@vidjil.org'],
                  subject="[Vidjil] web2py error",
                  message="Error Ticket:  %s" % ticket_url)

    return "Server error"
315 316 317 318 319 320 321 322 323 324 325 326





#########################################################################
##TODO remove useless function ( maybe used by web2py internally )



#########################################################################
##not used
327 328 329 330 331 332 333 334 335
def call():
    """
    exposes services. for example:
    http://..../[app]/default/call/jsonrpc
    decorate with @services.jsonrpc the functions to expose
    supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv
    """
    return service()

336 337
#########################################################################
##not used
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
@auth.requires_signature()
def data():
    """
    http://..../[app]/default/data/tables
    http://..../[app]/default/data/create/[table]
    http://..../[app]/default/data/read/[table]/[id]
    http://..../[app]/default/data/update/[table]/[id]
    http://..../[app]/default/data/delete/[table]/[id]
    http://..../[app]/default/data/select/[table]
    http://..../[app]/default/data/search/[table]
    but URLs must be signed, i.e. linked with
      A('table',_href=URL('data/tables',user_signature=True))
    or with the signed load operator
      LOAD('default','data.load',args='tables',ajax=True,user_signature=True)
    """
    return dict(form=crud())

355 356
#########################################################################
## not used
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
@auth.requires_login()
@auth.requires_membership('admin')
def add_membership(): 
    response.title = ""

    user_query = db(db.auth_user).select()
    group_query = db(~(db.auth_group.role.like("user%"))).select()
 
    form = SQLFORM.factory(
        Field('user', requires=IS_IN_SET([r.id for r in user_query], labels=[r.first_name+" "+r.last_name for r in user_query])),
        Field('group', requires=IS_IN_SET([r.id for r in group_query], labels=[r.role for r in group_query]))
        )

    if form.validate():
        db.auth_membership.insert(user_id=form.vars.user,
                                  group_id=form.vars.group)
        response.flash = "membership added"
        
    return dict(form=form)

377 378
#########################################################################
## not used
379
def upload_file():
Marc Duez's avatar
Marc Duez committed
380
        import shutil, os.path
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
        
        try:
            # Get the file from the form
            f = request.vars['files[]']
            p = request.vars['patient_id[]']
            i = request.vars['info[]']
            d = request.vars['date[]']
            
            # Store file
            id = db.sequence_file.insert(data_file = db.sequence_file.data_file.store(f.file, f.filename))
             
            record = db.sequence_file[id]
            path_list = []
            path_list.append(request.folder)
            path_list.append('uploads')
            path_list.append(record['data_file'])
            size =  shutil.os.path.getsize(shutil.os.path.join(*path_list))
            
            db.sequence_file[id] = dict(size_file=size ,
                                        patient_id=p,
                                        sampling_date=d,
                                        info=i)
            
            File = db(db.sequence_file.id==id).select()[0]
            res = dict(files=[{"name": str(f.filename), 
                               "size": size, 
                               "url": URL(f='download', args=[File['data_file']])
                               }])
            
            return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
 
        except:
            res = dict(files=[{"name": "kuik", "size": 0, "error": "fail!!" }])
            return gluon.contrib.simplejson.dumps(res, separators=(',',':'))
 
416 417
#########################################################################
## not used
418 419 420 421 422 423 424 425 426 427 428 429
def delete_file():
        try:
            id = request.args[0]
            query=db(db.sequence_file.id==id)
            patient_id=query.select()[0].patient_id
            query.delete()
            session.flash = "file deleted"
            redirect( URL(f='patient', args=[patient_id]) )
        except:
            redirect( URL(f='patient', args=[patient_id]) )
        

430 431
#########################################################################
## not used
432 433
def upload():
        return dict()
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
    
def user():
    """
    exposes:
    http://..../[app]/default/user/login
    http://..../[app]/default/user/logout
    http://..../[app]/default/user/register
    http://..../[app]/default/user/profile
    http://..../[app]/default/user/retrieve_password
    http://..../[app]/default/user/change_password
    http://..../[app]/default/user/manage_users (requires membership in
    use @auth.requires_login()
        @auth.requires_membership('group name')
        @auth.requires_permission('read','table name',record_id)
    to decorate functions that need access control
    """
    return dict(form=auth())