Beispiel #1
0
def enableNext(bid, j):
    [v, s, p] = bid.split(".")
    try:
        nextBtn = btn_node("%s.%s.%d" % (v, s, int(p) + 1), j)
    except LookupError:  ## done with this step
        try:
            nextBtn = btn_node("%s.%d.%d" % (v, int(s) + 1, 0), j)
        except LookupError:  ## done with this visit
            vNode = get_visit(bid, j)
            lib.set_node(vNode, True, jlib.VCOMPLETE)
            return int(v) + 1
    ## we have the next valid button: enable it!
    ## ... unless we're in redo-mode: skip completed steps until we've resumed
    resume = lib.get_node(j, jlib.RESUME)
    if resume:  # redo-mode!
        if compareBids(resume, nextBtn["id"]):  # we've caught up to the resume point
            lib.set_node(j, None, jlib.RESUME)  # end redo-mode
            lib.set_here(nextBtn, "disabled", False)  # enable as normal
        else:  # stay in redo-mode
            if lib.get_node(nextBtn, "time") == "":  # not stamped yet, so enable
                lib.set_here(nextBtn, "disabled", False)
            else:  # timestamped; this was complete before redo began.
                # don't enable; rather, advance progress & skip to next-next
                nextID = lib.get_node(nextBtn, "id")
                setProgress(nextID, get_visit(bid, j))
                return enableNext(nextID, j)
    else:  # no redo occurring, normal enable
        lib.set_here(nextBtn, "disabled", False)
    return int(v)
Beispiel #2
0
 def processLogin(self):
     subject = self.subject
     print 'SUBJECT['+ subject+']'
     self.mySubjectDir = j.checkSubjDir(subject)
     j.dirStructure(subject)  # verify/create subject's directory structure (visits, etc.)
     self.jsonpath = os.path.join(self.mySubjectDir,
                                  "%s_experiment_info.json" % subject)
     if os.path.exists(self.jsonpath):
         self.json = lib.load_json(self.jsonpath)
         self.json['flotscript'] = ''
         if 'flotscript_header' in self.json:
             del self.json['flotscript_header']
     else:
         lib.set_node(self.json, subject, j.SUBJID) ## get a fresh json_template
     ## find the next incomplete visit (if study complete, display final visit)
     for v in range(j.NUM_VISITS):
         self.setTab(v)
         if not lib.get_node(self.json, self.vNodePath + j.VCOMPLETE):
             break
     visit = v
     # handle subject's group assignment and create visit/session dir based on group, if needed.
     group = lib.get_node(self.json, j.GROUP)
     if not group == "":
         ### create & populate session dir
         self.visitDir, correctVisit = j.checkVisitDir(subject, visit, group, self.json)
         if not correctVisit == visit:
             self.subjectMoved("<b>Cannot move on to next visit without ROI masks!</b>", "false")
         self.setTab(correctVisit)
         return self.renderAndSave()   # saves the json, and renders the page
     else:
         return self.modalthing()  # render modal to assign group -> call setgroup() -> save json & render normally
Beispiel #3
0
 def completionChecks(self):
     tab = self.TabID
     vComplete = lib.get_node(self.json,self.vNodePath + j.VCOMPLETE)
     print "complete:", vComplete,
     if vComplete:
         return
     progress = lib.get_node(self.json, self.vNodePath + j.VPROGRESS)
     print "... progress:",progress, "tab:",tab
     if progress == "":    ## activate first step, deactivate all else
         if (tab == 0):
             [bt.enableOnly(self.json, t, None) for t in range(1,len(j.VISIT_LIST))]
             bt.enableOnly(self.json, tab, 'first')
         else:
             prevVNodePath = self.vNodePath[:-2] + str(tab-1) + ":"
             lastVComplete = lib.get_node(self.json, prevVNodePath + j.VCOMPLETE)
             if lastVComplete:
                 bt.enableOnly(self.json, tab, 'first')
             else:
                 bt.enableOnly(self.json, tab, None)
     else:
         activeVisit = bt.enableNext(progress,self.json)
         if not activeVisit == tab:
             bt.enableOnly(self.json, tab, None)
             if activeVisit < j.NUM_VISITS:
                 bt.enableOnly(self.json, activeVisit, 'first')
             else:
                 self.subjectMoved("<b>All visits complete!</b>", "false")
     return
