def validate_field(self, value, bad_samples, validate_leading_chars=True, max_length=MAX_LENGTH_SAMPLE_NAME): exists = False if value: exists = True if not is_valid_chars(value): bad_samples.append(value) if validate_leading_chars and value not in bad_samples and is_invalid_leading_chars(value): bad_samples.append(value) if value not in bad_samples and not is_valid_length(value, max_length): bad_samples.append(value) return exists
def _validate_sample(input, selectedTemplate, planObj): errorMsg = None sampleDisplayedName = "" if not input: errorMsg = "Required column is empty" else: if not is_valid_chars(input): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS elif is_invalid_leading_chars(input): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LEADING_CHARS else: value = input.strip() if value: if not is_valid_length(value, iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH %(str(iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME)) else: sampleDisplayedName = value sample = value.replace(' ', '_') return errorMsg, sampleDisplayedName
def validate_field(self, value, bad_samples, validate_leading_chars=True, max_length=MAX_LENGTH_SAMPLE_NAME): exists = False if value: exists = True if not is_valid_chars(value): bad_samples.append(value) if validate_leading_chars and value not in bad_samples and is_invalid_leading_chars( value): bad_samples.append(value) if value not in bad_samples and not is_valid_length( value, max_length): bad_samples.append(value) return exists
def _validate_sample(input, selectedTemplate, planObj): errorMsg = None sampleDisplayedName = "" if not input: errorMsg = "Required column is empty" else: if not is_valid_chars(input): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS elif is_invalid_leading_chars(input): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LEADING_CHARS else: value = input.strip() if value: if not is_valid_length( value, iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME): errorMsg = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH % ( str(iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME)) else: sampleDisplayedName = value sample = value.replace(' ', '_') return errorMsg, sampleDisplayedName
def _validate_barcodedSamples(input, selectedTemplate, barcodeKitName, planObj): errorMsg = None barcodedSampleJson = {} #{"bc10_noPE_sample3":{"26":"IonXpress_010"},"bc04_noPE_sample1":{"20":"IonXpress_004"},"bc08_noPE_sample2":{"24":"IonXpress_008"}} #20121122-new JSON format #{"bcSample1":{"barcodes":["IonXpress_001","IonXpress_002"]},"bcSample2":{"barcodes":["IonXpress_003"]}} barcodes = list(dnaBarcode.objects.filter(name = barcodeKitName).values('id', 'id_str').order_by('id_str')) if len(barcodes) == 0: errorMsg = "Barcode "+ barcodeKitName + " cannot be found. " return errorMsg, barcodedSampleJson errorMsgDict = {} try: for barcode in barcodes: key = barcode["id_str"] + plan_csv_writer.COLUMN_BC_SAMPLE_KEY sample = input.get(key, "") if sample: if not is_valid_chars(sample): errorMsgDict[key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS elif is_invalid_leading_chars(sample): errorMsgDict[key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LEADING_CHARS else: value = sample.strip() if value: if not is_valid_length(value, iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME): errorMsgDict[key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH %(str(iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME)) else: barcodedSample = barcodedSampleJson.get(value, {}) if barcodedSample: barcodeList = barcodedSample.get("barcodes", []) if barcodeList: barcodeList.append(barcode["id_str"]) else: barcodeDict = { "barcodes" : [barcode["id_str"]] } barcodedSampleJson[sample.strip()] = barcodeDict else: barcodeDict = { "barcodes" : [barcode["id_str"]] } barcodedSampleJson[sample.strip()] = barcodeDict except: logger.exception(format_exc()) errorMsg = "Internal error during barcoded sample processing" if errorMsgDict: return simplejson.dumps(errorMsgDict), barcodedSampleJson if not barcodedSampleJson: errorMsg = "Required column is empty. At least one barcoded sample is required. " else: planObj.get_easObj().barcodedSamples = barcodedSampleJson return errorMsg, barcodedSampleJson
def save_plan_or_template(request, planOid): """ Saving new or edited plan/template to db (source: plan template wizard) Editing a planned run from having 1 sample to 2 samples will result in one edited planned run and one new planned run """ def isReusable(submitIntent): return not (submitIntent == 'savePlan' or submitIntent == 'updatePlan') if request.method != 'POST': logger.exception(format_exc()) return HttpResponse(json.dumps({ "error": "Error, unsupported HTTP Request method (%s) for plan update." % request.method }), mimetype="application/json") # Process Inputs # pylint:disable=E1103 json_data = simplejson.loads(request.raw_post_data) submitIntent = json_data.get('submitIntent', '') logger.debug( 'views.save_plan_or_template POST.raw_post_data... simplejson Data: "%s"' % json_data) logger.debug("views.save_plan_or_template submitIntent=%s" % submitIntent) # saving Template or Planned Run isReusable = isReusable(submitIntent) runModeValue = json_data.get('runMode', 'single') isPlanGroupValue = runModeValue == 'pe' and not isReusable libraryKeyValue = json_data.get('libraryKey', '') forward3primeAdapterValue = json_data.get('forward3primeAdapter', '') msgvalue = 'Run Plan' if not isReusable else 'Template' if runModeValue == 'pe': return HttpResponse(json.dumps({ "error": "Error, paired-end plan is no longer supported. %s will not be saved." % (msgvalue) }), mimetype="application/html") planDisplayedNameValue = json_data.get('planDisplayedName', '').strip() noteValue = json_data.get('notes_workaround', '') # perform server-side validation to avoid things falling through the crack if not planDisplayedNameValue: return HttpResponse(json.dumps( {"error": "Error, please enter a %s Name." % (msgvalue)}), mimetype="application/html") if not is_valid_chars(planDisplayedNameValue): return HttpResponse(json.dumps( {"error": "Error, %s Name" % (msgvalue) + ERROR_MSG_INVALID_CHARS}), mimetype="application/html") if not is_valid_length(planDisplayedNameValue, MAX_LENGTH_PLAN_NAME): return HttpResponse(json.dumps({ "error": "Error, %s Name" % (msgvalue) + ERROR_MSG_INVALID_LENGTH % (str(MAX_LENGTH_PLAN_NAME)) }), mimetype="application/html") if noteValue: if not is_valid_chars(noteValue): return HttpResponse(json.dumps({ "error": "Error, %s note" % (msgvalue) + ERROR_MSG_INVALID_CHARS }), mimetype="application/html") if not is_valid_length(noteValue, MAX_LENGTH_NOTES): return HttpResponse(json.dumps({ "error": "Error, Note" + ERROR_MSG_INVALID_LENGTH % (str(MAX_LENGTH_NOTES)) }), mimetype="application/html") # Projects projectObjList = get_projects(request.user, json_data) # IonReporterUploader configuration and samples selectedPlugins = json_data.get('selectedPlugins', {}) IRconfigList = json_data.get('irConfigList', []) IRU_selected = False for uploader in selectedPlugins.values(): if 'ionreporteruploader' in uploader['name'].lower( ) and uploader['name'] != 'IonReporterUploader_V1_0': IRU_selected = True #if IRU is set to autoRun, user does not need to select the plugin explicitly. user could have set all IRU versions to autorun IRU_autorun_count = 0 if not IRU_selected: IRU_autoruns = Plugin.objects.filter( name__icontains="IonReporter", selected=True, active=True, autorun=True).exclude( name__icontains="IonReporterUploader_V1_0").order_by('-name') IRU_autorun_count = IRU_autoruns.count() if IRU_autorun_count > 0: IRU_selected = True if IRU_selected: samples_IRconfig = json_data.get('sample_irConfig', '') if samples_IRconfig: samples_IRconfig = ','.join(samples_IRconfig) #generate UUID for unique setIds id_uuid = {} setids = [ir.get('setid', "") for ir in IRconfigList] if setids: for setid in set(setids): if setid: id_uuid[setid] = str(uuid.uuid4()) for ir_config in IRconfigList: setid = ir_config.get('setid', '') if setid: ir_config['setid'] += '__' + id_uuid[setid] if IRU_autorun_count > 0 and not samples_IRconfig: #if more than one IRU version is set to autorun and user does not explicitly select one, #gui shows workflow config for IRU v1.0 samples_IRconfig = json_data.get('samples_workaround', '') # Samples barcodeIdValue = json_data.get('barcodeId', '') barcodedSamples = '' sampleValidationErrorMsg = '' sampleValidationErrorMsg_leadingChars = '' sampleValidationErrorMsg_length = '' # one Plan will be created per entry in sampleList # samples for barcoded Plan have a separate field (barcodedSamples) if isReusable: # samples entered only when saving planned run (not template) sampleList = [''] elif barcodeIdValue: # a barcode Set is selected sampleList = [''] bcSamplesValues = json_data.get('bcSamples_workaround', '') bcDictionary = {} bcId = "" for token in bcSamplesValues.split(","): if ((token.find("bcKey|")) == 0): bcId, bcId_str = token.split("|")[1:] else: sample = token.strip() if bcId and sample: if not is_valid_chars(sample): sampleValidationErrorMsg += sample + ', ' elif is_invalid_leading_chars(sample): sampleValidationErrorMsg_leadingChars += sample + ", " elif not is_valid_length(sample, MAX_LENGTH_SAMPLE_NAME): sampleValidationErrorMsg_length += sample + ", " bcDictionary.setdefault(sample, {}).setdefault('barcodes', []).append(bcId_str) bcId = "" barcodedSamples = simplejson.dumps(bcDictionary) logger.debug( "views.save_plan_or_template after simplejson.dumps... barcodedSamples=%s;" % (barcodedSamples)) if not bcDictionary: transaction.rollback() return HttpResponse(json.dumps({ "error": "Error, please enter at least one barcode sample name." }), mimetype="application/html") else: # Non-barcoded samples sampleList = [] if IRU_selected: samples = samples_IRconfig else: samples = json_data.get('samples_workaround', '') for sample in samples.split(','): if sample.strip(): if not is_valid_chars(sample): sampleValidationErrorMsg += sample + ', ' elif is_invalid_leading_chars(sample): sampleValidationErrorMsg_leadingChars += sample + ", " elif not is_valid_length(sample, MAX_LENGTH_SAMPLE_NAME): sampleValidationErrorMsg_length += sample + ", " else: sampleList.append(sample) logger.debug("views.save_plan_or_template sampleList=%s " % (sampleList)) if len( sampleList ) == 0 and not sampleValidationErrorMsg and not sampleValidationErrorMsg_leadingChars and not sampleValidationErrorMsg_length: transaction.rollback() return HttpResponse(json.dumps({ "error": "Error, please enter a sample name for the run plan." }), mimetype="application/html") # Samples validation if sampleValidationErrorMsg or sampleValidationErrorMsg_leadingChars or sampleValidationErrorMsg_length: message = "" if sampleValidationErrorMsg: message = "Error, sample name" + ERROR_MSG_INVALID_CHARS message = message + ' <br>Please fix: ' + sampleValidationErrorMsg + '<br>' if sampleValidationErrorMsg_leadingChars: message = message + "Error, sample name" + ERROR_MSG_INVALID_LEADING_CHARS message = message + ' <br>Please fix: ' + sampleValidationErrorMsg_leadingChars + '<br>' if sampleValidationErrorMsg_length: message = message + "Error, sample name" + ERROR_MSG_INVALID_LENGTH % ( str(MAX_LENGTH_SAMPLE_NAME)) message = message + ' <br>Please fix: ' + sampleValidationErrorMsg_length transaction.rollback() return HttpResponse(json.dumps({"error": message}), mimetype="application/html") selectedPluginsValue = json_data.get('selectedPlugins', {}) # end processing input data # Edit/Create Plan(s) if int(planOid) == 0: edit_existing_plan = False else: edit_existing_plan = True for i, sample in enumerate(sampleList): logger.debug( "...LOOP... views.save_plan_or_template SAMPLE=%s; isSystem=%s; isReusable=%s; isPlanGroup=%s " % (sample.strip(), json_data["isSystem"], isReusable, isPlanGroupValue)) # add IonReporter config values for each sample if len(IRconfigList) > 0: for uploader in selectedPluginsValue.values(): if 'ionreporteruploader' in uploader['name'].lower(): if len(IRconfigList) > 1 and not barcodeIdValue: uploader['userInput'] = [IRconfigList[i]] else: uploader['userInput'] = IRconfigList if len(sampleList) > 1: inputPlanDisplayedName = planDisplayedNameValue + '_' + sample.strip( ) else: inputPlanDisplayedName = planDisplayedNameValue selectedTemplatingKit = json_data.get('templatekitname', '') samplePrepInstrumentType = json_data.get('samplePrepInstrumentType', '') if samplePrepInstrumentType == 'ionChef': selectedTemplatingKit = json_data.get('templatekitionchefname', '') #PDD-TODO: remove the x_ prefix. the x_ prefix is just a reminder what the obsolete attributes to remove during the next phase kwargs = { 'planDisplayedName': inputPlanDisplayedName, "planName": inputPlanDisplayedName.replace(' ', '_'), 'usePreBeadfind': toBoolean(json_data['usePreBeadfind'], False), 'usePostBeadfind': toBoolean(json_data['usePostBeadfind'], False), 'preAnalysis': True, 'runType': json_data['runType'], 'templatingKitName': selectedTemplatingKit, 'controlSequencekitname': json_data.get('controlsequence', ''), 'runMode': runModeValue, 'isSystem': toBoolean(json_data['isSystem'], False), 'isReusable': isReusable, 'isPlanGroup': isPlanGroupValue, 'username': request.user.username, 'isFavorite': toBoolean(json_data.get('isFavorite', 'False'), False), 'pairedEndLibraryAdapterName': json_data.get('pairedEndLibraryAdapterName', ''), 'samplePrepKitName': json_data.get('samplePrepKitName', ''), 'planStatus': "planned", 'x_autoAnalyze': True, 'x_barcodedSamples': barcodedSamples, 'x_barcodeId': barcodeIdValue, 'x_bedfile': json_data.get('bedfile', ''), 'x_chipType': json_data.get('chipType', ''), 'x_flows': json_data.get('flows', None), 'x_forward3primeadapter': forward3primeAdapterValue, ###'_isReverseRun': = self.isReverseRun 'x_library': json_data.get('library', ''), 'x_libraryKey': libraryKeyValue, 'x_librarykitname': json_data.get('librarykitname', ''), 'x_notes': noteValue, 'x_regionfile': json_data.get('regionfile', ''), 'x_sample': sample.strip().replace(' ', '_'), 'x_sampleDisplayedName': sample.strip(), 'x_selectedPlugins': selectedPluginsValue, 'x_sequencekitname': json_data.get('sequencekitname', ''), 'x_variantfrequency': json_data.get('variantfrequency', ''), } planTemplate = None #if we're changing a plan from having 1 sample to say 2 samples, we need to UPDATE 1 plan and CREATE 1 plan!! try: if not edit_existing_plan: planTemplate, extra_kwargs = PlannedExperiment.objects.save_plan( -1, **kwargs) else: planTemplate, extra_kwargs = PlannedExperiment.objects.save_plan( planOid, **kwargs) edit_existing_plan = False # Update QCtype thresholds qcTypes = QCType.objects.all() for qcType in qcTypes: qc_threshold = json_data.get(qcType.qcName, '') if qc_threshold: # get existing PlannedExperimentQC if any plannedExpQcs = PlannedExperimentQC.objects.filter( plannedExperiment=planTemplate.id, qcType=qcType.id) if len(plannedExpQcs) > 0: for plannedExpQc in plannedExpQcs: plannedExpQc.threshold = qc_threshold plannedExpQc.save() else: kwargs = { 'plannedExperiment': planTemplate, 'qcType': qcType, 'threshold': qc_threshold } plannedExpQc = PlannedExperimentQC(**kwargs) plannedExpQc.save() # add/remove projects if projectObjList: #TODO: refactor this logic to simplify using django orm projectNameList = [project.name for project in projectObjList] for currentProject in planTemplate.projects.all(): if currentProject.name not in projectNameList: planTemplate.projects.remove(currentProject) for projectObj in projectObjList: planTemplate.projects.add(projectObj) else: planTemplate.projects.clear() except ValidationError, err: transaction.rollback() logger.exception(format_exc()) message = "Internal error while trying to save the plan. " for msg in err.messages: message += str(msg) message += " " return HttpResponse(json.dumps({"error": message}), mimetype="application/json") except Exception as excp: transaction.rollback() logger.exception(format_exc()) message = "Internal error while trying to save the plan. %s" % ( excp.message) return HttpResponse(json.dumps({"error": message}), mimetype="application/json")
def _validate_barcodedSamples(input, selectedTemplate, barcodeKitName, planObj): errorMsg = None barcodedSampleJson = {} #{"bc10_noPE_sample3":{"26":"IonXpress_010"},"bc04_noPE_sample1":{"20":"IonXpress_004"},"bc08_noPE_sample2":{"24":"IonXpress_008"}} #20121122-new JSON format #{"bcSample1":{"barcodes":["IonXpress_001","IonXpress_002"]},"bcSample2":{"barcodes":["IonXpress_003"]}} barcodes = list( dnaBarcode.objects.filter(name=barcodeKitName).values( 'id', 'id_str').order_by('id_str')) if len(barcodes) == 0: errorMsg = "Barcode " + barcodeKitName + " cannot be found. " return errorMsg, barcodedSampleJson errorMsgDict = {} try: for barcode in barcodes: key = barcode["id_str"] + plan_csv_writer.COLUMN_BC_SAMPLE_KEY sample = input.get(key, "") if sample: if not is_valid_chars(sample): errorMsgDict[ key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS elif is_invalid_leading_chars(sample): errorMsgDict[ key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LEADING_CHARS else: value = sample.strip() if value: if not is_valid_length( value, iondb.rundb.plan.views.MAX_LENGTH_SAMPLE_NAME): errorMsgDict[ key] = "Sample name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH % ( str(iondb.rundb.plan.views. MAX_LENGTH_SAMPLE_NAME)) else: barcodedSample = barcodedSampleJson.get(value, {}) if barcodedSample: barcodeList = barcodedSample.get( "barcodes", []) if barcodeList: barcodeList.append(barcode["id_str"]) else: barcodeDict = { "barcodes": [barcode["id_str"]] } barcodedSampleJson[ sample.strip()] = barcodeDict else: barcodeDict = {"barcodes": [barcode["id_str"]]} barcodedSampleJson[ sample.strip()] = barcodeDict except: logger.exception(format_exc()) errorMsg = "Internal error during barcoded sample processing" if errorMsgDict: return simplejson.dumps(errorMsgDict), barcodedSampleJson if not barcodedSampleJson: errorMsg = "Required column is empty. At least one barcoded sample is required. " else: planObj.get_easObj().barcodedSamples = barcodedSampleJson return errorMsg, barcodedSampleJson
def save_plan_or_template(request, planOid): """ Saving new or edited plan/template to db (source: plan template wizard) Editing a planned run from having 1 sample to 2 samples will result in one edited planned run and one new planned run """ def isReusable(submitIntent): return not (submitIntent == 'savePlan' or submitIntent == 'updatePlan') if request.method != 'POST': logger.exception(format_exc()) return HttpResponse(json.dumps({"error": "Error, unsupported HTTP Request method (%s) for plan update." % request.method}), mimetype="application/json") # Process Inputs # pylint:disable=E1103 json_data = simplejson.loads(request.raw_post_data) submitIntent = json_data.get('submitIntent', '') logger.debug('views.save_plan_or_template POST.raw_post_data... simplejson Data: "%s"' % json_data) logger.debug("views.save_plan_or_template submitIntent=%s" % submitIntent) # saving Template or Planned Run isReusable = isReusable(submitIntent) runModeValue = json_data.get('runMode', 'single') isPlanGroupValue = runModeValue == 'pe' and not isReusable libraryKeyValue = json_data.get('libraryKey', '') forward3primeAdapterValue = json_data.get('forward3primeAdapter', '') msgvalue = 'Run Plan' if not isReusable else 'Template' if runModeValue == 'pe': return HttpResponse(json.dumps({"error": "Error, paired-end plan is no longer supported. %s will not be saved." % (msgvalue)}), mimetype="application/html") planDisplayedNameValue = json_data.get('planDisplayedName', '').strip() noteValue = json_data.get('notes_workaround', '') # perform server-side validation to avoid things falling through the crack if not planDisplayedNameValue: return HttpResponse(json.dumps({"error": "Error, please enter a %s Name." %(msgvalue)}), mimetype="application/html") if not is_valid_chars(planDisplayedNameValue): return HttpResponse(json.dumps({"error": "Error, %s Name" %(msgvalue) + ERROR_MSG_INVALID_CHARS}), mimetype="application/html") if not is_valid_length(planDisplayedNameValue, MAX_LENGTH_PLAN_NAME): return HttpResponse(json.dumps({"error": "Error, %s Name" %(msgvalue) + ERROR_MSG_INVALID_LENGTH %(str(MAX_LENGTH_PLAN_NAME))}), mimetype="application/html") if noteValue: if not is_valid_chars(noteValue): return HttpResponse(json.dumps({"error": "Error, %s note" %(msgvalue) + ERROR_MSG_INVALID_CHARS}), mimetype="application/html") if not is_valid_length(noteValue, MAX_LENGTH_NOTES): return HttpResponse(json.dumps({"error": "Error, Note" + ERROR_MSG_INVALID_LENGTH %(str(MAX_LENGTH_NOTES))}), mimetype="application/html") # Projects projectObjList = get_projects(request.user, json_data) # IonReporterUploader configuration and samples selectedPlugins = json_data.get('selectedPlugins', {}) IRconfigList = json_data.get('irConfigList', []) IRU_selected = False for uploader in selectedPlugins.values(): if 'ionreporteruploader' in uploader['name'].lower() and uploader['name'] != 'IonReporterUploader_V1_0': IRU_selected = True #if IRU is set to autoRun, user does not need to select the plugin explicitly. user could have set all IRU versions to autorun IRU_autorun_count = 0 if not IRU_selected: IRU_autoruns = Plugin.objects.filter(name__icontains="IonReporter", selected=True, active=True, autorun=True).exclude(name__icontains="IonReporterUploader_V1_0").order_by('-name') IRU_autorun_count = IRU_autoruns.count() if IRU_autorun_count > 0: IRU_selected = True if IRU_selected: samples_IRconfig = json_data.get('sample_irConfig', '') if samples_IRconfig: samples_IRconfig = ','.join(samples_IRconfig) #generate UUID for unique setIds id_uuid = {} setids = [ir.get('setid', "") for ir in IRconfigList] if setids: for setid in set(setids): if setid: id_uuid[setid] = str(uuid.uuid4()) for ir_config in IRconfigList: setid = ir_config.get('setid', '') if setid: ir_config['setid'] += '__' + id_uuid[setid] if IRU_autorun_count > 0 and not samples_IRconfig: #if more than one IRU version is set to autorun and user does not explicitly select one, #gui shows workflow config for IRU v1.0 samples_IRconfig = json_data.get('samples_workaround', '') # Samples barcodeIdValue = json_data.get('barcodeId', '') barcodedSamples = '' sampleValidationErrorMsg = '' sampleValidationErrorMsg_leadingChars = '' sampleValidationErrorMsg_length = '' # one Plan will be created per entry in sampleList # samples for barcoded Plan have a separate field (barcodedSamples) if isReusable: # samples entered only when saving planned run (not template) sampleList = [''] elif barcodeIdValue: # a barcode Set is selected sampleList = [''] bcSamplesValues = json_data.get('bcSamples_workaround', '') bcDictionary = {} bcId = "" for token in bcSamplesValues.split(","): if ((token.find("bcKey|")) == 0): bcId, bcId_str = token.split("|")[1:] else: sample = token.strip() if bcId and sample: if not is_valid_chars(sample): sampleValidationErrorMsg += sample + ', ' elif is_invalid_leading_chars(sample): sampleValidationErrorMsg_leadingChars += sample + ", " elif not is_valid_length(sample, MAX_LENGTH_SAMPLE_NAME): sampleValidationErrorMsg_length += sample + ", " bcDictionary.setdefault(sample, {}).setdefault('barcodes',[]).append(bcId_str) bcId = "" barcodedSamples = simplejson.dumps(bcDictionary) logger.debug("views.save_plan_or_template after simplejson.dumps... barcodedSamples=%s;" % (barcodedSamples)) if not bcDictionary: transaction.rollback() return HttpResponse(json.dumps({"error": "Error, please enter at least one barcode sample name."}), mimetype="application/html") else: # Non-barcoded samples sampleList = [] if IRU_selected: samples = samples_IRconfig else: samples = json_data.get('samples_workaround', '') for sample in samples.split(','): if sample.strip(): if not is_valid_chars(sample): sampleValidationErrorMsg += sample + ', ' elif is_invalid_leading_chars(sample): sampleValidationErrorMsg_leadingChars += sample + ", " elif not is_valid_length(sample, MAX_LENGTH_SAMPLE_NAME): sampleValidationErrorMsg_length += sample + ", " else: sampleList.append(sample) logger.debug("views.save_plan_or_template sampleList=%s " % (sampleList)) if len(sampleList) == 0 and not sampleValidationErrorMsg and not sampleValidationErrorMsg_leadingChars and not sampleValidationErrorMsg_length: transaction.rollback() return HttpResponse(json.dumps({"error": "Error, please enter a sample name for the run plan."}), mimetype="application/html") # Samples validation if sampleValidationErrorMsg or sampleValidationErrorMsg_leadingChars or sampleValidationErrorMsg_length: message = "" if sampleValidationErrorMsg: message = "Error, sample name" + ERROR_MSG_INVALID_CHARS message = message + ' <br>Please fix: ' + sampleValidationErrorMsg + '<br>' if sampleValidationErrorMsg_leadingChars: message = message + "Error, sample name" + ERROR_MSG_INVALID_LEADING_CHARS message = message + ' <br>Please fix: ' + sampleValidationErrorMsg_leadingChars + '<br>' if sampleValidationErrorMsg_length: message = message + "Error, sample name" + ERROR_MSG_INVALID_LENGTH %(str(MAX_LENGTH_SAMPLE_NAME)) message = message + ' <br>Please fix: ' + sampleValidationErrorMsg_length transaction.rollback() return HttpResponse(json.dumps({"error": message}), mimetype="application/html") selectedPluginsValue = json_data.get('selectedPlugins', {}) # end processing input data # Edit/Create Plan(s) if int(planOid) == 0: edit_existing_plan = False else: edit_existing_plan = True for i, sample in enumerate(sampleList): logger.debug("...LOOP... views.save_plan_or_template SAMPLE=%s; isSystem=%s; isReusable=%s; isPlanGroup=%s " % (sample.strip(), json_data["isSystem"], isReusable, isPlanGroupValue)) # add IonReporter config values for each sample if len(IRconfigList) > 0: for uploader in selectedPluginsValue.values(): if 'ionreporteruploader' in uploader['name'].lower(): if len(IRconfigList) > 1 and not barcodeIdValue: uploader['userInput'] = [IRconfigList[i]] else: uploader['userInput'] = IRconfigList if len(sampleList) > 1: inputPlanDisplayedName = planDisplayedNameValue + '_' + sample.strip() else: inputPlanDisplayedName = planDisplayedNameValue selectedTemplatingKit = json_data.get('templatekitname', '') samplePrepInstrumentType = json_data.get('samplePrepInstrumentType', '') if samplePrepInstrumentType == 'ionChef': selectedTemplatingKit = json_data.get('templatekitionchefname', '') #PDD-TODO: remove the x_ prefix. the x_ prefix is just a reminder what the obsolete attributes to remove during the next phase kwargs = { 'planDisplayedName': inputPlanDisplayedName, "planName": inputPlanDisplayedName.replace(' ', '_'), 'usePreBeadfind': toBoolean(json_data['usePreBeadfind'], False), 'usePostBeadfind': toBoolean(json_data['usePostBeadfind'], False), 'preAnalysis': True, 'runType': json_data['runType'], 'templatingKitName': selectedTemplatingKit, 'controlSequencekitname': json_data.get('controlsequence', ''), 'runMode': runModeValue, 'isSystem': toBoolean(json_data['isSystem'], False), 'isReusable': isReusable, 'isPlanGroup': isPlanGroupValue, 'username': request.user.username, 'isFavorite': toBoolean(json_data.get('isFavorite', 'False'), False), 'pairedEndLibraryAdapterName': json_data.get('pairedEndLibraryAdapterName', ''), 'samplePrepKitName': json_data.get('samplePrepKitName', ''), 'planStatus' : "planned", 'x_autoAnalyze': True, 'x_barcodedSamples': barcodedSamples, 'x_barcodeId': barcodeIdValue, 'x_bedfile': json_data.get('bedfile', ''), 'x_chipType': json_data.get('chipType', ''), 'x_flows': json_data.get('flows', None), 'x_forward3primeadapter': forward3primeAdapterValue, ###'_isReverseRun': = self.isReverseRun 'x_library': json_data.get('library', ''), 'x_libraryKey': libraryKeyValue, 'x_librarykitname': json_data.get('librarykitname', ''), 'x_notes': noteValue, 'x_regionfile': json_data.get('regionfile', ''), 'x_sample': sample.strip().replace(' ', '_'), 'x_sampleDisplayedName': sample.strip(), 'x_selectedPlugins': selectedPluginsValue, 'x_sequencekitname': json_data.get('sequencekitname', ''), 'x_variantfrequency': json_data.get('variantfrequency', ''), } planTemplate = None #if we're changing a plan from having 1 sample to say 2 samples, we need to UPDATE 1 plan and CREATE 1 plan!! try: if not edit_existing_plan: planTemplate, extra_kwargs = PlannedExperiment.objects.save_plan(-1, **kwargs) else: planTemplate, extra_kwargs = PlannedExperiment.objects.save_plan(planOid, **kwargs) edit_existing_plan = False # Update QCtype thresholds qcTypes = QCType.objects.all() for qcType in qcTypes: qc_threshold = json_data.get(qcType.qcName, '') if qc_threshold: # get existing PlannedExperimentQC if any plannedExpQcs = PlannedExperimentQC.objects.filter(plannedExperiment=planTemplate.id, qcType=qcType.id) if len(plannedExpQcs) > 0: for plannedExpQc in plannedExpQcs: plannedExpQc.threshold = qc_threshold plannedExpQc.save() else: kwargs = { 'plannedExperiment': planTemplate, 'qcType': qcType, 'threshold': qc_threshold } plannedExpQc = PlannedExperimentQC(**kwargs) plannedExpQc.save() # add/remove projects if projectObjList: #TODO: refactor this logic to simplify using django orm projectNameList = [project.name for project in projectObjList] for currentProject in planTemplate.projects.all(): if currentProject.name not in projectNameList: planTemplate.projects.remove(currentProject) for projectObj in projectObjList: planTemplate.projects.add(projectObj) else: planTemplate.projects.clear() except ValidationError, err: transaction.rollback() logger.exception(format_exc()) message = "Internal error while trying to save the plan. " for msg in err.messages: message += str(msg) message += " " return HttpResponse(json.dumps({"error": message}), mimetype="application/json") except Exception as excp: transaction.rollback() logger.exception(format_exc()) message = "Internal error while trying to save the plan. %s" %(excp.message) return HttpResponse(json.dumps({"error": message}), mimetype="application/json")