def writeInitCodeJS(self, buff): inits = getInitVals(self.params) for param in inits: if inits[param].val in ['', None, 'None', 'none']: inits[param].val = 'undefined' # Check for unsupported units if inits['units'].val == 'from exp settings': inits['units'] = copy.copy(self.exp.settings.params['Units']) if inits['units'].val in ['cm', 'deg', 'degFlatPos', 'degFlat']: msg = ("'{units}' units for your '{name}' Slider are not currently supported for PsychoJS: " "switching units to 'height'. Note, this will affect the size and positioning of '{name}'.") logging.warning(msg.format(units=inits['units'].val, name=inits['name'].val)) inits['units'].val = "height" boolConverter = {False: 'false', True: 'true'} sliderStyles = {'slider': 'SLIDER', 'scrollbar': 'SLIDER', '()': 'RATING', 'rating': 'RATING', 'radio': 'RADIO', 'labels45': 'LABELS_45', 'whiteOnBlack': 'WHITE_ON_BLACK', 'triangleMarker': 'TRIANGLE_MARKER'} # If no style given, set default 'rating' as list if len(inits['styles'].val) == 0: inits['styles'].val = 'rating' # reformat styles for JS # concatenate styles and tweaks tweaksList = utils.listFromString(self.params['styleTweaks'].val) if type(inits['styles'].val) == list: # from an experiment <2021.1 stylesList = inits['styles'].val + tweaksList else: stylesList = [inits['styles'].val] + tweaksList stylesListJS = [sliderStyles[this] for this in stylesList] # if not isinstance(inits['styleTweaks'].val, (tuple, list)): # inits['styleTweaks'].val = [inits['styleTweaks'].val] # inits['styleTweaks'].val = ', '.join(["visual.Slider.StyleTweaks.{}".format(adj) # for adj in inits['styleTweaks'].val]) # convert that to string and JS-ify inits['styles'].val = py2js.expression2js(str(stylesListJS)) inits['styles'].valType = 'code' inits['depth'] = -self.getPosInRoutine() # build up an initialization string for Slider(): initStr = ("{name} = new visual.Slider({{\n" " win: psychoJS.window, name: '{name}',\n" " size: {size}, pos: {pos}, units: {units},\n" " labels: {labels}, ticks: {ticks},\n" " granularity: {granularity}, style: {styles},\n" " color: new util.Color({color}), \n" " fontFamily: {font}, bold: true, italic: false, depth: {depth}, \n" ).format(**inits) initStr += (" flip: {flip},\n" "}});\n\n").format(flip=boolConverter[inits['flip'].val]) buff.writeIndentedLines(initStr)
def test_listFromString(): assert ['yes', 'no'] == utils.listFromString("yes, no") assert ['yes', 'no'] == utils.listFromString("[yes, no]") assert ['yes', 'no'] == utils.listFromString("(yes, no)") assert ['yes', 'no'] == utils.listFromString("'yes', 'no'") assert ['yes', 'no'] == utils.listFromString("['yes', 'no']") assert ['yes', 'no'] == utils.listFromString("('yes', 'no')") # this should be returned without ast.literal_eval being used assert ['yes', 'no'] == utils.listFromString(('yes', 'no')) # this would create a syntax error in ast.literal_eval assert ["Don't", "Do"] == utils.listFromString("Don't, Do")
def importItems(self, items): """Import items from csv or excel sheet and convert to list of dicts. Will also accept a list of dicts. Note, for csv and excel files, 'options' must contain comma separated values, e.g., one, two, three. No parenthesis, or quotation marks required. Parameters ---------- items : Excel or CSV file, list of dicts Items used to populate the Form Returns ------- List of dicts A list of dicts, where each list entry is a dict containing all fields for a single Form item """ def _checkSynonyms(items, fieldNames): """Checks for updated names for fields (i.e. synonyms)""" replacedFields = set() for field in _synonyms: synonym = _synonyms[field] for item in items: if synonym in item: # convert to new name item[field] = item[synonym] del item[synonym] replacedFields.add(field) for field in replacedFields: fieldNames.append(field) fieldNames.remove(_synonyms[field]) logging.warning("Form {} included field no longer used {}. " "Replacing with new name '{}'".format( self.name, _synonyms[field], field)) def _checkRequiredFields(fieldNames): """Checks for required headings (do this after checking synonyms)""" for hdr in _knownFields: # is it required and/or present? if _knownFields[hdr] == _REQUIRED and hdr not in fieldNames: raise ValueError("Missing header ({}) in Form ({}). " "Headers found were: {}".format( hdr, self.name, fieldNames)) def _checkTypes(types, itemText): """A nested function for testing the number of options given Raises ValueError if n Options not > 1 """ itemDiff = set([types]) - set(_knownRespTypes) for incorrItemType in itemDiff: if incorrItemType == _REQUIRED: if self._itemsFile: itemsFileStr = ("in items file '{}'".format( self._itemsFile)) else: itemsFileStr = "" msg = ("Item {}{} is missing a required " "value for its response type. Permitted types are " "{}.".format(itemText, itemsFileStr, _knownRespTypes)) if self.autoLog: logging.error(msg) raise ValueError(msg) def _addDefaultItems(items): """ Adds default items when missing. Works in-place. Parameters ---------- items : List of dicts headers : List of column headers for each item """ def isPresent(d, field): # check if the field is there and not empty on this row return (field in d and d[field] not in [None, '']) missingHeaders = [] defaultValues = _knownFields for index, item in enumerate(items): defaultValues['index'] = index for header in defaultValues: # if header is missing of val is None or '' if not isPresent(item, header): oldHeader = header.replace('item', 'question') if isPresent(item, oldHeader): item[header] = item[oldHeader] logging.warning( "{} is a deprecated heading for Forms. " "Use {} instead".format(oldHeader, header)) continue # Default to colour scheme if specified if defaultValues[header] in ['fg', 'bg', 'em']: item[header] = self.colorScheme[ defaultValues[header]] else: item[header] = defaultValues[header] missingHeaders.append(header) msg = "Using default values for the following headers: {}".format( missingHeaders) if self.autoLog: logging.info(msg) if self.autoLog: logging.info("Importing items...") if not isinstance(items, list): # items is a conditions file self._itemsFile = Path(items) items, fieldNames = importConditions(items, returnFieldNames=True) else: # we already have a list so lets find the fieldnames fieldNames = set() for item in items: fieldNames = fieldNames.union(item) fieldNames = list(fieldNames) # convert to list at the end self._itemsFile = None _checkSynonyms(items, fieldNames) _checkRequiredFields(fieldNames) # Add default values if entries missing _addDefaultItems(items) # Convert options to list of strings for idx, item in enumerate(items): if item['ticks']: item['ticks'] = listFromString(item['ticks']) if 'tickLabels' in item and item['tickLabels']: item['tickLabels'] = listFromString(item['tickLabels']) if 'options' in item and item['options']: item['options'] = listFromString(item['options']) # Check types [_checkTypes(item['type'], item['itemText']) for item in items] # Check N options > 1 # Randomise items if requested if self.randomize: shuffle(items) return items