Beispiel #4
0
def nameLogfile(node,subject,useInfo=None):
    """
    Out: absolute path where stdout/stderr logfile should go
    """
    # error check: useInfo has 'run','history'; node has 'action', 'id'
    if not useInfo:     ## allow us to (optionally) use a different btn's info
        useInfo=node    ## use this btn's info by default
    run = useInfo['run']
    timeStr = time.strftime("_%b%d_%H%M%S_",time.localtime(float(lib.get_node(useInfo,'history:-1'))))

    action = node['action']
    tab = int(lib.get_node(node,'id')[0])
    filename = str(run) + timeStr + action + '.log'
    return os.path.abspath(os.path.join(lib.SUBJS, subject, "session%d"%tab, filename))
Beispiel #5
0
def checkVisitDir(subject,visit,group,myJson):    
    v = int(visit)
    maxV = len(VISIT_LIST)  ## off-by-one error???
    if v > maxV:   ##obo danger
        print "ERROR checkVisitDir: requested",v, "but the max visit number is", maxV
        raise Exception("Invalid visit number requested.")
    if v < 0:  ## supports visits[-1] indexing
        v = maxV + 1 + v
    subjDir = checkSubjDir(subject)  # checks/creates subjDir
    myVisitDir = getVisitDir(subject, v)
    if not os.path.exists(myVisitDir):
        raise OSError("Can't find this visit directory! %s"%myVisitDir)
    vType = get_node(bt.get_visit(v, myJson), VTYPE)
    # for realtime visits, verify murfi templates (in 'scripts' directory) exist
    if vType == 'realtime':
        # first check that localizer masks are present
        if not os.path.exists(os.path.join(subjDir, 'mask')):
            print "You can't do a realtime run until there are subject masks!"
            v = 0
            return getVisitDir(subject, v), v
        murfiDir = os.path.abspath(os.path.join(myVisitDir, "scripts"))
        if not os.path.exists(murfiDir):
            ### this is dumb. i should make it an importable library.
            print "Trying to create rt session for visit", v
            subprocess.Popen(["python", "createRtSession.py", subject, str(v), 'none', group], cwd=RTSCRIPTSDIR)
    return myVisitDir, v
Beispiel #6
0
 def doMakoLogin(self,subject=None,visit=None):
     self.subject = subject    ## keep this accessible to other methods
     self.mySubjectDir = j.checkSubjDir(subject)
     self.jsonpath = os.path.join(self.mySubjectDir, "%s_experiment_info.json"%subject)
     if os.path.exists(self.jsonpath):
         self.json = lib.load_json(self.jsonpath)  
     else:
         lib.set_node(self.json,subject,j.SUBJID) ## get a fresh json_template
     visit = lib.get_node(self.json,j.TAB)
     self.setTab(visit)          # activates the tab
     # handle subject's group assignment and create visit/session dir based on group, if needed.
     group = lib.get_node(self.json, j.GROUP)
     if not group == "":
         self.visitDir = j.checkVisitDir(subject,visit,group, self.json) ### create & populate session dir        
         return self.renderAndSave()   # saves the json, and renders the page
     else:
         return self.modalthing()  # render modal to assign group -> call setgroup() -> save json & render normally
Beispiel #7
0
def btn_node(bid,j):
    """
    Helper designed for MakoRoot.formHandler().
    Returns a dict of button properties for the desired button.
    In: * bid (str), "x.y.z"
        * j (dict-like), MakoRoot.json
    Out: * node (dict-like), subnode of j, for that button
    """
    [v,s,p] = bid.split('.')
    return lib.get_node(j,['protocol',v,'steps',s,'parts',p])    
