diff --git a/client/src/app/components/molecular-profiles/molecular-profile-fusion-variant-card/molecular-profile-fusion-variant-card.component.html b/client/src/app/components/molecular-profiles/molecular-profile-fusion-variant-card/molecular-profile-fusion-variant-card.component.html
index bb4573c58..9a2f8fc6c 100644
--- a/client/src/app/components/molecular-profiles/molecular-profile-fusion-variant-card/molecular-profile-fusion-variant-card.component.html
+++ b/client/src/app/components/molecular-profiles/molecular-profile-fusion-variant-card/molecular-profile-fusion-variant-card.component.html
@@ -27,10 +27,10 @@
-
+
+ nzTitle="VICC Nomenclature">
{{ variant.viccCompliantName }}
diff --git a/client/src/app/components/variants/fusion-variant-summary/fusion-variant-summary.page.html b/client/src/app/components/variants/fusion-variant-summary/fusion-variant-summary.page.html
index e8d3d3b3f..37c513e3c 100644
--- a/client/src/app/components/variants/fusion-variant-summary/fusion-variant-summary.page.html
+++ b/client/src/app/components/variants/fusion-variant-summary/fusion-variant-summary.page.html
@@ -114,9 +114,9 @@
nzSize="small"
[nzColumn]="{ xxl: 2, xl: 2, lg: 1, md: 1, sm: 1, xs: 1 }"
nzBordered="true">
-
+
{{ variant.viccCompliantName }}
diff --git a/client/src/app/components/variants/variant-popover/variant-popover.component.html b/client/src/app/components/variants/variant-popover/variant-popover.component.html
index 5aa804b1f..2920fc2d1 100644
--- a/client/src/app/components/variants/variant-popover/variant-popover.component.html
+++ b/client/src/app/components/variants/variant-popover/variant-popover.component.html
@@ -25,9 +25,9 @@
nzLayout="horizontal"
[nzColumn]="1"
nzBordered="true">
-
+
{{ variant.viccCompliantName }}
diff --git a/client/src/app/forms/types/feature-select/feature-select.type.ts b/client/src/app/forms/types/feature-select/feature-select.type.ts
index cf5e80534..b1db3df1c 100644
--- a/client/src/app/forms/types/feature-select/feature-select.type.ts
+++ b/client/src/app/forms/types/feature-select/feature-select.type.ts
@@ -211,6 +211,7 @@ export class CvcFeatureSelectField
nzContent: CvcFusionSelectForm,
nzData: {},
nzFooter: null,
+ nzWidth: '40%',
}
)
diff --git a/client/src/app/forms/types/feature-select/fusion-select/fusion-add.query.gql b/client/src/app/forms/types/feature-select/fusion-select/fusion-add.query.gql
index 76335dbe9..2d49aafb1 100644
--- a/client/src/app/forms/types/feature-select/fusion-select/fusion-add.query.gql
+++ b/client/src/app/forms/types/feature-select/fusion-select/fusion-add.query.gql
@@ -1,13 +1,15 @@
-mutation SelectOrCreateFusion($organizationId: Int, $fivePrimeGeneId: Int, $fivePrimePartnerStatus: FusionPartnerStatus!, $threePrimeGeneId: Int, $threePrimePartnerStatus: FusionPartnerStatus! ) {
+mutation SelectOrCreateFusion($organizationId: Int, $fivePrimeGeneId: Int, $fivePrimeRegulatoryFusionType: RegulatoryFusionType, $fivePrimePartnerStatus: FusionPartnerStatus!, $threePrimeGeneId: Int, $threePrimeRegulatoryFusionType: RegulatoryFusionType, $threePrimePartnerStatus: FusionPartnerStatus!) {
createFusionFeature(input: {
organizationId: $organizationId
fivePrimeGene: {
geneId: $fivePrimeGeneId,
partnerStatus: $fivePrimePartnerStatus
+ regulatoryFusionType: $fivePrimeRegulatoryFusionType
},
threePrimeGene: {
geneId: $threePrimeGeneId,
- partnerStatus: $threePrimePartnerStatus
+ partnerStatus: $threePrimePartnerStatus,
+ regulatoryFusionType: $threePrimeRegulatoryFusionType
}
}) {
diff --git a/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.html b/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.html
index f8c57f847..2f9bdda69 100644
--- a/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.html
+++ b/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.html
@@ -1,13 +1,46 @@
+
+
+
+
+
+
+@if(fusionType == 'transcript') {
+} @else {
+
+}
diff --git a/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.ts b/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.ts
index 2f6634f99..4d0c2bc8b 100644
--- a/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.ts
+++ b/client/src/app/forms/types/feature-select/fusion-select/fusion-select.form.ts
@@ -7,6 +7,7 @@ import {
} from '@angular/core'
import {
AbstractControl,
+ FormsModule,
ReactiveFormsModule,
UntypedFormGroup,
} from '@angular/forms'
@@ -14,6 +15,7 @@ import {
FeatureInstanceTypes,
FusionPartnerStatus,
Maybe,
+ RegulatoryFusionType,
SelectOrCreateFusionGQL,
SelectOrCreateFusionMutation,
SelectOrCreateFusionMutationVariables,
@@ -36,12 +38,16 @@ import {
} from '@app/core/utilities/mutation-state-wrapper'
import { NetworkErrorsService } from '@app/core/services/network-errors.service'
import { NZ_MODAL_DATA, NzModalModule, NzModalRef } from 'ng-zorro-antd/modal'
+import { NzRadioModule } from 'ng-zorro-antd/radio'
+import { NzSpaceModule } from 'ng-zorro-antd/space'
type FusionSelectModel = {
fivePrimeGeneId?: number
fivePrimePartnerStatus: FusionPartnerStatus
+ fivePrimeRegulatoryFusionType?: RegulatoryFusionType
threePrimeGeneId?: number
threePrimePartnerStatus: FusionPartnerStatus
+ threePrimeRegulatoryFusionType?: RegulatoryFusionType
}
export interface FusionSelectModalData {
@@ -56,11 +62,14 @@ export interface FusionSelectModalData {
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
+ FormsModule,
ReactiveFormsModule,
NzFormModule,
NzButtonModule,
NzAlertModule,
NzModalModule,
+ NzRadioModule,
+ NzSpaceModule,
RouterModule,
FormlyModule,
],
@@ -71,11 +80,31 @@ export class CvcFusionSelectForm {
readonly #modal = inject(NzModalRef)
readonly nzModalData: FusionSelectModalData = inject(NZ_MODAL_DATA)
- model: FusionSelectModel
- form: UntypedFormGroup
- config: FormlyFieldConfig[]
+ transcriptForm: UntypedFormGroup
+ regulatoryForm: UntypedFormGroup
layout: NzFormLayoutType = 'vertical'
+ transcriptConfig: FormlyFieldConfig[]
+ regulatoryConfig: FormlyFieldConfig[]
+
+ transcriptModel: FusionSelectModel = {
+ fivePrimeGeneId: undefined,
+ threePrimeGeneId: undefined,
+ fivePrimePartnerStatus: FusionPartnerStatus.Known,
+ threePrimePartnerStatus: FusionPartnerStatus.Known,
+ fivePrimeRegulatoryFusionType: undefined,
+ threePrimeRegulatoryFusionType: undefined,
+ }
+
+ regulatoryModel: FusionSelectModel = {
+ fivePrimeGeneId: undefined,
+ threePrimeGeneId: undefined,
+ fivePrimePartnerStatus: FusionPartnerStatus.Regulatory,
+ threePrimePartnerStatus: FusionPartnerStatus.Known,
+ fivePrimeRegulatoryFusionType: undefined,
+ threePrimeRegulatoryFusionType: undefined,
+ }
+
options: FormlyFormOptions
selectOrCreateFusionMutator: MutatorWithState<
@@ -86,20 +115,17 @@ export class CvcFusionSelectForm {
mutationState?: MutationState
+ fusionType: 'transcript' | 'regulatory' = 'transcript'
+
constructor(
private query: SelectOrCreateFusionGQL,
- private errors: NetworkErrorsService
+ errors: NetworkErrorsService
) {
this.selectOrCreateFusionMutator = new MutatorWithState(errors)
- this.form = new UntypedFormGroup({})
+ this.transcriptForm = new UntypedFormGroup({})
+ this.regulatoryForm = new UntypedFormGroup({})
- this.model = {
- fivePrimeGeneId: undefined,
- threePrimeGeneId: undefined,
- fivePrimePartnerStatus: FusionPartnerStatus.Known,
- threePrimePartnerStatus: FusionPartnerStatus.Known,
- }
this.options = {}
const selectOptions = [
@@ -117,7 +143,22 @@ export class CvcFusionSelectForm {
},
]
- this.config = [
+ const regulatoryOptions = Object.keys(RegulatoryFusionType)
+ .map((x) => {
+ const val = RegulatoryFusionType[x as keyof typeof RegulatoryFusionType]
+ return { label: val, value: val }
+ })
+ .sort((a, b) => {
+ if (a.label == 'enhancer') {
+ return -1
+ } else if (a.label == 'promoter') {
+ return -1
+ } else {
+ return a.label.localeCompare(b.label)
+ }
+ })
+
+ this.transcriptConfig = [
{
wrappers: ['form-layout'],
props: {
@@ -157,11 +198,7 @@ export class CvcFusionSelectForm {
fieldGroup: [
{
wrappers: ['form-card'],
- props: {
- formCardOptions: {
- title: 'New Fusion Feature',
- },
- },
+ props: {},
fieldGroup: [
{
wrappers: ['form-row'],
@@ -177,13 +214,36 @@ export class CvcFusionSelectForm {
props: {
label: "5' Partner Status",
tooltip:
- "Select Known if the specific 5' Gene partner is known, Unknown if not. Select Multiple if there are multiple potential 5' Gene partners",
+ "Select Known if the specific 5' Gene partner is known, Unknown if not. Select Multiple if there are multiple potential 5' Gene partners.",
required: true,
placeholder: "5' Partner Status",
options: selectOptions,
multiple: false,
},
},
+ {
+ key: 'threePrimePartnerStatus',
+ type: 'base-select',
+ props: {
+ required: true,
+ placeholder: "3' Partner Status",
+ label: "3' Partner Status",
+ tooltip:
+ "Select Known if the specific 3' Gene partner is known, Unknown if not. Select Multiple if there are multiple potential 3' Gene partners.",
+ options: selectOptions,
+ multiple: false,
+ },
+ },
+ ],
+ },
+ {
+ wrappers: ['form-row'],
+ props: {
+ formRowOptions: {
+ span: 12,
+ },
+ },
+ fieldGroup: [
{
key: 'fivePrimeGeneId',
type: 'feature-select',
@@ -204,8 +264,78 @@ export class CvcFusionSelectForm {
FusionPartnerStatus.Known,
},
},
+ {
+ key: 'threePrimeGeneId',
+ type: 'feature-select',
+ props: {
+ label: "3' Fusion Partner",
+ placeholder: 'Select Gene',
+ tooltip: "Select the 3' Gene partner in the Fusion",
+ canChangeFeatureType: false,
+ hideFeatureTypeSelect: true,
+ featureType: FeatureInstanceTypes.Gene,
+ },
+ expressions: {
+ 'props.disabled': (field) =>
+ field.model.threePrimePartnerStatus !=
+ FusionPartnerStatus.Known,
+ 'props.required': (field) =>
+ field.model.threePrimePartnerStatus ==
+ FusionPartnerStatus.Known,
+ },
+ },
+ ],
+ },
+ {
+ wrappers: ['form-row'],
+ props: {
+ formRowOptions: {
+ span: 24,
+ },
+ },
+ fieldGroup: [
+ {
+ key: 'organizationId',
+ type: 'org-submit-button',
+ props: {
+ submitLabel: 'Create Fusion',
+ align: 'right',
+ },
+ },
],
},
+ ],
+ },
+ ],
+ },
+ ]
+
+ this.regulatoryConfig = [
+ {
+ wrappers: ['form-layout'],
+ props: {
+ showDevPanel: false,
+ },
+ validators: {
+ sameGene: {
+ message: "5' and 3' Genes must be different",
+ expression: (x: AbstractControl) => {
+ const model = x.value
+ if (model && model.fivePrimeGeneId && model.threePrimeGeneId) {
+ if (model.fivePrimeGeneId == model.threePrimeGeneId) {
+ return false
+ }
+ }
+ return true
+ },
+ errorPath: 'fivePrimeGeneId',
+ },
+ },
+ fieldGroup: [
+ {
+ wrappers: ['form-card'],
+ props: {},
+ fieldGroup: [
{
wrappers: ['form-row'],
props: {
@@ -214,6 +344,18 @@ export class CvcFusionSelectForm {
},
},
fieldGroup: [
+ {
+ key: 'fivePrimeRegulatoryFusionType',
+ type: 'base-select',
+ props: {
+ label: 'Regulatory Element Type',
+ tooltip: '',
+ required: true,
+ placeholder: 'Regulatory Element',
+ options: regulatoryOptions,
+ multiple: false,
+ },
+ },
{
key: 'threePrimePartnerStatus',
type: 'base-select',
@@ -222,11 +364,35 @@ export class CvcFusionSelectForm {
placeholder: "3' Partner Status",
label: "3' Partner Status",
tooltip:
- "Select Known if the specific 3' Gene partner is known, Unknown if not. Select Multiple if there are multiple potential 3' Gene partners",
+ "Select Known if the specific 3' Gene partner is known, Unknown if not. Select Multiple if there are multiple potential 3' Gene partners.",
options: selectOptions,
multiple: false,
},
},
+ ],
+ },
+ {
+ wrappers: ['form-row'],
+ props: {
+ formRowOptions: {
+ span: 12,
+ },
+ },
+ fieldGroup: [
+ {
+ key: 'fivePrimeGeneId',
+ type: 'feature-select',
+ props: {
+ label: 'Regulatory Partner',
+ placeholder: 'Select Gene',
+ tooltip:
+ 'Select the Regulatory Gene partner in the Fusion',
+ canChangeFeatureType: false,
+ hideFeatureTypeSelect: true,
+ featureType: FeatureInstanceTypes.Gene,
+ required: true,
+ },
+ },
{
key: 'threePrimeGeneId',
type: 'feature-select',
@@ -276,18 +442,24 @@ export class CvcFusionSelectForm {
modelChange(model: Maybe) {
if (model) {
- if (this.model.fivePrimePartnerStatus != FusionPartnerStatus.Known) {
- this.model = {
- ...this.model,
- fivePrimeGeneId: undefined,
- }
- }
- if (this.model.threePrimePartnerStatus != FusionPartnerStatus.Known) {
- this.model = {
- ...this.model,
- threePrimeGeneId: undefined,
- }
- }
+ //if (
+ // this.model.fivePrimePartnerStatus != FusionPartnerStatus.Known &&
+ // this.model.fivePrimePartnerStatus != FusionPartnerStatus.Regulatory
+ //) {
+ // this.model = {
+ // ...this.model,
+ // fivePrimeGeneId: undefined,
+ // }
+ //}
+ //if (
+ // this.model.threePrimePartnerStatus != FusionPartnerStatus.Known &&
+ // this.model.threePrimePartnerStatus != FusionPartnerStatus.Regulatory
+ //) {
+ // this.model = {
+ // ...this.model,
+ // threePrimeGeneId: undefined,
+ // }
+ //}
//mark form as invalid here?
if (
diff --git a/client/src/app/generated/civic.apollo-helpers.ts b/client/src/app/generated/civic.apollo-helpers.ts
index 30b8db065..6f02e4b8c 100644
--- a/client/src/app/generated/civic.apollo-helpers.ts
+++ b/client/src/app/generated/civic.apollo-helpers.ts
@@ -1193,7 +1193,7 @@ export type FlaggableFieldPolicy = {
link?: FieldPolicy | FieldReadFunction,
name?: FieldPolicy | FieldReadFunction
};
-export type FusionKeySpecifier = ('comments' | 'creationActivity' | 'deprecated' | 'deprecationActivity' | 'deprecationReason' | 'description' | 'events' | 'featureAliases' | 'featureInstance' | 'featureType' | 'fivePrimeGene' | 'fivePrimePartnerStatus' | 'flagged' | 'flags' | 'fullName' | 'id' | 'lastAcceptedRevisionEvent' | 'lastCommentEvent' | 'lastSubmittedRevisionEvent' | 'link' | 'name' | 'openRevisionCount' | 'revisions' | 'sources' | 'threePrimeGene' | 'threePrimePartnerStatus' | 'variants' | FusionKeySpecifier)[];
+export type FusionKeySpecifier = ('comments' | 'creationActivity' | 'deprecated' | 'deprecationActivity' | 'deprecationReason' | 'description' | 'events' | 'featureAliases' | 'featureInstance' | 'featureType' | 'fivePrimeGene' | 'fivePrimePartnerStatus' | 'flagged' | 'flags' | 'fullName' | 'id' | 'lastAcceptedRevisionEvent' | 'lastCommentEvent' | 'lastSubmittedRevisionEvent' | 'link' | 'name' | 'openRevisionCount' | 'regulatoryFusionType' | 'revisions' | 'sources' | 'threePrimeGene' | 'threePrimePartnerStatus' | 'variants' | FusionKeySpecifier)[];
export type FusionFieldPolicy = {
comments?: FieldPolicy | FieldReadFunction,
creationActivity?: FieldPolicy | FieldReadFunction,
@@ -1217,6 +1217,7 @@ export type FusionFieldPolicy = {
link?: FieldPolicy | FieldReadFunction,
name?: FieldPolicy | FieldReadFunction,
openRevisionCount?: FieldPolicy | FieldReadFunction,
+ regulatoryFusionType?: FieldPolicy | FieldReadFunction,
revisions?: FieldPolicy | FieldReadFunction,
sources?: FieldPolicy | FieldReadFunction,
threePrimeGene?: FieldPolicy | FieldReadFunction,
diff --git a/client/src/app/generated/civic.apollo.ts b/client/src/app/generated/civic.apollo.ts
index 5405547ce..91bcd3fbb 100644
--- a/client/src/app/generated/civic.apollo.ts
+++ b/client/src/app/generated/civic.apollo.ts
@@ -2931,6 +2931,7 @@ export type Fusion = Commentable & EventOriginObject & EventSubject & Flaggable
link: Scalars['String']['output'];
name: Scalars['String']['output'];
openRevisionCount: Scalars['Int']['output'];
+ regulatoryFusionType?: Maybe;
/** List and filter revisions. */
revisions: RevisionConnection;
sources: Array;
@@ -3052,11 +3053,14 @@ export type FusionPartnerInput = {
geneId?: InputMaybe;
/** The status of the fusion partner */
partnerStatus: FusionPartnerStatus;
+ /** If the fusion partner status is set to regulatory, what type of regulatory fusion is it? */
+ regulatoryFusionType?: InputMaybe;
};
export enum FusionPartnerStatus {
Known = 'KNOWN',
Multiple = 'MULTIPLE',
+ Regulatory = 'REGULATORY',
Unknown = 'UNKNOWN'
}
@@ -5731,6 +5735,35 @@ export enum ReferenceBuild {
Ncbi36 = 'NCBI36'
}
+export enum RegulatoryFusionType {
+ CaatSignal = 'CAAT_signal',
+ DNaseIHypersensitiveSite = 'DNase_I_hypersensitive_site',
+ GcSignal = 'GC_signal',
+ TataBox = 'TATA_box',
+ Attenuator = 'attenuator',
+ Enhancer = 'enhancer',
+ EnhancerBlockingElement = 'enhancer_blocking_element',
+ ImprintingControlRegion = 'imprinting_control_region',
+ Insulator = 'insulator',
+ LocusControlRegion = 'locus_control_region',
+ MatrixAttachmentRegion = 'matrix_attachment_region',
+ Minus_10Signal = 'minus_10_signal',
+ Minus_35Signal = 'minus_35_signal',
+ Other = 'other',
+ PolyASignalSequence = 'polyA_signal_sequence',
+ Promoter = 'promoter',
+ RecodingStimulatoryRegion = 'recoding_stimulatory_region',
+ RecombinationEnhancer = 'recombination_enhancer',
+ ReplicationRegulatoryRegion = 'replication_regulatory_region',
+ ResponseElement = 'response_element',
+ RibosomeBindingSite = 'ribosome_binding_site',
+ Riboswitch = 'riboswitch',
+ Silencer = 'silencer',
+ Terminator = 'terminator',
+ TranscriptionalCisRegulatoryRegion = 'transcriptional_cis_regulatory_region',
+ UOrf = 'uORF'
+}
+
export type RejectRevisionsActivity = ActivityInterface & {
__typename: 'RejectRevisionsActivity';
createdAt: Scalars['ISO8601DateTime']['output'];
@@ -9407,8 +9440,10 @@ export type FeatureSelectTypeaheadFieldsFragment = { __typename: 'Feature', id:
export type SelectOrCreateFusionMutationVariables = Exact<{
organizationId?: InputMaybe;
fivePrimeGeneId?: InputMaybe;
+ fivePrimeRegulatoryFusionType?: InputMaybe;
fivePrimePartnerStatus: FusionPartnerStatus;
threePrimeGeneId?: InputMaybe;
+ threePrimeRegulatoryFusionType?: InputMaybe;
threePrimePartnerStatus: FusionPartnerStatus;
}>;
@@ -17374,9 +17409,9 @@ export const FeatureSelectTagDocument = gql`
}
}
export const SelectOrCreateFusionDocument = gql`
- mutation SelectOrCreateFusion($organizationId: Int, $fivePrimeGeneId: Int, $fivePrimePartnerStatus: FusionPartnerStatus!, $threePrimeGeneId: Int, $threePrimePartnerStatus: FusionPartnerStatus!) {
+ mutation SelectOrCreateFusion($organizationId: Int, $fivePrimeGeneId: Int, $fivePrimeRegulatoryFusionType: RegulatoryFusionType, $fivePrimePartnerStatus: FusionPartnerStatus!, $threePrimeGeneId: Int, $threePrimeRegulatoryFusionType: RegulatoryFusionType, $threePrimePartnerStatus: FusionPartnerStatus!) {
createFusionFeature(
- input: {organizationId: $organizationId, fivePrimeGene: {geneId: $fivePrimeGeneId, partnerStatus: $fivePrimePartnerStatus}, threePrimeGene: {geneId: $threePrimeGeneId, partnerStatus: $threePrimePartnerStatus}}
+ input: {organizationId: $organizationId, fivePrimeGene: {geneId: $fivePrimeGeneId, partnerStatus: $fivePrimePartnerStatus, regulatoryFusionType: $fivePrimeRegulatoryFusionType}, threePrimeGene: {geneId: $threePrimeGeneId, partnerStatus: $threePrimePartnerStatus, regulatoryFusionType: $threePrimeRegulatoryFusionType}}
) {
new
feature {
diff --git a/client/src/app/generated/server.model.graphql b/client/src/app/generated/server.model.graphql
index cbd4f2c88..bdd9f4d0a 100644
--- a/client/src/app/generated/server.model.graphql
+++ b/client/src/app/generated/server.model.graphql
@@ -5034,6 +5034,7 @@ type Fusion implements Commentable & EventOriginObject & EventSubject & Flaggabl
link: String!
name: String!
openRevisionCount: Int!
+ regulatoryFusionType: RegulatoryFusionType
"""
List and filter revisions.
@@ -5229,11 +5230,17 @@ input FusionPartnerInput {
The status of the fusion partner
"""
partnerStatus: FusionPartnerStatus!
+
+ """
+ If the fusion partner status is set to regulatory, what type of regulatory fusion is it?
+ """
+ regulatoryFusionType: RegulatoryFusionType
}
enum FusionPartnerStatus {
KNOWN
MULTIPLE
+ REGULATORY
UNKNOWN
}
@@ -9878,6 +9885,35 @@ enum ReferenceBuild {
NCBI36
}
+enum RegulatoryFusionType {
+ CAAT_signal
+ DNase_I_hypersensitive_site
+ GC_signal
+ TATA_box
+ attenuator
+ enhancer
+ enhancer_blocking_element
+ imprinting_control_region
+ insulator
+ locus_control_region
+ matrix_attachment_region
+ minus_10_signal
+ minus_35_signal
+ other
+ polyA_signal_sequence
+ promoter
+ recoding_stimulatory_region
+ recombination_enhancer
+ replication_regulatory_region
+ response_element
+ ribosome_binding_site
+ riboswitch
+ silencer
+ terminator
+ transcriptional_cis_regulatory_region
+ uORF
+}
+
type RejectRevisionsActivity implements ActivityInterface {
createdAt: ISO8601DateTime!
events: [Event!]!
diff --git a/client/src/app/generated/server.schema.json b/client/src/app/generated/server.schema.json
index 9abd2613d..a11cc1037 100644
--- a/client/src/app/generated/server.schema.json
+++ b/client/src/app/generated/server.schema.json
@@ -23958,6 +23958,18 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "regulatoryFusionType",
+ "description": null,
+ "args": [],
+ "type": {
+ "kind": "ENUM",
+ "name": "RegulatoryFusionType",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "revisions",
"description": "List and filter revisions.",
@@ -24607,6 +24619,18 @@
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "regulatoryFusionType",
+ "description": "If the fusion partner status is set to regulatory, what type of regulatory fusion is it?",
+ "type": {
+ "kind": "ENUM",
+ "name": "RegulatoryFusionType",
+ "ofType": null
+ },
+ "defaultValue": null,
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"interfaces": null,
@@ -24638,6 +24662,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "REGULATORY",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"possibleTypes": null
@@ -44810,6 +44840,173 @@
],
"possibleTypes": null
},
+ {
+ "kind": "ENUM",
+ "name": "RegulatoryFusionType",
+ "description": null,
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": [
+ {
+ "name": "attenuator",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "CAAT_signal",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "DNase_I_hypersensitive_site",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "enhancer",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "enhancer_blocking_element",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "GC_signal",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "imprinting_control_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "insulator",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "locus_control_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "matrix_attachment_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "minus_35_signal",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "minus_10_signal",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "polyA_signal_sequence",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "promoter",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "recoding_stimulatory_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "recombination_enhancer",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "replication_regulatory_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "response_element",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "ribosome_binding_site",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "riboswitch",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "silencer",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "TATA_box",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "terminator",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "transcriptional_cis_regulatory_region",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "uORF",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "other",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "possibleTypes": null
+ },
{
"kind": "OBJECT",
"name": "RejectRevisionsActivity",
diff --git a/server/app/graphql/mutations/create_fusion_feature.rb b/server/app/graphql/mutations/create_fusion_feature.rb
index cafd2a96d..cde0a1de7 100644
--- a/server/app/graphql/mutations/create_fusion_feature.rb
+++ b/server/app/graphql/mutations/create_fusion_feature.rb
@@ -36,14 +36,22 @@ def ready?(organization_id: nil, five_prime_gene:, three_prime_gene:, **kwargs)
# check that partner status matches gene_id presence
[ five_prime_gene, three_prime_gene ].each do |gene_input|
- if gene_input.gene_id.present? && gene_input.partner_status != "known"
- raise GraphQL::ExecutionError, "Partner status needs to be 'known' if a gene_id is set"
+ if gene_input.gene_id.present? && (gene_input.partner_status != "known" && gene_input.partner_status != "regulatory")
+ raise GraphQL::ExecutionError, "Partner status needs to be 'known' or 'regulatory' if a gene_id is set"
end
- if gene_input.gene_id.blank? && gene_input.partner_status == "known"
- raise GraphQL::ExecutionError, "Partner status can't be 'known' if a gene_id is not set"
+ if gene_input.gene_id.blank? && (gene_input.partner_status == "known" || gene_input.partner_status == "regulatory")
+ raise GraphQL::ExecutionError, "Partner status can't be 'known' or 'regulatory' if a gene_id is not set"
end
end
+ # check that maximum one gene has regulatory_fusion_type set
+ if five_prime_gene.partner_status == "regulatory" && three_prime_gene.partner_status == "regulatory"
+ raise GraphQL::ExecutionError, "Only one Fusion partner can be marked 'regulatory'"
+ end
+ if five_prime_gene.regulatory_fusion_type.present? && three_prime_gene.regulatory_fusion_type.present?
+ raise GraphQL::ExecutionError, "Only one Fusion partner can have a regulatory fusion type set."
+ end
+
return true
end
@@ -53,12 +61,16 @@ def authorized?(organization_id: nil, **kwargs)
end
def resolve(five_prime_gene:, three_prime_gene:, organization_id: nil)
+ # only one can be set
+ regulatory_fusion_type = five_prime_gene.regulatory_fusion_type || three_prime_gene.regulatory_fusion_type
+
existing_feature_instance = Features::Fusion
.find_by(
five_prime_gene_id: five_prime_gene.gene_id,
three_prime_gene_id: three_prime_gene.gene_id,
five_prime_partner_status: five_prime_gene.partner_status,
three_prime_partner_status: three_prime_gene.partner_status,
+ regulatory_fusion_type: regulatory_fusion_type
)
if existing_feature_instance.present?
@@ -73,6 +85,7 @@ def resolve(five_prime_gene:, three_prime_gene:, organization_id: nil)
three_prime_gene_id: three_prime_gene.gene_id,
five_prime_partner_status: five_prime_gene.partner_status,
three_prime_partner_status: three_prime_gene.partner_status,
+ regulatory_fusion_type: regulatory_fusion_type,
originating_user: context[:current_user],
organization_id: organization_id,
)
diff --git a/server/app/graphql/types/entities/fusion_type.rb b/server/app/graphql/types/entities/fusion_type.rb
index b438ebaad..c7ebf745e 100644
--- a/server/app/graphql/types/entities/fusion_type.rb
+++ b/server/app/graphql/types/entities/fusion_type.rb
@@ -6,6 +6,8 @@ class FusionType < Types::Entities::FeatureType
field :five_prime_partner_status, Types::Fusion::FusionPartnerStatus, null: false
field :three_prime_partner_status, Types::Fusion::FusionPartnerStatus, null: false
+ field :regulatory_fusion_type, Types::Fusion::RegulatoryFusionTypeType, null: true
+
def five_prime_gene
Loaders::AssociationLoader.for(Features::Fusion, :five_prime_gene).load(object)
end
diff --git a/server/app/graphql/types/fusion/fusion_partner_input_type.rb b/server/app/graphql/types/fusion/fusion_partner_input_type.rb
index a150e802f..55b3bc657 100644
--- a/server/app/graphql/types/fusion/fusion_partner_input_type.rb
+++ b/server/app/graphql/types/fusion/fusion_partner_input_type.rb
@@ -6,5 +6,7 @@ class FusionPartnerInputType < Types::BaseInputObject
description: "The status of the fusion partner"
argument :gene_id, Int, required: false,
description: "The CIViC gene ID of the partner, if known"
+ argument :regulatory_fusion_type, Types::Fusion::RegulatoryFusionTypeType, required: false,
+ description: "If the fusion partner status is set to regulatory, what type of regulatory fusion is it?"
end
end
diff --git a/server/app/graphql/types/fusion/fusion_partner_status.rb b/server/app/graphql/types/fusion/fusion_partner_status.rb
index c0842ead0..ac59781e7 100644
--- a/server/app/graphql/types/fusion/fusion_partner_status.rb
+++ b/server/app/graphql/types/fusion/fusion_partner_status.rb
@@ -3,5 +3,6 @@ class FusionPartnerStatus < Types::BaseEnum
value "KNOWN", value: "known"
value "UNKNOWN", value: "unknown"
value "MULTIPLE", value: "multiple"
+ value "REGULATORY", value: "regulatory"
end
end
diff --git a/server/app/graphql/types/fusion/regulatory_fusion_type_type.rb b/server/app/graphql/types/fusion/regulatory_fusion_type_type.rb
new file mode 100644
index 000000000..28e547c20
--- /dev/null
+++ b/server/app/graphql/types/fusion/regulatory_fusion_type_type.rb
@@ -0,0 +1,8 @@
+module Types::Fusion
+ class RegulatoryFusionTypeType < Types::BaseEnum
+ Constants::REGULATORY_FUSION_ENUM_TYPES
+ .each do |(name, _)|
+ value name, value: name
+ end
+ end
+end
diff --git a/server/app/models/actions/create_fusion_feature.rb b/server/app/models/actions/create_fusion_feature.rb
index fd34cdfe6..bb5321036 100644
--- a/server/app/models/actions/create_fusion_feature.rb
+++ b/server/app/models/actions/create_fusion_feature.rb
@@ -2,10 +2,18 @@ module Actions
class CreateFusionFeature
include Actions::Transactional
- attr_reader :feature, :originating_user, :organization_id, :create_variant, :five_prime_partner_status, :three_prime_partner_status
+ attr_reader :feature, :originating_user, :organization_id, :create_variant, :five_prime_partner_status, :three_prime_partner_status, :regulatory_fusion_type
+
+ def initialize(originating_user:, five_prime_gene_id:, three_prime_gene_id:, five_prime_partner_status:, three_prime_partner_status:, regulatory_fusion_type:, organization_id: nil, create_variant: true)
+ @five_prime_partner_status = five_prime_partner_status
+ @three_prime_partner_status = three_prime_partner_status
+ @regulatory_fusion_type = regulatory_fusion_type
+ @originating_user = originating_user
+ @organization_id = organization_id
+ @create_variant = create_variant
- def initialize(originating_user:, five_prime_gene_id:, three_prime_gene_id:, five_prime_partner_status:, three_prime_partner_status:, organization_id: nil, create_variant: true)
feature_name = "#{construct_fusion_partner_name(five_prime_gene_id, five_prime_partner_status)}::#{construct_fusion_partner_name(three_prime_gene_id, three_prime_partner_status)}"
+
@feature = Feature.new(
name: feature_name,
)
@@ -14,18 +22,18 @@ def initialize(originating_user:, five_prime_gene_id:, three_prime_gene_id:, fiv
three_prime_gene_id: three_prime_gene_id,
five_prime_partner_status: five_prime_partner_status,
three_prime_partner_status: three_prime_partner_status,
+ regulatory_fusion_type: regulatory_fusion_type,
feature: feature,
)
- @five_prime_partner_status = five_prime_partner_status
- @three_prime_partner_status = three_prime_partner_status
- @originating_user = originating_user
- @organization_id = organization_id
- @create_variant = create_variant
end
def construct_fusion_partner_name(gene_id, partner_status)
if partner_status == "known"
Features::Gene.find(gene_id).name
+ elsif partner_status == "regulatory"
+ gene_name = Features::Gene.find(gene_id).name
+ rft = Features::Fusion.format_regulatory_fusion_type(regulatory_fusion_type)
+ "#{rft}@#{gene_name}"
elsif partner_status == "unknown"
"?"
elsif partner_status == "multiple"
diff --git a/server/app/models/activities/create_fusion_feature.rb b/server/app/models/activities/create_fusion_feature.rb
index 9ae8e0e41..0329ccaf7 100644
--- a/server/app/models/activities/create_fusion_feature.rb
+++ b/server/app/models/activities/create_fusion_feature.rb
@@ -1,13 +1,14 @@
module Activities
class CreateFusionFeature < Base
- attr_reader :feature, :five_prime_gene_id, :three_prime_gene_id, :five_prime_partner_status, :three_prime_partner_status, :create_variant
+ attr_reader :feature, :five_prime_gene_id, :three_prime_gene_id, :five_prime_partner_status, :three_prime_partner_status, :create_variant, :regulatory_fusion_type
- def initialize(originating_user:, organization_id:, five_prime_gene_id:, three_prime_gene_id:, five_prime_partner_status:, three_prime_partner_status:, create_variant: true)
+ def initialize(originating_user:, organization_id:, five_prime_gene_id:, three_prime_gene_id:, five_prime_partner_status:, three_prime_partner_status:, regulatory_fusion_type:, create_variant: true)
super(organization_id: organization_id, user: originating_user)
@five_prime_gene_id = five_prime_gene_id
@three_prime_gene_id = three_prime_gene_id
@five_prime_partner_status = five_prime_partner_status
@three_prime_partner_status = three_prime_partner_status
+ @regulatory_fusion_type = regulatory_fusion_type
@create_variant = create_variant
end
@@ -25,6 +26,7 @@ def call_actions
three_prime_gene_id: three_prime_gene_id,
five_prime_partner_status: five_prime_partner_status,
three_prime_partner_status: three_prime_partner_status,
+ regulatory_fusion_type: regulatory_fusion_type,
originating_user: user,
organization_id: organization&.id,
create_variant: create_variant
diff --git a/server/app/models/constants.rb b/server/app/models/constants.rb
index b887c8b26..5a595771b 100644
--- a/server/app/models/constants.rb
+++ b/server/app/models/constants.rb
@@ -139,5 +139,39 @@ module Constants
REPRESENTATIVE_FUSION_VARIANT_NAME = "Fusion"
+ # INSDC regulatory class vocabulary as required here: https://fusions.cancervariants.org/en/latest/nomenclature.html#regulatory-nomenclature
+ REGULATORY_FUSION_TYPES = [
+ [ "attenuator", "SO:0000140" ],
+ [ "CAAT_signal", "SO:0000172" ],
+ [ "DNase_I_hypersensitive_site", "SO:0000685" ],
+ [ "enhancer", "SO:0000165" ],
+ [ "enhancer_blocking_element", nil ],
+ [ "GC_signal", "SO:0000173" ],
+ [ "imprinting_control_region", nil ],
+ [ "insulator", "SO:0000627" ],
+ [ "locus_control_region", "SO:0000037" ],
+ [ "matrix_attachment_region", "SO:0000036" ],
+ [ "minus_35_signal", "SO:0000176" ],
+ [ "minus_10_signal", "SO:0000175" ],
+ [ "polyA_signal_sequence", "SO:0000551" ],
+ [ "promoter", "SO:0000167" ],
+ [ "recoding_stimulatory_region", "SO:1001268" ],
+ [ "recombination_enhancer", "SO:0002059" ],
+ [ "replication_regulatory_region", "SO:0001682" ],
+ [ "response_element", nil ],
+ [ "ribosome_binding_site", "SO:0000552" ],
+ [ "riboswitch", "SO:0000035" ],
+ [ "silencer", "SO:0000625" ],
+ [ "TATA_box", "SO:0000174" ],
+ [ "terminator", "SO:0000141" ],
+ [ "transcriptional_cis_regulatory_region", "SO:0001055" ],
+ [ "uORF", "SO:0002027" ],
+ [ "other", nil ],
+ ]
+
+ REGULATORY_FUSION_ENUM_TYPES = REGULATORY_FUSION_TYPES.map { |(type, _)| [ type, type ] }.to_h
+
+ FUSION_PARTNER_STATUSES = [ "known", "unknown", "multiple", "regulatory" ].map { [ _1, _1 ] }.to_h
+
API_HMAC_KEY = ENV["CIVIC_API_HMAC_KEY"] || Rails.application.credentials.api_hmac_key
end
diff --git a/server/app/models/features/fusion.rb b/server/app/models/features/fusion.rb
index c883162a2..22bb99573 100644
--- a/server/app/models/features/fusion.rb
+++ b/server/app/models/features/fusion.rb
@@ -6,17 +6,10 @@ class Fusion < ActiveRecord::Base
belongs_to :five_prime_gene, class_name: "Features::Gene", optional: true
belongs_to :three_prime_gene, class_name: "Features::Gene", optional: true
- enum :five_prime_partner_status, {
- known: "known",
- unknown: "unknown",
- multiple: "multiple",
- }, prefix: true
+ enum :five_prime_partner_status, Constants::FUSION_PARTNER_STATUSES, prefix: true
+ enum :three_prime_partner_status, Constants::FUSION_PARTNER_STATUSES, prefix: true
- enum :three_prime_partner_status, {
- known: "known",
- unknown: "unknown",
- multiple: "multiple",
- }, prefix: true
+ enum :regulatory_fusion_type, Constants::REGULATORY_FUSION_ENUM_TYPES
has_many :variant_groups
has_many :source_suggestions
@@ -24,31 +17,22 @@ class Fusion < ActiveRecord::Base
# TODO - move to feature?
has_many :comment_mentions, foreign_key: :comment_id, class_name: "EntityMention"
- validate :partner_status_valid_for_gene_ids
- validate :at_least_one_gene_id
+ validates_with FusionFeatureValidator
- def partner_status_valid_for_gene_ids
- if !self.in_revision_validation_context
- [ self.five_prime_gene, self.three_prime_gene ].zip([ self.five_prime_partner_status, self.three_prime_partner_status ], [ :five_prime_gene, :three_prime_gene ]).each do |gene, status, fk|
- if gene.nil? && status == "known"
- errors.add(fk, "Partner status cannot be 'known' if the gene isn't set")
- elsif !gene.nil? && status != "known"
- errors.add(fk, "Partner status has to be 'known' if gene is set")
- end
- end
- end
+ def display_name
+ name
end
- def at_least_one_gene_id
- if !self.in_revision_validation_context && self.five_prime_gene_id.nil? && self.three_prime_gene_id.nil?
- errors.add(:base, "One or both of the genes need to be set")
+ def self.format_regulatory_fusion_type(rft)
+ if rft == "enhancer"
+ "reg_e"
+ elsif rft == "promoter"
+ "reg_p"
+ else
+ "reg_#{rft}"
end
end
- def display_name
- name
- end
-
def editable_fields
[
:description,
diff --git a/server/app/models/variant_type.rb b/server/app/models/variant_type.rb
index fe029b4ef..925eff052 100644
--- a/server/app/models/variant_type.rb
+++ b/server/app/models/variant_type.rb
@@ -3,6 +3,7 @@ class VariantType < ActiveRecord::Base
has_and_belongs_to_many :variants
has_and_belongs_to_many :pipeline_types
+ enum :regulatory_fusion_type, Constants::REGULATORY_FUSION_ENUM_TYPES
def url
if self.soid != "N/A"
diff --git a/server/app/models/variants/fusion_variant.rb b/server/app/models/variants/fusion_variant.rb
index 37c919794..2216260f3 100644
--- a/server/app/models/variants/fusion_variant.rb
+++ b/server/app/models/variants/fusion_variant.rb
@@ -65,7 +65,16 @@ def required_fields
def generate_vicc_name
if name == Constants::REPRESENTATIVE_FUSION_VARIANT_NAME
- "#{construct_five_prime_name(name_type: :representative)}::#{construct_three_prime_name(name_type: :representative)}"
+ # for vicc names, the regulatory component is always first, regardless of 5' vs 3'
+ if fusion.regulatory_fusion_type.present?
+ if fusion.five_prime_partner_status == "regulatory"
+ "#{construct_five_prime_name(name_type: :representative)}::#{construct_three_prime_name(name_type: :representative)}"
+ else
+ "#{construct_three_prime_name(name_type: :representative)}::#{construct_five_prime_name(name_type: :representative)}"
+ end
+ else
+ "#{construct_five_prime_name(name_type: :representative)}::#{construct_three_prime_name(name_type: :representative)}"
+ end
else
"#{construct_five_prime_name(name_type: :vicc)}::#{construct_three_prime_name(name_type: :vicc)}"
end
@@ -102,6 +111,7 @@ def construct_five_prime_name(name_type:)
partner_status: fusion.five_prime_partner_status,
gene: fusion.five_prime_gene,
exon_coords: five_prime_end_exon_coordinates,
+ regulatory_type: fusion.regulatory_fusion_type
)
end
@@ -111,10 +121,11 @@ def construct_three_prime_name(name_type:)
partner_status: fusion.three_prime_partner_status,
gene: fusion.three_prime_gene,
exon_coords: three_prime_start_exon_coordinates,
+ regulatory_type: fusion.regulatory_fusion_type
)
end
- def construct_partner_name(name_type:, partner_status:, gene:, exon_coords:)
+ def construct_partner_name(name_type:, partner_status:, gene:, exon_coords:, regulatory_type:)
if partner_status == "known"
case name_type
when :representative
@@ -128,6 +139,8 @@ def construct_partner_name(name_type:, partner_status:, gene:, exon_coords:)
"?"
elsif partner_status == "multiple"
"v"
+ elsif partner_status == "regulatory"
+ "#{Features::Fusion.format_regulatory_fusion_type(regulatory_type)}@#{gene.name}(entrez:#{gene.entrez_id})"
end
end
diff --git a/server/app/validators/fusion_feature_validator.rb b/server/app/validators/fusion_feature_validator.rb
new file mode 100644
index 000000000..e1b09b8d1
--- /dev/null
+++ b/server/app/validators/fusion_feature_validator.rb
@@ -0,0 +1,36 @@
+class FusionFeatureValidator < ActiveModel::Validator
+ def validate(record)
+ partner_status_valid_for_gene_ids(record)
+ at_least_one_gene_id(record)
+ regulatory_status_set_correctly(record)
+ end
+
+ private
+ def partner_status_valid_for_gene_ids(record)
+ if !record.in_revision_validation_context
+ [ record.five_prime_gene, record.three_prime_gene ].zip([ record.five_prime_partner_status, record.three_prime_partner_status ], [ :five_prime_gene, :three_prime_gene ]).each do |gene, status, fk|
+ if gene.nil? && (status == "known" || status == "regulatory")
+ record.errors.add(fk, "Partner status cannot be 'known' or 'regulatory' if the gene isn't set")
+ elsif !gene.nil? && (status != "known" && status != "regulatory")
+ record.errors.add(fk, "Partner status has to be 'known' or 'regulatory' if gene is set")
+ end
+ end
+ end
+ end
+
+ def at_least_one_gene_id(record)
+ if !record.in_revision_validation_context && record.five_prime_gene_id.nil? && record.three_prime_gene_id.nil?
+ record.errors.add(:base, "One or both of the genes need to be set")
+ end
+ end
+
+ def regulatory_status_set_correctly(record)
+ if record.three_prime_partner_status == "regulatory" && record.five_prime_partner_status == "regulatory"
+ record.errors.add(:base, "Only one fusion partner may be marked as regulatory")
+ elsif record.three_prime_partner_status == "regulatory" || record.five_prime_partner_status == "regulatory"
+ if record.regulatory_fusion_type.blank?
+ record.errors.add(:regulatory_fusion_type, "You must select a regulatory fusion type if one of the fusion partners is marked as regulatory")
+ end
+ end
+ end
+end
diff --git a/server/db/migrate/20250102170055_add_regulatory_fusion_types_enum.rb b/server/db/migrate/20250102170055_add_regulatory_fusion_types_enum.rb
new file mode 100644
index 000000000..36fd399ce
--- /dev/null
+++ b/server/db/migrate/20250102170055_add_regulatory_fusion_types_enum.rb
@@ -0,0 +1,26 @@
+class AddRegulatoryFusionTypesEnum < ActiveRecord::Migration[7.1]
+ def up
+ create_enum :regulatory_fusion_types, Constants::REGULATORY_FUSION_TYPES.map(&:first)
+ add_enum_value :fusion_partner_status, "regulatory"
+
+ add_column :variant_types, :regulatory_fusion_type, :enum, enum_type: :regulatory_fusion_types, null: true
+ add_column :fusions, :regulatory_fusion_type, :enum, enum_type: :regulatory_fusion_types, null: true
+ add_index :variant_types, :regulatory_fusion_type
+
+ Constants::REGULATORY_FUSION_TYPES.each do |(type, soid)|
+ if soid.present?
+ vt = VariantType.find_by!(soid: soid)
+ vt.regulatory_fusion_type = type
+ vt.save!
+ end
+ end
+ end
+
+ def down
+ remove_column :variant_types, :regulatory_fusion_type
+ remove_column :fusions, :regulatory_fusion_type
+ execute <<-SQL
+ DROP TYPE regulatory_fusion_types;
+ SQL
+ end
+end
diff --git a/server/db/schema.rb b/server/db/schema.rb
index f95fba0c7..a1d9b22c2 100644
--- a/server/db/schema.rb
+++ b/server/db/schema.rb
@@ -10,15 +10,17 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.1].define(version: 2024_12_20_161652) do
+ActiveRecord::Schema[8.0].define(version: 2025_03_07_152119) do
# These are extensions that must be enabled in order to support this database
- enable_extension "plpgsql"
+ enable_extension "pg_catalog.plpgsql"
# Custom types defined in this database.
# Note that some types may not work with other database engines. Be careful if changing database.
+ create_enum "endorsement_status", ["active", "revoked", "requires_review"]
create_enum "exon_coordinate_record_state", ["stub", "exons_provided", "fully_curated"]
create_enum "exon_offset_direction", ["positive", "negative"]
- create_enum "fusion_partner_status", ["known", "unknown", "multiple"]
+ create_enum "fusion_partner_status", ["known", "unknown", "multiple", "regulatory"]
+ create_enum "regulatory_fusion_types", ["attenuator", "CAAT_signal", "DNase_I_hypersensitive_site", "enhancer", "enhancer_blocking_element", "GC_signal", "imprinting_control_region", "insulator", "locus_control_region", "matrix_attachment_region", "minus_35_signal", "minus_10_signal", "polyA_signal_sequence", "promoter", "recoding_stimulatory_region", "recombination_enhancer", "replication_regulatory_region", "response_element", "ribosome_binding_site", "riboswitch", "silencer", "TATA_box", "terminator", "transcriptional_cis_regulatory_region", "uORF", "other"]
create_enum "variant_coordinate_record_state", ["stub", "fully_curated"]
create_table "acmg_codes", id: :serial, force: :cascade do |t|
@@ -97,11 +99,12 @@
t.index ["token", "search_type"], name: "index_advanced_searches_on_token_and_search_type"
end
- create_table "affiliations", id: false, force: :cascade do |t|
+ create_table "affiliations", force: :cascade do |t|
t.bigint "user_id"
t.bigint "organization_id"
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
+ t.boolean "can_endorse", default: false, null: false
t.index ["organization_id"], name: "index_affiliations_on_organization_id"
t.index ["user_id"], name: "index_affiliations_on_user_id"
end
@@ -382,6 +385,19 @@
t.index ["user_id"], name: "index_domain_expert_tags_on_user_id"
end
+ create_table "endorsements", force: :cascade do |t|
+ t.bigint "organization_id", null: false
+ t.bigint "user_id", null: false
+ t.bigint "assertion_id", null: false
+ t.enum "status", default: "active", null: false, enum_type: "endorsement_status"
+ t.datetime "last_reviewed", precision: nil, null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["assertion_id"], name: "index_endorsements_on_assertion_id"
+ t.index ["organization_id"], name: "index_endorsements_on_organization_id"
+ t.index ["user_id"], name: "index_endorsements_on_user_id"
+ end
+
create_table "entity_mentions", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@@ -550,6 +566,7 @@
t.enum "three_prime_partner_status", default: "unknown", null: false, enum_type: "fusion_partner_status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.enum "regulatory_fusion_type", enum_type: "regulatory_fusion_types"
t.index ["five_prime_gene_id"], name: "index_fusions_on_five_prime_gene_id"
t.index ["three_prime_gene_id"], name: "index_fusions_on_three_prime_gene_id"
end
@@ -680,6 +697,8 @@
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "most_recent_activity_timestamp", precision: nil
+ t.boolean "can_endorse", default: false, null: false
+ t.boolean "is_approved_vcep", default: false, null: false
t.index ["most_recent_activity_timestamp"], name: "index_organizations_on_most_recent_activity_timestamp"
end
@@ -977,8 +996,10 @@
t.integer "parent_id"
t.integer "lft"
t.integer "rgt"
+ t.enum "regulatory_fusion_type", enum_type: "regulatory_fusion_types"
t.index ["display_name"], name: "index_variant_types_on_display_name"
t.index ["name"], name: "index_variant_types_on_name"
+ t.index ["regulatory_fusion_type"], name: "index_variant_types_on_regulatory_fusion_type"
t.index ["soid"], name: "index_variant_types_on_soid"
end
@@ -1017,7 +1038,7 @@
t.boolean "deprecated", default: false, null: false
t.integer "deprecation_reason"
t.integer "deprecation_comment_id"
- t.text "open_cravat_url_parameters"
+ t.text "open_cravat_url"
t.bigint "feature_id"
t.string "type", null: false
t.string "ncit_id"
diff --git a/server/test/fixtures/feature_fusions.yml b/server/test/fixtures/feature_fusions.yml
new file mode 100644
index 000000000..b4a5cce50
--- /dev/null
+++ b/server/test/fixtures/feature_fusions.yml
@@ -0,0 +1,27 @@
+braf_vhl_fusion:
+ five_prime_gene: braf
+ three_prime_gene: vhl
+ five_prime_partner_status: known
+ three_prime_partner_status: known
+ regulatory_fusion_type: enhancer
+
+braf_fusion:
+ five_prime_gene: braf
+ three_prime_gene: null
+ five_prime_partner_status: known
+ three_prime_partner_status: unknown
+ regulatory_fusion_type: null
+
+vhl_fusion:
+ five_prime_gene: null
+ three_prime_gene: vhl
+ five_prime_partner_status: unknown
+ three_prime_partner_status: known
+ regulatory_fusion_type: null
+
+regulatory_fusion:
+ five_prime_gene: braf
+ three_prime_gene: vhl
+ five_prime_partner_status: regulatory
+ three_prime_partner_status: known
+ regulatory_fusion_type: enhancer
diff --git a/server/test/fixtures/features.yml b/server/test/fixtures/features.yml
index 86ffe0e40..816423435 100644
--- a/server/test/fixtures/features.yml
+++ b/server/test/fixtures/features.yml
@@ -13,3 +13,8 @@ msi:
full_name: Microsatellite Instability
description: The description for the MSI factor
feature_instance: msi (Features::Factor)
+
+braf_fusion:
+ name: BRAF::?
+ description: BRAF and unknown partner
+ feature_instance: braf_fusion (Features::Fusion)
diff --git a/server/test/models/fusion_feature_test.rb b/server/test/models/fusion_feature_test.rb
new file mode 100644
index 000000000..d0ae823c2
--- /dev/null
+++ b/server/test/models/fusion_feature_test.rb
@@ -0,0 +1,47 @@
+require "test_helper"
+
+class FusionFeatureTest < ActiveSupport::TestCase
+ setup do
+ feature = features(:braf_fusion)
+ @braf_vhl_fusion = feature_fusions(:braf_vhl_fusion)
+ @braf_vhl_fusion.feature = feature
+ @braf_fusion = feature_fusions(:braf_fusion)
+ @braf_fusion.feature = feature
+ @vhl_fusion = feature_fusions(:vhl_fusion)
+ @vhl_fusion.feature = feature
+ @regulatory_fusion = feature_fusions(:regulatory_fusion)
+ @regulatory_fusion.feature = feature
+ end
+
+ test "valid fusion with both genes set and known partner status" do
+ assert @braf_vhl_fusion.valid?
+ end
+
+ test "invalid fusion with one gene set and known partner status" do
+ @braf_fusion.three_prime_partner_status = "known"
+ assert_not @braf_fusion.valid?
+ assert_includes @braf_fusion.errors[:three_prime_gene], "Partner status cannot be 'known' or 'regulatory' if the gene isn't set"
+ end
+
+ test "invalid fusion with both genes unset" do
+ @braf_fusion.five_prime_gene = nil
+ assert_not @braf_fusion.valid?
+ assert_includes @braf_fusion.errors[:base], "One or both of the genes need to be set"
+ end
+
+ test "invalid fusion with both partners marked as regulatory" do
+ @regulatory_fusion.three_prime_partner_status = "regulatory"
+ assert_not @regulatory_fusion.valid?
+ assert_includes @regulatory_fusion.errors[:base], "Only one fusion partner may be marked as regulatory"
+ end
+
+ test "invalid fusion with regulatory partner but no regulatory fusion type" do
+ @regulatory_fusion.regulatory_fusion_type = nil
+ assert_not @regulatory_fusion.valid?
+ assert_includes @regulatory_fusion.errors[:regulatory_fusion_type], "You must select a regulatory fusion type if one of the fusion partners is marked as regulatory"
+ end
+
+ test "valid fusion with regulatory partner and regulatory fusion type" do
+ assert @regulatory_fusion.valid?
+ end
+end
diff --git a/server/test/test_helper.rb b/server/test/test_helper.rb
index b509ff1ff..9c733692d 100644
--- a/server/test/test_helper.rb
+++ b/server/test/test_helper.rb
@@ -8,6 +8,7 @@ class ActiveSupport::TestCase
set_fixture_class feature_genes: Features::Gene
set_fixture_class feature_factors: Features::Factor
+ set_fixture_class feature_fusions: Features::Fusion
# Add more helper methods to be used by all tests here...
end