def migrate_interpretation_request_cancer_to_interpreted_genome_v6( json_dict, assembly, interpretation_service, reference_database_versions, software_versions, report_url, comments): """ :type json_dict: dict :type assembly: Assembly :type interpretation_service: str :type reference_database_versions: dict :type software_versions: dict :type report_url: str :type comments: list :rtype: CancerInterpretationRequest_6_0_0 """ if CancerInterpretationRequest_5_0_0.validate(CancerInterpretationRequest_5_0_0.fromJsonDict(json_dict)): raise MigrationError( "Cannot transform a cancer interpretation request in version 5.0.0 into an interpreted genome") if CancerInterpretationRequest_6_0_0.validate(CancerInterpretationRequest_6_0_0.fromJsonDict(json_dict)): raise MigrationError( "Cannot transform a cancer interpretation request in version 6.0.0 into an interpreted genome") types = [ InterpretedGenome_6_0_0, CancerInterpretedGenome_5_0_0, CancerInterpretationRequest_4_0_0 ] migrations = [ lambda x: x, MigrateReports500To600().migrate_cancer_interpreted_genome, lambda x: MigrateReports400To500().migrate_cancer_interpretation_request_to_cancer_interpreted_genome( old_instance=x, assembly=assembly, interpretation_service=interpretation_service, reference_database_versions=reference_database_versions, software_versions=software_versions, report_url=report_url, comments=comments) ] return MigrationHelpers.migrate(json_dict, types, migrations)
def migrate_cancer_interpretation_request_to_cancer_interpreted_genome( self, old_instance, assembly, interpretation_service, reference_database_versions, software_versions, report_url=None, comments=None): """ NOTE: we migrate from a model where only one sample and one participant is supported, thus we do not need a list of samples or participants :type old_instance: reports_4_0_0.CancerInterpretationRequest :type assembly: reports_5_0_0.Assembly :type interpretation_service: str :type reference_database_versions: dict :type software_versions: dict :type report_url: str :type comments: list :rtype: reports_5_0_0.CancerInterpretedGenome """ new_instance = self.convert_class( self.new_model.CancerInterpretedGenome, old_instance) # :type: reports_5_0_0.CancerInterpretedGenome new_instance.interpretationRequestId = old_instance.reportRequestId new_instance.interpretationRequestVersion = old_instance.reportVersion new_instance.interpretationService = interpretation_service new_instance.referenceDatabasesVersions = reference_database_versions if not isinstance(software_versions, dict): software_versions = {} software_versions['tiering'] = old_instance.tieringVersion new_instance.softwareVersions = software_versions new_instance.reportUrl = report_url new_instance.comments = comments participant_id = old_instance.cancerParticipant.individualId tumor_samples = old_instance.cancerParticipant.tumourSamples if not tumor_samples: raise MigrationError( "There is no tumour sample to perform the migration") elif len(tumor_samples) > 1: raise MigrationError( "There are several tumour samples, cannot decide which to use '{}'" .format(str(tumor_samples))) sample_id = tumor_samples[0].sampleId new_instance.variants = self.convert_collection( old_instance.tieredVariants, self._migrate_reported_variant_cancer, assembly=assembly, participant_id=participant_id, sample_id=sample_id) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.CancerInterpretedGenome)
def migrate_cancer_exit_questionnaire(self, old_instance, assembly): """ :type old_instance: reports_5_0_0.CancerExitQuestionnaire :type assembly: reports_5_0_0.Assembly :rtype: reports_6_0_0.CancerExitQuestionnaire """ if assembly is None: raise MigrationError( "Parameter <assembly> is required to migrate cancer exit questionnaire to version 6" ) new_c_eq = self.convert_class( target_klass=self.new_model.CancerExitQuestionnaire, instance=old_instance) new_c_eq.somaticVariantLevelQuestions = self.convert_collection( old_instance.somaticVariantLevelQuestions, self._migrate_somatic_variant_level_question, assembly=assembly) new_c_eq.germlineVariantLevelQuestions = self.convert_collection( old_instance.germlineVariantLevelQuestions, self._migrate_germline_variant_level_question, assembly=assembly) new_c_eq.otherActionableVariants = self.convert_collection( old_instance.otherActionableVariants, self._migrate_other_actionable_variant, assembly=assembly) return self.validate_object( object_to_validate=new_c_eq, object_type=self.new_model.CancerExitQuestionnaire)
def migrate_interpreted_genome_rd(self, old_instance, assembly, interpretation_request_version): """ :type old_instance: reports_4_0_0.InterpretedGenomeRD :type assembly: reports_5_0_0.Assembly :type interpretation_request_version: int :rtype: reports_5_0_0.InterpretedGenomeRD """ if assembly is None or interpretation_request_version is None: raise MigrationError( "Parameters <assembly> and <interpretation_request_version> are required for models earlier than 5.0.0" ) new_instance = self.convert_class( self.new_model.InterpretedGenomeRD, old_instance) # type:self.new_model.InterpretedGenomeRD new_instance.interpretationRequestVersion = interpretation_request_version new_instance.interpretationService = old_instance.companyName new_instance.variants = self.convert_collection( old_instance.reportedVariants, self._migrate_reported_variant, assembly=assembly) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.InterpretedGenomeRD)
def migrate_action(self, old_instance): """ NOTE: fields that cannot be filled are "actionType" :type old_instance: reports_4_0_0.Actions :rtype reports_5_0_0.Action :return: """ new_instance = self.convert_class(self.new_model.Action, old_instance) new_instance.evidenceType = old_instance.actionType new_instance.actionType = None # rename evidence to references new_instance.references = old_instance.evidence # maps the action status if old_instance.status is not None: status = old_instance.status.lower().replace('-', '_') if status == reports_5_0_0.ActionStatus.clinical: new_instance.status = reports_5_0_0.ActionStatus.clinical elif status == reports_5_0_0.ActionStatus.pre_clinical: new_instance.status = reports_5_0_0.ActionStatus.pre_clinical elif status is not None and status != "": raise MigrationError("Action status does not match any known value '{}'".format(status)) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.Action )
def _migrate_reported_variant_cancer(self, old_instance, assembly, participant_id, sample_ids): ne_instance = old_instance.reportedVariantCancer new_instance = self.convert_class( self.new_model.ReportedVariantCancer, ne_instance) # :type: reports_5_0_0.ReportedVariant new_instance.variantCoordinates = self.convert_class( reports_5_0_0.VariantCoordinates, ne_instance) new_instance.variantCoordinates.assembly = self._migrate_assembly( assembly) if old_instance.reportedVariantCancer.cDnaChange: new_instance.cdnaChanges = [ old_instance.reportedVariantCancer.cDnaChange ] if ne_instance.proteinChange: new_instance.proteinChanges = [ne_instance.proteinChange] # NOTE: missing fields: genomicChanges sample_id = sample_ids.get(old_instance.alleleOrigins[0], None) if not sample_id: raise MigrationError('Couldn\'t retrieve Sample ID for {}'.format( old_instance.alleleOrigins[0])) # builds up the VariantCall object # NOTE: fields that cannot be filled "phaseSet" new_instance.variantCalls = [ reports_5_0_0.VariantCall( depthReference=ne_instance.depthReference, depthAlternate=ne_instance.depthAlternate, vaf=ne_instance.vaf, zygosity=reports_5_0_0.Zygosity.na, alleleOrigins=old_instance.alleleOrigins, participantId=participant_id, sampleId=sample_id) ] if ne_instance.commonAf is not None: new_instance.alleleFrequencies = [ reports_5_0_0.AlleleFrequency( study='genomics_england', population='ALL', alternateFrequency=self.convert_string_to_float( ne_instance.commonAf) / 100) ] # NOTE: some fields cannot be filled: "fdp50", "recurrentlyReported", "others" new_instance.variantAttributes = reports_5_0_0.VariantAttributes( ihp=ne_instance.ihp) new_instance.alleleOrigins = old_instance.alleleOrigins new_instance.reportEvents = self.convert_collection( list(zip(ne_instance.reportEvents, new_instance.reportEvents)), self._migrate_report_event_cancer) return new_instance
def _migrate_assembly(self, assembly): new_assembly = None if assembly is not None: if assembly.lower().startswith(reports_5_0_0.Assembly.GRCh37.lower()) \ or assembly.lower().startswith('hg19'): new_assembly = reports_5_0_0.Assembly.GRCh37 elif assembly.lower().startswith( reports_5_0_0.Assembly.GRCh38.lower()): new_assembly = reports_5_0_0.Assembly.GRCh38 else: raise MigrationError( "Assembly does not match any known value '{}'".format( assembly)) return new_assembly
def _extract_variant_details(variant_details): """ The format of variant_details is "chr:pos:ref:alt" """ details = list( map(lambda x: x.strip(), re.compile(":|>").split(variant_details))) if len(details) != 4: raise MigrationError( "Variant details: {variant_details} should have fields chr, pos, ref and alt" .format(variant_details=variant_details)) try: details[1] = int(details[1]) except ValueError: raise MigrationError( "Position {position} is not an integer !".format( position=details[1])) return { "chromosome": details[0], "position": details[1], "reference": details[2], "alternate": details[3], }
def migrate_interpretation_request_rd(self, old_instance, old_ig): """ Migrates a reports_5_0_0.InterpretationRequestRD into a reports_4_0_0.InterpretationRequestRD :type old_instance: reports_5_0_0.InterpretationRequestRD :type old_ig: reports_5_0_0.InterpretedGenomeRD :rtype: reports_4_0_0.InterpretationRequestRD """ new_instance = self.convert_class( self.new_model.InterpretationRequestRD, old_instance) new_instance.versionControl = self.new_model.ReportVersionControl() new_instance.genomeAssemblyVersion = old_instance.genomeAssembly # ensure null lists of files are not passing through if new_instance.bams is None: new_instance.bams = [] if new_instance.vcfs is None: new_instance.vcfs = [] # grabs the list of variants from the interpreted genome new_instance.tieredVariants = self.convert_collection( old_ig.variants, self._migrate_reported_variant) new_instance.tieringVersion = old_ig.softwareVersions.get( "tiering", "") new_instance.analysisVersion = '' new_instance.analysisReturnUri = '' if old_instance.additionalInfo: new_instance.analysisVersion = old_instance.additionalInfo.get( 'analysisVersion') or '' new_instance.analysisReturnUri = old_instance.additionalInfo.get( 'analysisReturnUri', '') new_instance.tieringVersion = old_instance.additionalInfo.get( 'tieringVersion', '') new_instance.complexGeneticPhenomena = old_instance.additionalInfo.get( 'complexGeneticPhenomena') new_instance.cellbaseVersion = old_instance.additionalInfo.get( 'cellbaseVersion', '') new_instance.interpretGenome = bool( distutils.util.strtobool( old_instance.additionalInfo.get('interpretGenome', 'false'))) if not old_instance.pedigree: raise MigrationError( "Cannot reverse migrate an Interpretation Request for RD with null pedigree" ) new_instance.pedigree = MigrateParticipant110To100().migrate_pedigree( old_instance.pedigree) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.InterpretationRequestRD)
def _migrate_action(self, actions): old_instance = actions[0] new_instance = actions[1] new_instance.evidenceType = old_instance.actionType new_instance.actionType = None new_instance.references = old_instance.evidence if old_instance.status is not None: status = old_instance.status.lower().replace('-', '_') if status == reports_5_0_0.ActionStatus.clinical: new_instance.status = reports_5_0_0.ActionStatus.clinical elif status == reports_5_0_0.ActionStatus.pre_clinical: new_instance.status = reports_5_0_0.ActionStatus.pre_clinical elif status is not None and status != "": raise MigrationError( "Action status does not match any known value '{}'".format( status)) return new_instance
def migrate_tumour_sample(self, old_sample, ldp_code): ts = self.new_model.TumourSample tt = self.new_model.TumourType new_sample = self.convert_class(ts, old_sample) # :type: reports_5_0_0.TumourSample tumour_type_enum = [ tt.PRIMARY, tt.METASTATIC_RECURRENCE, tt.RECURRENCE_OF_PRIMARY_TUMOUR, tt.METASTASES ] new_sample.tumourType = old_sample.tumourType if old_sample.tumourType in tumour_type_enum else None new_sample.tumourId = self.convert_int_to_str(value=old_sample.tumourId) if ldp_code is None: raise MigrationError("Cannot migrate '{}' to '{}' having an empty value of 'ldp_code'".format( type(old_sample), type(new_sample)) ) new_sample.LDPCode = ldp_code return self.validate_object(object_to_validate=new_sample, object_type=ts)
def migrate_rd_exit_questionnaire(self, old_instance, assembly): """ :type old_instance: reports_5_0_0.RareDiseaseExitQuestionnaire :type assembly: reports_5_0_0.Assembly :rtype: reports_6_0_0.RareDiseaseExitQuestionnaire """ if assembly is None: raise MigrationError( "Parameter <assembly> is required to migrate exit questionnaire to version 6" ) migrated_instance = self.convert_class( self.new_model.RareDiseaseExitQuestionnaire, old_instance) migrated_instance.variantGroupLevelQuestions = self.convert_collection( old_instance.variantGroupLevelQuestions, self._migrate_variant_group_level_question, assembly=assembly) return self.validate_object( object_to_validate=migrated_instance, object_type=self.new_model.RareDiseaseExitQuestionnaire)
def migrate_clinical_report_rd(self, old_instance, assembly): """ :type old_instance: reports_4_0_0.ClinicalReportRD :type assembly: reports_5_0_0.Assembly :rtype: reports_5_0_0.ClinicalReportRD """ if assembly is None: raise MigrationError( "Parameter <assembly> is required to migrate model versions earlier than 5.0.0" ) new_instance = self.convert_class( self.new_model.ClinicalReportRD, old_instance) # :type self.new_model.ClinicalReportRD try: new_instance.interpretationRequestVersion = self.convert_string_to_integer( old_instance.interpretationRequestVersion) except MigrationError as ex: logging.error( "Error converting 'interpretationRequestVersion' to integer from value '{}'" .format(old_instance.interpretationRequestVersion)) raise ex new_instance.references = old_instance.supportingEvidence new_instance.variants = self.convert_collection( old_instance.candidateVariants, self._migrate_reported_variant, assembly=assembly) if old_instance.additionalAnalysisPanels is not None: panels = [] for panel in old_instance.additionalAnalysisPanels: new_panel = self.new_model.AdditionalAnalysisPanel( ) # :type reports_5_0_0.AdditionalAnalysisPanel new_panel.specificDisease = panel.specificDisease new_panel.panel = self.new_model.GenePanel( panelName=panel.panelName, panelVersion=panel.panelVersion) panels.append(new_panel) new_instance.additionalAnalysisPanels = panels return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.ClinicalReportRD)
def _merge_annotations_and_frequencies(numeric_annotations, allele_frequencies): if numeric_annotations is None: numeric_annotations = {} if not isinstance(numeric_annotations, dict): raise MigrationError( "additionalNumericVariantAnnotations should be dict but is: {}" .format(numeric_annotations)) if allele_frequencies is not None: for af in allele_frequencies: annotation_key = "{}:{}".format(af.study, af.population) if annotation_key in numeric_annotations: logging.warning( "{} already exists in numeric_annotations with value {} instead of {}" .format(annotation_key, numeric_annotations.get(annotation_key), af.alternateFrequency)) else: numeric_annotations["{}:{}".format( af.study, af.population)] = af.alternateFrequency return numeric_annotations
def migrate_interpretation_request_rd(self, old_instance, assembly): """ Migrates an InterpretationRequestRD into an InterpretedGenomeRD, several unexisting fields need to be provided :type old_instance: reports_4_0_0.InterpretationRequestRD :rtype: reports_5_0_0.InterpretationRequestRD """ if assembly is None: raise MigrationError( "Parameter <assembly> is required if version is older than 5.0.0" ) new_instance = self.convert_class( self.new_model.InterpretationRequestRD, old_instance) # type: reports_5_0_0.InterpretationRequestRD new_instance.genomeAssembly = assembly new_instance.pedigree = self._migrate_pedigree(old_instance.pedigree) # NOTE: store fields in additional fields that are lost otherwise if not new_instance.additionalInfo: new_instance.additionalInfo = {} if old_instance.analysisVersion: new_instance.additionalInfo[ 'analysisVersion'] = old_instance.analysisVersion if old_instance.analysisReturnUri: new_instance.additionalInfo[ 'analysisReturnUri'] = old_instance.analysisReturnUri if old_instance.tieringVersion: new_instance.additionalInfo[ 'tieringVersion'] = old_instance.tieringVersion if old_instance.complexGeneticPhenomena: new_instance.additionalInfo['complexGeneticPhenomena'] = str( old_instance.complexGeneticPhenomena) if old_instance.cellbaseVersion: new_instance.additionalInfo[ 'cellbaseVersion'] = old_instance.cellbaseVersion if old_instance.interpretGenome: new_instance.additionalInfo['interpretGenome'] = str( old_instance.interpretGenome) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.InterpretationRequestRD)
def migrate(json_dict, types, migrations): valid_types = MigrationHelpers.is_valid(json_dict, types) if len(valid_types) == 0: raise MigrationError("JSON dict not valid according to any model") elif len(valid_types) == 1: typ = valid_types[0] elif len(valid_types) > 1: observed_version = MigrationHelpers.get_version_control(json_dict) typ = None for t in valid_types: expected_version = MigrationHelpers.get_version_control(t().toJsonDict()) if expected_version == observed_version: # chooses the type having the correct expected version typ = t break if typ is None: # if none matched chooses the most conservative one typ = valid_types[-1] migrations_to_apply = migrations[0:types.index(typ) + 1] migrated = typ.fromJsonDict(json_dict) for migration in reversed(migrations_to_apply): migrated = migration(migrated) return migrated
def migrate_cancer_clinical_report(self, old_instance, assembly, participant_id, sample_ids): """ NOTE: we migrate from a model where only one sample and one participant is supported, thus we do not need a list of samples or participants :type old_instance: reports_4_0_0.ClinicalReportCancer :type assembly: reports_5_0_0.Assembly :type participant_id: str :type sample_ids: map[str (alleleOrigin)]: str - {'germline_variant': 'LP...', 'somatic_variant': 'LP...'} :rtype: reports_5_0_0.ClinicalReportCancer """ if not sample_ids or not assembly or not participant_id: raise MigrationError( "Missing required fields to migrate cancer clinical report from 4.0.0 to 5.0.0" ) new_instance = self.convert_class( self.new_model.ClinicalReportCancer, old_instance) # :type: reports_5_0_0.ClinicalReportCancer try: new_instance.interpretationRequestVersion = self.convert_string_to_integer( old_instance.interpretationRequestVersion) except MigrationError as ex: logging.error( "Error converting 'interpretationRequestVersion' to integer from value '{}'" .format(old_instance.interpretationRequestVersion)) raise ex new_instance.variants = self.convert_collection( old_instance.candidateVariants, self._migrate_reported_variant_cancer, assembly=assembly, participant_id=participant_id, sample_ids=sample_ids) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.ClinicalReportCancer)
def migrate_cancer_interpretation_request(self, old_instance, assembly): """ :type old_instance: reports_4_0_0.CancerInterpretationRequest :rtype: reports_5_0_0.CancerInterpretationRequest """ if assembly is None: raise MigrationError( "Parameter <assembly> is required to migrate cancer interpretation request to version 5" ) new_instance = self.convert_class( self.new_model.CancerInterpretationRequest, old_instance) # :type: reports_5_0_0.CancerInterpretationRequest new_instance.interpretationRequestId = old_instance.reportRequestId new_instance.interpretationRequestVersion = old_instance.reportVersion new_instance.genomeAssembly = assembly new_instance.cancerParticipant = self._migrate_cancer_participant( old_participant=old_instance.cancerParticipant) if not new_instance.additionalInfo: new_instance.additionalInfo = {} if old_instance.analysisUri: new_instance.additionalInfo[ 'analysisUri'] = old_instance.analysisUri if old_instance.analysisVersion: new_instance.additionalInfo[ 'analysisVersion'] = old_instance.analysisVersion if old_instance.tieringVersion: new_instance.additionalInfo[ 'tieringVersion'] = old_instance.tieringVersion if old_instance.interpretGenome: new_instance.additionalInfo['interpretGenome'] = str( old_instance.interpretGenome) return self.validate_object( object_to_validate=new_instance, object_type=self.new_model.CancerInterpretationRequest)
def _raise_migration_error_for_parameter(parameter): raise MigrationError( "Missing required field {parameter} to migrate a cancer interpreted genome from 4.0.0 to 5.0.0" .format(parameter=parameter))