Beispiel #8
0
def get_visit(info,j):
    if isinstance(info,str):   ## info is a buttonID, like v.s.p
        visit = int(info[0])
    elif isinstance(info,unicode):  ## also probably a buttonID?
        visit = int(info[0])
    elif isinstance(info,int):  ## info is just a visit number
        visit = info
    else:
        print "info in get_visit:", info
        raise TypeError("get_visit: Didn't expect a %s"%(type(info)))
    return lib.get_node(j, [jlib.FULLSTUDY,visit])
Beispiel #9
0
def parent_node(bid,j):
    """
    Helper that get's a button's parent-step's parts.
    Returns the parent node of a button, which must be the "parts"
    list of the step that the button is a part of. There may be only
    one part in the list.
    In: * bid (str), "x.y.z"
        * j (dict-like), MakoRoot.json
    Out: * parent (list), subnode of j, for that button
    """
    [v,s,p] = bid.split('.')   ## won't be using p
    return lib.get_node(j, ['protocol',v,'steps',s,'parts'])    
Beispiel #10
0
def checkPsychoDone(subject, node):
    tab = int(lib.get_node(node,'id')[0])
    expName = os.path.splitext(os.path.basename(node['file']))[0]
    filename = expName + getTimestamp(node, -2) + 'trials.psydat'
    doneFile = os.path.abspath(os.path.join(lib.SUBJS, subject, "session%d"%tab, 'ltTaskData', expName, filename))
    print "Psychopy complete? Checking", doneFile
    if os.path.exists(doneFile):
        datFile = fromFile(doneFile)
        print "Found .psydat file. Collected",datFile.thisN, "trials, so finished is",datFile.finished
        return datFile.finished
    else:
        return False
Beispiel #11
0
def sib_node(bid,j,z):
    """
    Button groups consist of sibling buttons. Get sibling z for the
    button bid.
    Returns a dict of button properties for the desired sibling button.
    In: * bid (str), eg "x.y.z"
        * j (dict-like), MakoRoot.json
        * z (int/str), indicating which sibling to return
    Out: * node (dict-like), subnode of j, for the sibling button
    """
    [v,s,p] = bid.split('.')
    return lib.get_node(j,['protocol',v,'steps',s,'parts',z])    
Beispiel #12
0
def movementRedo(j, tab):
    ## Use this visit's progress to figure out what things to redo.
    ## Collaborate with enableNext() to use timestamps as a high-water mark.
    ##    j (dict) = full json for the subject
    ##    tab (int) = visit/session number
    vNode = get_visit(tab, j)
    vBids = visitBids(tab, j)  ## full, ordered list of all button ids for this visit
    progress = getProgress(vNode)
    currentBid = vBids[vBids.index(progress) + 1]  ## for current step (after progress)
    ## 1. build prereq button id list
    # 1.1 get base prereqs, plus prereqs based on current step's action keyword
    action = lib.get_node(btn_node(currentBid, j), "action")
    prereqs = []
    for btn in [btn_node(bid, j) for bid in visitBids(tab, j)]:
        if btn.has_key("prereqFor"):
            prfor = btn["prereqFor"].split(".")  # some prereqs are '.'-joined
            if ("all" in prfor) or (action in prfor):
                prereqs.append(btn["id"])
    # 1.2 get all siblings of the current step
    sibs = [lib.get_node(sib, "id") for sib in parent_node(currentBid, j)]
    prereqs.extend(sibs)
    # 2. prepare prereqs for redo: clear all tstamps, uncheck checkboxes.
    for prereq in prereqs:
        prNode = btn_node(prereq, j)
        clearTimeStamp(prNode)
        if lib.get_node(prNode, "action") == "":  # it's a checkbox
            lib.set_here(prNode, "checked", False)  # clear checkboxes
    # 3. save progress to study-wide resume pointer, unless we're restarting a redo.
    resume = lib.get_node(j, jlib.RESUME)
    if resume:
        pass  ## restarting redo-mode due to another movement
    else:  ## enter fresh redo-mode
        lib.set_node(j, progress, jlib.RESUME)
    # 4. reset progress so that the first prereq is the next thing to happen.
    vBids.insert(0, "")  # if 1st prereq is tab.0.0, then progress = "" (as expected)
    resetBid = vBids[vBids.index(prereqs[0]) - 1]
    setProgress(resetBid, vNode)
    return
