def validateBarcodeSampleTubeLabel(self, new_barcode_sample_tube_label): field_name = SavePlanFieldNames.BARCODE_SAMPLE_TUBE_LABEL if new_barcode_sample_tube_label: valid = True self.validationErrors[field_name] = [] if not is_valid_length(new_barcode_sample_tube_label, MAX_LENGTH_PLAN_NAME): valid = False self.validationErrors[field_name].append('Error, Tube Label length should be %s characters maximum.' % str(MAX_LENGTH_PLAN_NAME)) if valid: self.validationErrors.pop(field_name, None) else: self.validationErrors.pop(field_name, None)
def _validate_plan_name(input, selectedTemplate, planObj): errorMsg = None if not is_valid_chars(input.strip()): errorMsg = "Plan name should contain only numbers, letters, spaces, and the following: . - _" else: value = input.strip() if value: if not is_valid_length(value, iondb.rundb.plan.views.MAX_LENGTH_PLAN_NAME): errorMsg = "Plan name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH %(str(iondb.rundb.plan.views.MAX_LENGTH_PLAN_NAME)) else: planObj.get_planObj().planDisplayedName = value planObj.get_planObj().planName = value.replace(' ', '_') return errorMsg
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_plan_name(input, selectedTemplate, planObj): errorMsg = None if not is_valid_chars(input.strip()): errorMsg = "Plan name should contain only numbers, letters, spaces, and the following: . - _" else: value = input.strip() if value: if not is_valid_length( value, iondb.rundb.plan.views.MAX_LENGTH_PLAN_NAME): errorMsg = "Plan name" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH % ( str(iondb.rundb.plan.views.MAX_LENGTH_PLAN_NAME)) else: planObj.get_planObj().planDisplayedName = value planObj.get_planObj().planName = value.replace(' ', '_') return errorMsg
def validateBarcodeSampleTubeLabel(self, new_barcode_sample_tube_label): field_name = SavePlanFieldNames.BARCODE_SAMPLE_TUBE_LABEL if new_barcode_sample_tube_label: valid = True self.validationErrors[field_name] = [] if not is_valid_length(new_barcode_sample_tube_label, MAX_LENGTH_PLAN_NAME): valid = False self.validationErrors[field_name].append( 'Error, Tube Label length should be %s characters maximum.' % str(MAX_LENGTH_PLAN_NAME)) if valid: self.validationErrors.pop(field_name, None) else: self.validationErrors.pop(field_name, None)
def validateNote(self, new_note_value): field_name = SavePlanFieldNames.NOTE if new_note_value: valid = True self.validationErrors[field_name] = [] if not is_valid_chars(new_note_value): valid = False self.validationErrors[field_name].append('Error, Note should contain only numbers, letters, spaces, and the following: . - _') if not is_valid_length(new_note_value, MAX_LENGTH_NOTES): valid = False self.validationErrors[field_name].append('Error, Note length should be %s characters maximum.' % str(MAX_LENGTH_NOTES)) if valid: self.validationErrors.pop(field_name, None) else: self.validationErrors.pop(field_name, None)
def _validate_notes(input, selectedTemplate, planObj): errorMsg = None if input: if not is_valid_chars(input): errorMsg = "Notes" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS else: value = input.strip() if value: if not is_valid_length(value, iondb.rundb.plan.views.MAX_LENGTH_NOTES): errorMsg = "Notes" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH %(str(iondb.rundb.plan.views.MAX_LENGTH_NOTES)) else: planObj.get_expObj().notes = value else: planObj.get_expObj().notes = "" return errorMsg
def _validate_projects(input, selectedTemplate, planObj): errorMsg = None projects = '' if input: for project in input.split(";"): if not is_valid_chars(project.strip()): errorMsg = "Project should contain only numbers, letters, spaces, and the following: . - _" else: value = input.strip() if value: if not is_valid_length(value, iondb.rundb.plan.views.MAX_LENGTH_PROJECT_NAME): errorMsg = "Project name length should be " + str(iondb.rundb.plan.views.MAX_LENGTH_PROJECT_NAME) + " characters maximum." else: projects += project.strip() projects += ',' return errorMsg, projects
def validateField(self, field_name, new_field_value): if field_name == 'templateName': self.validationErrors[field_name] = [] valid = True if not new_field_value: self.validationErrors[field_name].append('Error, please enter a Template Name.') valid = False if not is_valid_chars(new_field_value): self.validationErrors[field_name].append('Error, Template Name should contain only numbers, letters, spaces, and the following: . - _') valid = False if not is_valid_length(new_field_value, MAX_LENGTH_PLAN_NAME): self.validationErrors[field_name].append('Error, Template Name length should be %s characters maximum. It is currently %s characters long.' % (str(MAX_LENGTH_PLAN_NAME), str(len(new_field_value)))) valid = False if valid: self.validationErrors.pop(field_name, None)
def _validate_notes(input, selectedTemplate, planObj): errorMsg = None if input: if not is_valid_chars(input): errorMsg = "Notes" + iondb.rundb.plan.views.ERROR_MSG_INVALID_CHARS else: value = input.strip() if value: if not is_valid_length( value, iondb.rundb.plan.views.MAX_LENGTH_NOTES): errorMsg = "Notes" + iondb.rundb.plan.views.ERROR_MSG_INVALID_LENGTH % ( str(iondb.rundb.plan.views.MAX_LENGTH_NOTES)) else: planObj.get_expObj().notes = value else: planObj.get_expObj().notes = "" return errorMsg
def validatePlanName(self, new_plan_name): field_name = SavePlanFieldNames.PLAN_NAME self.validationErrors[field_name] = [] valid = True if not new_plan_name: self.validationErrors[field_name].append('Error, please enter a Plan Name.') valid = False if not is_valid_chars(new_plan_name): self.validationErrors[field_name].append('Error, Plan Name should contain only numbers, letters, spaces, and the following: . - _') valid = False if not is_valid_length(new_plan_name, MAX_LENGTH_PLAN_NAME): self.validationErrors[field_name].append('Error, Plan Name length should be %s characters maximum. It is currently %s characters long.' % (str(MAX_LENGTH_PLAN_NAME), str(len(new_plan_name)))) valid = False if valid: self.validationErrors.pop(field_name, None)
def validateNote(self, new_note_value): field_name = SavePlanFieldNames.NOTE if new_note_value: valid = True self.validationErrors[field_name] = [] if not is_valid_chars(new_note_value): valid = False self.validationErrors[field_name].append( 'Error, Note should contain only numbers, letters, spaces, and the following: . - _' ) if not is_valid_length(new_note_value, MAX_LENGTH_NOTES): valid = False self.validationErrors[field_name].append( 'Error, Note length should be %s characters maximum.' % str(MAX_LENGTH_NOTES)) if valid: self.validationErrors.pop(field_name, None) else: self.validationErrors.pop(field_name, None)
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_projects(input, selectedTemplate, planObj): errorMsg = None projects = '' if input: for project in input.split(";"): if not is_valid_chars(project.strip()): errorMsg = "Project should contain only numbers, letters, spaces, and the following: . - _" else: value = input.strip() if value: if not is_valid_length( value, iondb.rundb.plan.views.MAX_LENGTH_PROJECT_NAME): errorMsg = "Project name length should be " + str( iondb.rundb.plan.views.MAX_LENGTH_PROJECT_NAME ) + " characters maximum." else: projects += project.strip() projects += ',' return errorMsg, projects
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 validatePlanName(self, new_plan_name): field_name = SavePlanFieldNames.PLAN_NAME self.validationErrors[field_name] = [] valid = True if not new_plan_name: self.validationErrors[field_name].append( 'Error, please enter a Plan Name.') valid = False if not is_valid_chars(new_plan_name): self.validationErrors[field_name].append( 'Error, Plan Name should contain only numbers, letters, spaces, and the following: . - _' ) valid = False if not is_valid_length(new_plan_name, MAX_LENGTH_PLAN_NAME): self.validationErrors[field_name].append( 'Error, Plan Name length should be %s characters maximum. It is currently %s characters long.' % (str(MAX_LENGTH_PLAN_NAME), str(len(new_plan_name)))) valid = False if valid: self.validationErrors.pop(field_name, None)
def validateField(self, field_name, new_field_value): if field_name == 'templateName': self.validationErrors[field_name] = [] valid = True if not new_field_value: self.validationErrors[field_name].append( 'Error, please enter a Template Name.') valid = False if not is_valid_chars(new_field_value): self.validationErrors[field_name].append( 'Error, Template Name should contain only numbers, letters, spaces, and the following: . - _' ) valid = False if not is_valid_length(new_field_value, MAX_LENGTH_PLAN_NAME): self.validationErrors[field_name].append( 'Error, Template Name length should be %s characters maximum. It is currently %s characters long.' % (str(MAX_LENGTH_PLAN_NAME), str(len(new_field_value)))) valid = False if valid: self.validationErrors.pop(field_name, None)
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 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 validateStep(self): any_samples = False self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME] = [] self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID] = [] self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION] = [] self.validationErrors[SavePlanFieldNames.BAD_TUBE_LABEL] = [] self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID] = [] if self.prepopulatedFields[KitsFieldNames.BARCODES]: for barcode in self.prepopulatedFields[KitsFieldNames.BARCODES]: if self.validate_field(self.savedFields[self.__get_index_key(SavePlanFieldNames.BARCODE_SAMPLE_NAME, barcode.pk)], self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME]): any_samples = True external_id = self.savedFields[self.__get_index_key(SavePlanFieldNames.BARCODE_SAMPLE_EXTERNAL_ID, barcode.pk)] if external_id: self.validate_field(external_id, self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]) description = self.savedFields[self.__get_index_key(SavePlanFieldNames.BARCODE_SAMPLE_DESCRIPTION, barcode.pk)] if description: self.validate_field(description, self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION], False, MAX_LENGTH_SAMPLE_DESCRIPTION) ir_set_id = self.savedFields[self.__get_index_key(SavePlanFieldNames.IR_SET_ID, barcode.pk)] if ir_set_id and not (str(ir_set_id).isdigit()): self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID].append(ir_set_id) else: for i in self.prepopulatedFields[SavePlanFieldNames.CHIP_RANGE]: if self.validate_field(self.savedFields[self.__get_index_key(SavePlanFieldNames.SAMPLE_NAME, i)], self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME]): any_samples = True tube_label = self.savedFields[self.__get_index_key(SavePlanFieldNames.TUBE_LABEL, i)] if tube_label and not is_valid_length(tube_label, MAX_LENGTH_SAMPLE_TUBE_LABEL): self.validationErrors[SavePlanFieldNames.BAD_TUBE_LABEL].append(tube_label) external_id = self.savedFields[self.__get_index_key(SavePlanFieldNames.SAMPLE_EXTERNAL_ID, i)] if external_id: self.validate_field(external_id, self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]) description = self.savedFields[self.__get_index_key(SavePlanFieldNames.SAMPLE_DESCRIPTION, i)] if description: self.validate_field(description, self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION], False, MAX_LENGTH_SAMPLE_DESCRIPTION) ir_set_id = self.savedFields[self.__get_index_key(SavePlanFieldNames.IR_SET_ID, i)] if ir_set_id and not (str(ir_set_id).isdigit()): self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID].append(ir_set_id) if any_samples: self.validationErrors.pop(SavePlanFieldNames.NO_SAMPLES, None) else: self.validationErrors[SavePlanFieldNames.NO_SAMPLES] = "You must enter at least one sample" if not self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME]: self.validationErrors.pop(SavePlanFieldNames.BAD_SAMPLE_NAME, None) if not self.validationErrors[SavePlanFieldNames.BAD_TUBE_LABEL]: self.validationErrors.pop(SavePlanFieldNames.BAD_TUBE_LABEL, None) if not self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]: self.validationErrors.pop(SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID, None) if not self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION]: self.validationErrors.pop(SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION, None) if not self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID]: self.validationErrors.pop(SavePlanFieldNames.BAD_IR_SET_ID, None)
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 validateStep(self): any_samples = False self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME] = [] self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID] = [] self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION] = [] self.validationErrors[SavePlanFieldNames.BAD_TUBE_LABEL] = [] self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID] = [] if self.prepopulatedFields[KitsFieldNames.BARCODES]: for barcode in self.prepopulatedFields[KitsFieldNames.BARCODES]: if self.validate_field( self.savedFields[self.__get_index_key( SavePlanFieldNames.BARCODE_SAMPLE_NAME, barcode.pk)], self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_NAME]): any_samples = True external_id = self.savedFields[self.__get_index_key( SavePlanFieldNames.BARCODE_SAMPLE_EXTERNAL_ID, barcode.pk)] if external_id: self.validate_field( external_id, self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]) description = self.savedFields[self.__get_index_key( SavePlanFieldNames.BARCODE_SAMPLE_DESCRIPTION, barcode.pk)] if description: self.validate_field( description, self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION], False, MAX_LENGTH_SAMPLE_DESCRIPTION) ir_set_id = self.savedFields[self.__get_index_key( SavePlanFieldNames.IR_SET_ID, barcode.pk)] if ir_set_id and not (str(ir_set_id).isdigit()): self.validationErrors[ SavePlanFieldNames.BAD_IR_SET_ID].append(ir_set_id) else: for i in self.prepopulatedFields[SavePlanFieldNames.CHIP_RANGE]: if self.validate_field( self.savedFields[self.__get_index_key( SavePlanFieldNames.SAMPLE_NAME, i)], self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_NAME]): any_samples = True tube_label = self.savedFields[self.__get_index_key( SavePlanFieldNames.TUBE_LABEL, i)] if tube_label and not is_valid_length( tube_label, MAX_LENGTH_SAMPLE_TUBE_LABEL): self.validationErrors[ SavePlanFieldNames.BAD_TUBE_LABEL].append(tube_label) external_id = self.savedFields[self.__get_index_key( SavePlanFieldNames.SAMPLE_EXTERNAL_ID, i)] if external_id: self.validate_field( external_id, self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]) description = self.savedFields[self.__get_index_key( SavePlanFieldNames.SAMPLE_DESCRIPTION, i)] if description: self.validate_field( description, self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION], False, MAX_LENGTH_SAMPLE_DESCRIPTION) ir_set_id = self.savedFields[self.__get_index_key( SavePlanFieldNames.IR_SET_ID, i)] if ir_set_id and not (str(ir_set_id).isdigit()): self.validationErrors[ SavePlanFieldNames.BAD_IR_SET_ID].append(ir_set_id) if any_samples: self.validationErrors.pop(SavePlanFieldNames.NO_SAMPLES, None) else: self.validationErrors[ SavePlanFieldNames. NO_SAMPLES] = "You must enter at least one sample" if not self.validationErrors[SavePlanFieldNames.BAD_SAMPLE_NAME]: self.validationErrors.pop(SavePlanFieldNames.BAD_SAMPLE_NAME, None) if not self.validationErrors[SavePlanFieldNames.BAD_TUBE_LABEL]: self.validationErrors.pop(SavePlanFieldNames.BAD_TUBE_LABEL, None) if not self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID]: self.validationErrors.pop( SavePlanFieldNames.BAD_SAMPLE_EXTERNAL_ID, None) if not self.validationErrors[ SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION]: self.validationErrors.pop( SavePlanFieldNames.BAD_SAMPLE_DESCRIPTION, None) if not self.validationErrors[SavePlanFieldNames.BAD_IR_SET_ID]: self.validationErrors.pop(SavePlanFieldNames.BAD_IR_SET_ID, None)