Beispiel #13
0
 def updateProgress(self,bid):
     """
     Only call this once an action (or structural scan) is done.
     """
     # do nothing if progress > bid (because we're redoing something)
     curProg= lib.get_node(self.json, self.vNodePath + j.VPROGRESS)
     if curProg == "":    # beginning of a visit, or we're in redo mode.
         bt.setProgress(bid, bt.get_visit(bid,self.json))
     elif bt.compareBids(curProg, bid):   # step
         print "new progress will be",bid
         bt.setProgress(bid, bt.get_visit(bid,self.json))
     else:
         print "completed", bid, ", but that's less than", curProg
     return
Beispiel #14
0
 def makoRealtimeStim(self, btn_value, node):
     murfNode = bt.sib_node(node['id'], self.json, 0)
     stimLog = bt.nameLogfile(node, self.subject, murfNode)
     self.run = murfNode['run']   ## in case of accidental logout
     self.flotJavascript()
     # based on group, use proper stimulus file
     group = lib.get_node(self.json, j.GROUP)
     if group == "high":
         psychofile = j.HIFILE
     elif group == "low":
         psychofile = j.LOFILE
     lib.set_here(node, 'file', psychofile)  # record which stimulus file was used
     # ready to launch!
     self.stimProc, h = lib.doStim(self.subject, self.TabID, self.run, stimLog, psychofile,self.json["study_info"]["group"])
     lib.set_here(node,'disabled', True)  # this button only launches. 'End Murfi' cleans up
     return
Beispiel #15
0
 def subjectMoved(self, reason, moved="false"):
     print self.TabID, reason, moved
     ## timestamp and store comment
     infoNode = lib.get_node(self.json, ['protocol',self.TabID,'visit_info'])
     bt.timeStamp(infoNode)
     infoNode['comments'].append(reason)
     ## determine which steps need to be redone
     if moved == "true":
         ## QUESTION: should we be on an active visit only?
         ## enable & clear printed timestamp on localizers & test equipment
         bt.movementRedo(self.json, self.TabID)
         ## REMINDER: on functional runs, they should get to hit "end" (minor bug)
     elif moved == "false":
         pass  ## just adding the comment to visit_info
     else:
         raise Exception("Invalid value for 'moved' from radio button!")
     return self.renderAndSave()
Beispiel #16
0
def dirStructure(subject):
    # subject (str): subject id
    for v, vNode in enumerate(VISIT_LIST):
        vType = get_node(vNode, VTYPE)
        vDir = getVisitDir(subject, v)
        if vType == "prepost":
            if os.path.exists(vDir):
                pass 
            else:
                os.mkdir(vDir)
        elif vType == "realtime":
            rtDataDir = os.path.abspath(os.path.join(vDir, "data"))
            if not os.path.exists(rtDataDir):
                os.makedirs(rtDataDir)
        else:
            print "Didn't understand visit type", vType, "for visit number", v
            raise Exception("Subject's directory structure creation/verification failed.")
    return
Beispiel #17
0
def rtDone(j, bid):
    # Purpose: When an RT run completes, this advances the "progress" key
    #   to the last part of the RT step, so that enableNext can enable the
    #   next step or next visit.
    # This is a helper function for handling an "End Murfi" buttonpress. Nothing 
    #   else should call it.
    # Inputs:
    # j (dict) = full json the subject
    # bid (str) = "x.y.z", as above

    node = btn_node(bid, j)
    lib.set_here(node, 'done', True)
    # disable all sibling parts
    parent = parent_node(bid, j)
    for p in range(0, len(parent)):
        lib.set_here(parent[p], 'disabled', True)
    # progress is set to button ID of the last part of this step.    
    lastID = lib.get_node(parent, ['-1', 'id'])
    setProgress(lastID, get_visit(bid, j))
Beispiel #18
0
def flotSetup(subject):
    flotcalls = []
    actTempl = os.path.join(os.path.abspath("."), "template_active.json")
    refTempl = os.path.join(os.path.abspath("."), "template_reference.json")
    u = os.path.expanduser('~')
    for v, vNode in enumerate(VISIT_LIST):
        vType = get_node(vNode, VTYPE)
        vDir = getVisitDir(subject, v)
        if vType == "realtime":
            rtDataDir = os.path.abspath(os.path.join(vDir, "data"))
            for run in range(1, STUDY_INFO['runsPerRtVisit'] + 1):
                filebase = "run%03d_"%run
                actFile = os.path.join(rtDataDir, filebase + "active.json")
                refFile = os.path.join(rtDataDir, filebase + "reference.json")
                placeholder = '$("#rtgraph%d_%d")' % (v, run)
                flotcalls.append('flotplot("%s", "%s", %s);' % (os.path.relpath(actFile, u), 
                                                                os.path.relpath(refFile, u), placeholder))
                if not os.path.exists(actFile):
                    shutil.copy(actTempl, os.path.join(rtDataDir, actFile))
                if not os.path.exists(refFile):
                    shutil.copy(refTempl, os.path.join(rtDataDir, refFile))
    return flotcalls
Beispiel #19
0
def checkVisitDir(subject,visit,group,myJson):
    print SUBJS
    print RTSCRIPTSDIR
    v = int(visit)
    maxV = len(VISIT_LIST)  ## off-by-one error???
    if v > maxV:   ##obo danger
        print "ERROR checkVisitDir: requested",v, "but the max visit number is", maxV
        raise Exception("Invalid visit number requested.")
    if v < 0:  ## supports visits[-1] indexing
        v = maxV + 1 + v
    myVisitDir = os.path.abspath(os.path.join(SUBJS,subject,'session%d'%v))
    print myVisitDir
    if not os.path.exists(myVisitDir):
        if not os.path.exists(checkSubjDir(subject)):
            raise OSError("Can't find directory for this subject.")
        else:
            os.mkdir(myVisitDir)
            vType = get_node(myJson, "%s:%d:%s"%(FULLSTUDY, v, VTYPE))
            if vType == 'realtime':
                os.mkdir(os.path.join(myVisitDir, 'data'))  ## psychopy data directory.
                ### this is dumb. i should make it an importable library.
                print "Trying to create rt session for visit", visit
                subprocess.Popen(["python", "createRtSession.py", subject, str(v), 'none', group], cwd=RTSCRIPTSDIR)
    return myVisitDir
Beispiel #20
0
 def subjectMoved(reason):
     print self.tab, reason
     infoNode = lib.get_node(self.json, ['Protocol',self.tab,'visit_info'])
     bt.timeStamp(infoNode)
     infoNode['comments'].append(reason)
     return self.renderAndSave()
Beispiel #21
0
def getTimestamp(node,index=-1):  ## return time formatted like psychopy.data.getDateStr (but not unicode)
    return time.strftime("%Y_%b_%d_%H%M",time.localtime(float(lib.get_node(node,'history:%d'%index))))
Beispiel #22
0
def visitBids(tab, j):
    vNode = get_visit(int(tab), j)
    vBids = []
    for s in range(0, len(vNode['steps'])):
        vBids.extend([str(p) for p in lib.get_node(vNode,"steps:%d:parts:*:id"%s)])
    return vBids
Beispiel #23
0
def getProgress(visitNode):
    return lib.get_node(visitNode, jlib.VPROGRESS)