diff --git a/src/app/views/add/evidence/AddEvidenceController.js b/src/app/views/add/evidence/AddEvidenceController.js index 0c9d645a6..a2c97a9cc 100644 --- a/src/app/views/add/evidence/AddEvidenceController.js +++ b/src/app/views/add/evidence/AddEvidenceController.js @@ -19,7 +19,7 @@ controllerAs: 'vm' }) .state('add.evidence.basic', { - url: '/basic?geneId&variantId&geneName&variantName&diseaseName&pubmedId&sourceSuggestionId&variantOrigin&evidenceType&evidenceDirection&evidenceLevel&clinicalSignificance', + url: '/basic?geneId&variantId&geneName&variantName&diseaseName&pubmedId&sourceType&sourceId&variantOrigin&evidenceType&evidenceDirection&evidenceLevel&clinicalSignificance&isSuggestedSource', template: '', data: { titleExp: '"Add Evidence"', diff --git a/src/app/views/add/evidence/addEvidenceBasic.js b/src/app/views/add/evidence/addEvidenceBasic.js index c724810eb..f711738cf 100644 --- a/src/app/views/add/evidence/addEvidenceBasic.js +++ b/src/app/views/add/evidence/addEvidenceBasic.js @@ -73,14 +73,13 @@ vm.newEvidence = { gene: '', variant: '', - pubmed_id: '', + source: {citation_id: '', source_type: ''}, description: '', disease: { name: '' }, disease_name: '', noDoid: false, - //pubchem_id: '', drugs: [], drug_interaction_type: null, rating: '', @@ -96,7 +95,7 @@ vm.newEvidence.comment = { title: 'Additional Comments', text:'' }; vm.newEvidence.drugs = []; - vm.newEvidence.source_suggestion_id = _.isUndefined($stateParams.sourceSuggestionId) ? null : Number($stateParams.sourceSuggestionId); + vm.newEvidence.source_suggestion_id = _.isUndefined($stateParams.sourceId) ? null : Number($stateParams.sourceId); vm.formErrors = {}; vm.formMessages = {}; @@ -204,60 +203,122 @@ } } }, - { - key: 'pubmed_id', - type: 'publication', + key: 'source.source_type', + type: 'horizontalSelectHelp', + wrapper: 'attributeDefinition', + controller: /* @ngInject */ function($scope, $stateParams, ConfigService, _) { + if($stateParams.sourceType) { + var st = $stateParams.sourceType; + var permitted = _.keys(ConfigService.evidenceAttributeDescriptions.source_type); + if(_.includes(permitted, st)) { + $scope.model.source_type = st; + $scope.to.data.attributeDefinition = $scope.to.data.attributeDefinitions[st]; + // update source field info + // this unfortunately reproduces code in onChange below, but updating value above doesn't trigger onChange... + var sourceField = _.find($scope.fields, { key: 'source.citation_id'}); + sourceField.templateOptions.data.sourceType = st; + } else { + console.warn('Ignoring pre-population of Source Type with invalid value: ' + st); + } + } + }, templateOptions: { - label: 'Pubmed ID', - value: 'vm.newEvidence.pubmed_id', - minLength: 1, + label: 'Source Type', required: true, + options: [{ value: '', label: 'Please select a Source Type' }].concat(make_options(descriptions.source_type)), + valueProp: 'value', + labelProp: 'label', + helpText: help['Source Type'], data: { - description: '--' + attributeDefinition: ' ', + attributeDefinitions: descriptions.source_type }, - helpText: help['Pubmed ID'] - }, - modelOptions: { - updateOn: 'default blur', - allowInvalid: false, - debounce: { - default: 300, - blur: 0 - } - }, - controller: /* @ngInject */ function($scope, $stateParams) { - if($stateParams.pubmedId) { - $scope.model.pubmed_id = $stateParams.pubmedId; + onChange: function(value, options, scope) { + // field value is showing up undefined here for some reason, accessing it via options + var val = options.value(); + // set attribute definition + options.templateOptions.data.attributeDefinition = options.templateOptions.data.attributeDefinitions[val]; + // set source_type on citation_id and clear field + var sourceField = _.find(scope.fields, { key: 'source.citation_id'}); + sourceField.value(''); + sourceField.templateOptions.data.citation = '--'; + if(val) { sourceField.templateOptions.data.sourceType = val; } + else { sourceField.templateOptions.data.sourceType = undefined; } } + } + }, + { + key: 'source.citation_id', + type: 'publication', + templateOptions: { + label: 'Source ID', + required: true, + data: { + citation: '--', + sourceType: undefined, + }, }, - validators: { - validPubmedId: { + asyncValidators: { + validId: { expression: function($viewValue, $modelValue, scope) { - if ($viewValue.length > 0) { - if ($viewValue.match(/[^0-9]+/)) { return false; } - var deferred = $q.defer(); + var type = scope.model.source.source_type; + var deferred = $q.defer(); + if ($viewValue.length > 0 && type !== '') { + if ($viewValue.match(/[^0-9]+/)) { return false; } // must be number scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: type + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; - deferred.resolve(response); + scope.options.templateOptions.data.citation = response.citation; + deferred.resolve(true); }, function (error) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = '--'; - deferred.reject(error); + if(error.status === 404) { + scope.options.templateOptions.data.citation = 'No ' + type + ' source found with specified ID.'; + } else { + scope.options.templateOptions.data.citation = 'Error fetching source, check console log for details.'; + } + deferred.reject(false); } ); - return deferred.promise; } else { scope.options.templateOptions.data.description = '--'; - return true; + deferred.resolve(true); } + return deferred.promise; }, - message: '"This does not appear to be a valid Pubmed ID."' + message: '"This does not appear to be a valid source ID."' } + }, + controller: /* @ngInject */ function($scope, $stateParams) { + if($stateParams.sourceId) { + // get citation + Sources.get($stateParams.sourceId) + .then(function(response){ + $scope.model.source = response; + $scope.to.data.citation = response.citation; + }); + } + }, + expressionProperties: { + 'templateOptions.disabled': 'to.data.sourceType === "" || to.data.sourceType === undefined', + 'templateOptions.label': 'to.data.sourceType ? to.data.sourceType === "ASCO" ? "ASCO Web ID" : "PubMed ID" : "Source ID"', + 'templateOptions.placeholder': 'to.data.sourceType ? to.data.sourceType === "ASCO" ? "Search by ASCO Abstract Number" : "Search by PubMed ID" : "Please select Source Type"', + // ng expressions here don't have access to config help objects, so we must clumsily insert them into the expression here + 'templateOptions.helpText': 'to.data.sourceType ? to.data.sourceType === "ASCO" ? "' + help['SourceASCO'] + '" : "' + help['SourcePubMed'] + '" : "Please enter a Source Type before entering a Source ID."', + }, + modelOptions: { + debounce: { + default: 300, + blur: 0 + }, + updateOn: 'default blur' } }, { // duplicates warning row @@ -268,28 +329,31 @@ vm.pubmedName = ''; function searchForDups(values) { + vm.duplicates = []; if(_.every(values, function(val) { return _.isString(val) && val.length > 0; })) { - vm.duplicates = []; Search.post({ - 'operator': 'AND', - 'queries': [ - { - 'field': 'gene_name', - 'condition': {'name': 'contains', 'parameters': [values[0]]} - }, - { - 'field': 'variant_name', - 'condition': {'name': 'contains', 'parameters': [values[1]]} - }, - { - 'field': 'pubmed_id', - 'condition': {'name': 'is', 'parameters': [values[2]] - } - } - ], - 'entity': 'evidence_items', - 'save': false - }) + 'operator': 'AND', + 'queries': [ + { + 'field': 'gene_name', + 'condition': {'name': 'contains', 'parameters': [values[0]]} + }, + { + 'field': 'variant_name', + 'condition': {'name': 'contains', 'parameters': [values[1]]} + }, + { + 'field': 'source_type', + 'condition': {'name': 'is_equal_to', 'parameters': [values[2]]} + }, + { + 'field': 'citation_id', + 'condition': {'name': 'is_equal_to', 'parameters': [values[3]]} + } + ], + 'entity': 'evidence_items', + 'save': false + }) .then(function (response) { vm.duplicates = response.results; }); @@ -301,7 +365,8 @@ $scope.$watchGroup([ 'model.gene.name', 'model.variant.name', - 'model.pubmed_id' + 'model.source_type', + 'model.source.citation_id' ], searchForDups); } }, @@ -323,6 +388,7 @@ }, templateOptions: { label: 'Variant Origin', + required: true, value: 'vm.newEvidence.variant_origin', options: [{ value: '', label: 'Please select a Variant Origin' }].concat(make_options(descriptions.variant_origin)), valueProp: 'value', @@ -593,8 +659,8 @@ 'templateOptions.options': function($viewValue, $modelValue, scope) { return _.filter(scope.to.clinicalSignificanceOptions, function(option) { return !!(option.type === scope.model.evidence_type || - option.type === 'default' || - option.type === 'N/A'); + option.type === 'default' || + option.type === 'N/A'); }); }, 'templateOptions.disabled': 'model.evidence_type === ""' // deactivate if evidence_type unselected @@ -656,7 +722,7 @@ }, hideExpression: function($viewValue, $modelValue, scope) { return !(scope.model.evidence_type === 'Predictive' && // evidence type must be predictive - _.without(scope.model.drugs, '').length > 1); + _.without(scope.model.drugs, '').length > 1); } }, { @@ -752,6 +818,7 @@ newEvidence.evidenceId = newEvidence.id; newEvidence.drugs = _.without(newEvidence.drugs, ''); newEvidence.phenotypes = _.without(newEvidence.phenotypes, ''); + if(newEvidence.drugs.length < 2) { newEvidence.drug_interaction_type = null; } // delete interaction if only 1 drug // convert variant name to object, if a string // TODO: figure out how to handle this more elegantly using angular-formly config object diff --git a/src/app/views/browse/directives/browseGrid.js b/src/app/views/browse/directives/browseGrid.js index c1cc33166..d0e80a43b 100644 --- a/src/app/views/browse/directives/browseGrid.js +++ b/src/app/views/browse/directives/browseGrid.js @@ -753,8 +753,27 @@ ], 'sources': [ { - name: 'pubmed_id', - displayName: 'Pubmed ID', + name: 'source_type', + displayName: 'Type', + enableFiltering: true, + allowCellFocus: false, + cellTemplate: 'app/views/events/common/genericHighlightCell.tpl.html', + type: 'string', + width: '8%', + filter: { + type: uiGridConstants.filter.SELECT, + term: null, + disableCancelFilterButton: false, + selectOptions: [ + { value: null, label: '--' }, + { value: '0', label: 'PubMed'}, + { value: '1', label: 'ASCO'} + ] + } + }, + { + name: 'citation_id', + displayName: 'Citation ID', enableFiltering: true, allowCellFocus: false, cellTemplate: 'app/views/events/common/genericHighlightCell.tpl.html', @@ -770,7 +789,7 @@ enableFiltering: true, allowCellFocus: false, type: 'string', - width: '20%', + width: '15%', cellTemplate: 'app/views/browse/directives/browseGridTooltipCell.tpl.html', filter: { condition: uiGridConstants.filter.CONTAINS @@ -794,7 +813,7 @@ type: 'string', enableFiltering: true, allowCellFocus: false, - width: '15%', + width: '12%', cellTemplate: 'app/views/browse/directives/browseGridTooltipCell.tpl.html', filter: { condition: uiGridConstants.filter.CONTAINS diff --git a/src/app/views/events/evidence/edit/evidenceEditBasic.js b/src/app/views/events/evidence/edit/evidenceEditBasic.js index 218ebe81b..8bcfd1b08 100644 --- a/src/app/views/events/evidence/edit/evidenceEditBasic.js +++ b/src/app/views/events/evidence/edit/evidenceEditBasic.js @@ -4,6 +4,7 @@ .directive('evidenceEditBasic', evidenceEditBasicDirective) .controller('EvidenceEditBasicController', EvidenceEditBasicController); + // @ngInject function evidenceEditBasicDirective() { return { @@ -58,10 +59,14 @@ vm.evidenceRevisions = EvidenceRevisions; vm.evidenceHistory = EvidenceHistory; vm.evidenceEdit = angular.copy(vm.evidence); - vm.evidenceEdit.pubmed_id = vm.evidence.source.pubmed_id; + vm.evidenceEdit = _.omit(vm.evidenceEdit, ['lifecycle_actions']); vm.evidenceEdit.comment = { title: 'Evidence EID' + vm.evidence.id + ' Revision Description', text:'' }; vm.evidenceEdit.drugs = _.filter(_.map(vm.evidence.drugs, 'name'), function(name){ return name !== 'N/A'; }); vm.evidenceEdit.phenotypes = _.map(vm.evidenceEdit.phenotypes, function(phenotype) { return phenotype.hpo_class; }); + vm.evidenceEdit.source = { + source_type: vm.evidenceEdit.source.source_type, + citation_id: vm.evidenceEdit.source.citation_id + }; vm.styles = EvidenceViewOptions.styles; vm.user = {}; @@ -108,52 +113,113 @@ } }, { - key: 'pubmed_id', - type: 'publication', + key: 'source.source_type', + type: 'horizontalSelectHelp', + wrapper: 'attributeDefinition', + controller: /* @ngInject */ function($scope) { + // set attribute definition + var type = $scope.model.source_type === 'asco' ? 'ASCO':'PubMed'; + $scope.options.templateOptions.data.attributeDefinition = + $scope.options.templateOptions.data.attributeDefinitions[type]; + }, templateOptions: { - label: 'Pubmed ID', - value: 'vm.evidenceEdit.pubmed_id', - minLength: 1, + label: 'Source Type', required: true, + // here we specify options instead of generating from config b/c the server gives us lowercase type strings instead of the multi-case strings used for the labels + options: [{ value: '', label: 'Please select a Source Type' }].concat(make_options(descriptions.source_type)), + valueProp: 'value', + labelProp: 'label', + helpText: help['Source Type'], data: { - description: '--' + attributeDefinition: ' ', + attributeDefinitions: descriptions.source_type }, - helpText: help['Pubmed ID'] - }, - modelOptions: { - updateOn: 'default blur', - allowInvalid: false, - debounce: { - default: 300, - blur: 0 + onChange: function(value, options, scope) { + // set attribute definition + // server returns all lowercase for source_type, we need to convert to the multicase + // versions to match the attribute descriptions here... + var type = value === 'asco' ? 'ASCO':'PubMed'; + options.templateOptions.data.attributeDefinition = options.templateOptions.data.attributeDefinitions[value]; + // set source_type on citation_id and clear field + var sourceField = _.find(scope.fields, { key: 'source.citation_id'}); + sourceField.value(''); + sourceField.templateOptions.data.citation = '--'; + if(value) { sourceField.templateOptions.data.sourceType = value; } + else { sourceField.templateOptions.data.sourceType = undefined; } } + } + }, + { + key: 'source.citation_id', + type: 'publication', + templateOptions: { + label: 'Source ID', + required: true, + data: { + citation: '--', + }, + helpText: help['Source'] }, - validators: { - validPubmedId: { + asyncValidators: { + validId: { expression: function($viewValue, $modelValue, scope) { - if ($viewValue.length > 0) { - var deferred = $q.defer(); + var type = scope.model.source.source_type; + var deferred = $q.defer(); + if ($viewValue.length > 0 && type !== '') { + if ($viewValue.match(/[^0-9]+/)) { return false; } // must be number scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: type + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; - deferred.resolve(response); + scope.options.templateOptions.data.citation = response.citation; + deferred.resolve(true); }, function (error) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = '--'; - deferred.reject(error); + if(error.status === 404) { + scope.options.templateOptions.data.citation = 'No ' + type + ' source found with specified ID.'; + } else { + scope.options.templateOptions.data.citation = 'Error fetching source, check console log for details.'; + } + deferred.reject(false); } ); - return deferred.promise; } else { scope.options.templateOptions.data.description = '--'; - return true; + deferred.resolve(true); } + return deferred.promise; }, - message: '"This does not appear to be a valid Pubmed ID."' + message: '"This does not appear to be a valid source ID."' } + }, + controller: /* @ngInject */ function($scope, $stateParams) { + if($stateParams.sourceId) { + // get citation + Sources.get($stateParams.sourceId) + .then(function(response){ + $scope.model.source = response; + $scope.to.data.citation = response.citation; + }); + } + }, + expressionProperties: { + 'templateOptions.disabled': 'model.source.source_type === "" || model.source.source_type === undefined', + 'templateOptions.label': 'model.source.source_type ? model.source.source_type === "ASCO" ? "ASCO ID" : "PubMed ID" : "Source ID"', + 'templateOptions.placeholder': 'model.source.source_type ? model.source.source_type === "ASCO" ? "Search by ASCO Abstract Number" : "Search by PubMed ID" : "Please select Source Type"', + 'templateOptions.helpText': 'model.source.source_type ? model.source.source_type === "ASCO" ? "' + help['SourceASCO'] + '" : "' + help['SourcePubMed'] + '" : "Please enter a Source Type before entering a Source ID."', + + }, + modelOptions: { + debounce: { + default: 300, + blur: 0 + }, + updateOn: 'default blur' } }, { @@ -216,7 +282,6 @@ templateOptions: { label: 'Disease Name', required: true, - value: 'vm.newEvidence.disease_name', minLength: 32, helpText: help['Disease Name'] }, @@ -229,7 +294,6 @@ rows: 5, required: true, label: 'Evidence Statement', - value: 'vm.evidenceEdit.description', minLength: 32, helpText: help['Evidence Statement'] } diff --git a/src/app/views/events/evidence/edit/evidenceEditBasic.tpl.html b/src/app/views/events/evidence/edit/evidenceEditBasic.tpl.html index 560ba4d84..9144b3779 100644 --- a/src/app/views/events/evidence/edit/evidenceEditBasic.tpl.html +++ b/src/app/views/events/evidence/edit/evidenceEditBasic.tpl.html @@ -105,7 +105,7 @@

Thank you.

- + diff --git a/src/app/views/events/evidence/summary/evidenceSummary.tpl.html b/src/app/views/events/evidence/summary/evidenceSummary.tpl.html index d849ebc12..0cc4736b8 100644 --- a/src/app/views/events/evidence/summary/evidenceSummary.tpl.html +++ b/src/app/views/events/evidence/summary/evidenceSummary.tpl.html @@ -106,7 +106,7 @@ - Citation: + Source: {{ evidence.source.citation }} @@ -114,13 +114,22 @@ - Pubmed ID: + + + + PubMed ID: + + + ASCO ID: + + + - - + diff --git a/src/app/views/events/genes/edit/geneEditBasic.js b/src/app/views/events/genes/edit/geneEditBasic.js index d33da94e6..71c8e3d5e 100644 --- a/src/app/views/events/genes/edit/geneEditBasic.js +++ b/src/app/views/events/genes/edit/geneEditBasic.js @@ -38,6 +38,7 @@ vm.isAuthenticated = Security.isAuthenticated(); vm.gene = Genes.data.item; + // convert source objects to array of IDs, multi-input field type does not handle objects at this time vm.pendingFields = _.keys(GeneRevisions.data.pendingFields).length > 0; vm.pendingFieldsList = _.map(_.keys(GeneRevisions.data.pendingFields), function(field) { return field.charAt(0).toUpperCase() + field.slice(1); @@ -46,7 +47,7 @@ vm.geneHistory = GeneHistory; vm.geneEdit = angular.copy(vm.gene); vm.geneEdit.comment = { title: 'GENE ' + vm.gene.name + ' Revision Description', text:'' }; - vm.geneEdit.source_ids = _.map(vm.gene.sources, 'pubmed_id'); + vm.geneEdit.source_ids = _.map(vm.gene.sources, 'citation_id'); vm.myGeneInfo = geneModel.data.myGeneInfo; vm.variants = geneModel.data.variants; vm.variantGroups = geneModel.data.variantGroups; @@ -121,7 +122,7 @@ minLength: 1, required: true, data: { - description: '--' + citation: '--' } }, modelOptions: { @@ -138,10 +139,14 @@ if ($viewValue.length > 0) { var deferred = $q.defer(); scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: 'PubMed' + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; + scope.options.templateOptions.data.citation = response[0].description; deferred.resolve(response); }, function (error) { diff --git a/src/app/views/events/genes/edit/geneEditBasic.tpl.html b/src/app/views/events/genes/edit/geneEditBasic.tpl.html index 892b82c05..1c3790739 100644 --- a/src/app/views/events/genes/edit/geneEditBasic.tpl.html +++ b/src/app/views/events/genes/edit/geneEditBasic.tpl.html @@ -98,11 +98,11 @@

Thank you.

- - - +
+
+
 
-  
-  
-  
+      
+
+
diff --git a/src/app/views/events/variantGroups/edit/variantGroupEditBasic.js b/src/app/views/events/variantGroups/edit/variantGroupEditBasic.js index 13931998d..48ae0b896 100644 --- a/src/app/views/events/variantGroups/edit/variantGroupEditBasic.js +++ b/src/app/views/events/variantGroups/edit/variantGroupEditBasic.js @@ -49,7 +49,7 @@ vm.variantGroupHistory = VariantGroupHistory; vm.variantGroupEdit = angular.copy(vm.variantGroup); vm.variantGroupEdit.comment = { title: 'VARIANT GROUP ' + vm.variantGroup.name + ' Revision Description', text:'' }; - vm.variantGroupEdit.sources = _.map(vm.variantGroup.sources, 'pubmed_id'); + vm.variantGroupEdit.sources = _.map(vm.variantGroup.sources, 'citation_id'); vm.variantGroupEdit.variantsEdit = _.map(vm.variantGroupEdit.variants, function(variant) { return { name: variant.entrez_name + ' - ' + variant.name, id: variant.id }; }); @@ -105,7 +105,7 @@ minLength: 1, required: true, data: { - description: '--' + citation: '--' } }, modelOptions: { @@ -122,10 +122,14 @@ if ($viewValue.length > 0) { var deferred = $q.defer(); scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: 'PubMed' + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; + scope.options.templateOptions.data.citation = response[0].description; deferred.resolve(response); }, function (error) { diff --git a/src/app/views/events/variants/edit/variantEditBasic.js b/src/app/views/events/variants/edit/variantEditBasic.js index 0a349a11f..eb3c22684 100644 --- a/src/app/views/events/variants/edit/variantEditBasic.js +++ b/src/app/views/events/variants/edit/variantEditBasic.js @@ -49,7 +49,7 @@ vm.variantEdit = angular.copy(_.omit(vm.variant, ['evidence_items', 'lifecycle_actions'])); vm.variantEdit.comment = { title: 'VARIANT ' + vm.variant.name + ' Suggested Revision', text:'' }; - vm.variantEdit.sources = _.map(vm.variant.sources, 'pubmed_id'); + vm.variantEdit.sources = _.map(vm.variant.sources, 'citation_id'); vm.variantEdit.noClinVar = false; vm.myVariantInfo = variantModel.data.myVariantInfo; @@ -244,7 +244,7 @@ minLength: 1, required: true, data: { - description: '--' + citation: '--' } }, modelOptions: { @@ -261,10 +261,14 @@ if ($viewValue.length > 0) { var deferred = $q.defer(); scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: 'PubMed' + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; + scope.options.templateOptions.data.citation = response[0].description; deferred.resolve(response); }, function (error) { diff --git a/src/app/views/sources/components/sourceSuggestionGrid.js b/src/app/views/sources/components/sourceSuggestionGrid.js index f79499395..aea8932a9 100644 --- a/src/app/views/sources/components/sourceSuggestionGrid.js +++ b/src/app/views/sources/components/sourceSuggestionGrid.js @@ -138,11 +138,30 @@ cellTemplate: '
' }, { - name: 'pubmed_id', - displayName: 'Pubmed ID', - width: '10%', + name: 'source_type', + displayName: 'Type', + enableFiltering: true, + allowCellFocus: false, + cellTemplate: 'app/views/events/common/genericHighlightCell.tpl.html', + type: 'string', + width: '8%', + filter: { + type: uiGridConstants.filter.SELECT, + term: null, + disableCancelFilterButton: false, + selectOptions: [ + { value: null, label: '--' }, + { value: 'PubMed', label: 'PubMed'}, + { value: 'ASCO', label: 'ASCO'} + ] + } + }, + { + name: 'citation_id', + displayName: 'Citation ID', + width: '8%', visible: true, - cellTemplate: '', + cellTemplate: '
{{ row.entity[col.field] }}
', //visible: mode === 'full', enableFiltering: true, allowCellFocus: false, @@ -157,7 +176,7 @@ visible: mode === 'full', enableFiltering: true, allowCellFocus: false, - width: '15%', + width: '13%', type: 'string', cellTemplate: 'app/views/sources/components/cellTemplateCitation.tpl.html', filter: { @@ -278,11 +297,14 @@ if(_.has(source, 'disease') && source.disease !== null) { urlElements.push('diseaseName=' + source.disease); } - if(_.has(source, 'pubmed_id') && source.pubmed_id !== null) { - urlElements.push('pubmedId=' + source.pubmed_id); + if(_.has(source, 'source_type') && source.source_type !== null) { + urlElements.push('sourceType=' + source.source_type); + } + if(_.has(source, 'citation_id') && source.citation_id !== null) { + urlElements.push('citationId=' + source.citation_id); } - urlElements.push('sourceSuggestionId=' + source.id); + urlElements.push('sourceId=' + source.source_id); source.addEvidenceUrl = urlBase + '?' + urlElements.join('&'); return source; diff --git a/src/app/views/sources/summary/SourcesSummaryController.js b/src/app/views/sources/summary/SourcesSummaryController.js index b4c99a9d4..79955e432 100644 --- a/src/app/views/sources/summary/SourcesSummaryController.js +++ b/src/app/views/sources/summary/SourcesSummaryController.js @@ -43,14 +43,25 @@ var query = { 'operator': 'AND', 'queries': [ - {'field': 'pubmed_id', + { + 'field': 'source_type', 'condition': { - 'name': 'is', + 'name': 'is_equal_to', 'parameters': [ - source.pubmed_id + source.source_type ] } - }], + }, + { + 'field': 'citation_id', + 'condition': { + 'name': 'is_equal_to', + 'parameters': [ + source.citation_id + ] + } + } + ], 'entity': 'evidence_items', 'save': true }; diff --git a/src/app/views/sources/summary/sourcesSummary.tpl.html b/src/app/views/sources/summary/sourcesSummary.tpl.html index cef7a9984..09c3d5710 100644 --- a/src/app/views/sources/summary/sourcesSummary.tpl.html +++ b/src/app/views/sources/summary/sourcesSummary.tpl.html @@ -10,13 +10,14 @@

-

Full Source Title

-

Authors: Authors List

+

Full Source Title

+ Authors: Authors List
@@ -38,14 +39,35 @@

Full Source Title

- Pubmed ID: + + + + PubMed ID: + + + ASCO ID: + + + - - - - + + + + + + + + + + + + + + diff --git a/src/app/views/suggest/source/SuggestSourceController.js b/src/app/views/suggest/source/SuggestSourceController.js index d5688164b..774c6344e 100644 --- a/src/app/views/suggest/source/SuggestSourceController.js +++ b/src/app/views/suggest/source/SuggestSourceController.js @@ -17,6 +17,8 @@ console.log('SuggestSourceController called.'); var help = ConfigService.evidenceHelpText; + var descriptions = ConfigService.evidenceAttributeDescriptions; + var make_options = ConfigService.optionMethods.make_options; // make options for pull down var vm = $scope.vm = {}; vm.isAuthenticated = Security.isAuthenticated(); @@ -28,7 +30,9 @@ vm.error = {}; vm.newSuggestion= { - suggestion: { + source: { + citation_id: '', + source_type: '' }, comment: { title: 'Source Suggestion Comment' @@ -37,53 +41,103 @@ vm.suggestionFields =[ { - key: 'pubmed_id', - type: 'publication', - model: vm.newSuggestion.suggestion, + key: 'source.source_type', + type: 'horizontalSelectHelp', + wrapper: 'attributeDefinition', templateOptions: { - label: 'Pubmed ID', - value: 'vm.newSuggestion.pubmed_id', - minLength: 1, + label: 'Source Type', required: true, + // here we specify options instead of generating from config b/c the server gives us lowercase type strings instead of the multi-case strings used for the labels + options: [{ value: '', label: 'Please select a Source Type' }].concat(make_options(descriptions.source_type)), + valueProp: 'value', + labelProp: 'label', + helpText: help['Source Type'], data: { - description: '--' + attributeDefinition: ' ', + attributeDefinitions: descriptions.source_type }, - helpText: help['Pubmed ID'] - }, - modelOptions: { - updateOn: 'default blur', - allowInvalid: false, - debounce: { - default: 300, - blur: 0 + onChange: function(value, options, scope) { + options.templateOptions.data.attributeDefinition = options.templateOptions.data.attributeDefinitions[value]; + // set source_type on citation_id and clear field + var sourceField = _.find(scope.fields, { key: 'source.citation_id'}); + sourceField.value(''); + sourceField.templateOptions.data.citation = '--'; + if(value) { sourceField.templateOptions.data.sourceType = value; } + else { sourceField.templateOptions.data.sourceType = undefined; } } + } + }, + { + key: 'source.citation_id', + type: 'publication', + templateOptions: { + label: 'Source ID', + required: true, + data: { + citation: '--', + }, + helpText: help['Source'] }, - validators: { - validPubmedId: { + asyncValidators: { + validId: { expression: function($viewValue, $modelValue, scope) { - if (!_.isUndefined($viewValue) && $viewValue.length > 0) { - var deferred = $q.defer(); + var type = scope.model.source.source_type; + var deferred = $q.defer(); + if ($viewValue.length > 0 && type !== '') { + if ($viewValue.match(/[^0-9]+/)) { return false; } // must be number scope.options.templateOptions.loading = true; - Publications.verify($viewValue).then( + var reqObj = { + citationId: $viewValue, + sourceType: type + }; + Publications.verify(reqObj).then( function (response) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = response.description; - deferred.resolve(response); + scope.options.templateOptions.data.citation = response.citation; + deferred.resolve(true); }, function (error) { scope.options.templateOptions.loading = false; - scope.options.templateOptions.data.description = '--'; - deferred.reject(error); + if(error.status === 404) { + scope.options.templateOptions.data.citation = 'No ' + type + ' source found with specified ID.'; + } else { + scope.options.templateOptions.data.citation = 'Error fetching source, check console log for details.'; + } + deferred.reject(false); } ); - return deferred.promise; } else { scope.options.templateOptions.data.description = '--'; - return true; + deferred.resolve(true); } + return deferred.promise; }, - message: '"This does not appear to be a valid Pubmed ID."' + message: '"This does not appear to be a valid source ID."' } + }, + controller: /* @ngInject */ function($scope, $stateParams) { + if($stateParams.sourceId) { + // get citation + Sources.get($stateParams.sourceId) + .then(function(response){ + $scope.model.source = response; + $scope.to.data.citation = response.citation; + }); + } + }, + expressionProperties: { + 'templateOptions.disabled': 'model.source.source_type === "" || model.source.source_type === undefined', + 'templateOptions.label': 'model.source.source_type ? model.source.source_type === "ASCO" ? "ASCO ID" : "PubMed ID" : "Source ID"', + 'templateOptions.placeholder': 'model.source.source_type ? model.source.source_type === "ASCO" ? "Search by ASCO Abstract Number" : "Search by PubMed ID" : "Please select Source Type"', + 'templateOptions.helpText': 'model.source.source_type ? model.source.source_type === "ASCO" ? "' + help['SourceASCO'] + '" : "' + help['SourcePubMed'] + '" : "Please enter a Source Type before entering a Source ID."', + + }, + modelOptions: { + debounce: { + default: 300, + blur: 0 + }, + updateOn: 'default blur' } }, { @@ -120,10 +174,12 @@ } } }, - modelOptions: { - debounce: { - default: 300 - } + 'modelOptions': { + 'debounce': { + 'default': 200, + 'blur': 0 + }, + 'updateOn': 'default blur' } }, { @@ -185,7 +241,16 @@ typeaheadSearch: function(val) { return Diseases.beginsWith(val) .then(function(response) { - return response; + var labelLimit = 70; + return _.map(response, function(disease) { + if (disease.aliases.length > 0) { + disease.alias_list = disease.aliases.join(', '); + if(disease.alias_list.length > labelLimit) { disease.alias_list = _.truncate(disease.alias_list, labelLimit); } + } else { + disease.alias_list = '--'; + } + return disease; + }); }); } } @@ -197,35 +262,33 @@ hideExpression: 'model.noDoid' }, { // duplicates warning row - templateUrl: 'app/views/suggest/source/evidenceDuplicateWarning.tpl.html', + templateUrl: 'app/views/add/evidence/addEvidenceDuplicateWarning.tpl.html', controller: /* @ngInject */ function($scope, Search) { - console.log('dup warning controller loaded.'); var vm = $scope.vm = {}; vm.duplicates = []; vm.pubmedName = ''; function searchForDups(values) { + vm.duplicates = []; if(_.every(values, function(val) { return _.isString(val) && val.length > 0; })) { Search.post({ 'operator': 'AND', 'queries': [ { - 'field': 'pubmed_id', - 'condition': {'name': 'is', 'parameters': [values[0]] - } + 'field': 'gene_name', + 'condition': {'name': 'contains', 'parameters': [values[0]]} }, { - 'field': 'gene_name', + 'field': 'variant_name', 'condition': {'name': 'contains', 'parameters': [values[1]]} }, { - 'field': 'variant_name', - 'condition': {'name': 'contains', 'parameters': [values[2]]} + 'field': 'source_type', + 'condition': {'name': 'is_equal_to', 'parameters': [values[2]]} }, { - 'field': 'disease_doid', - 'condition': {'name': 'is', 'parameters': [values[3]] - } + 'field': 'citation_id', + 'condition': {'name': 'is_equal_to', 'parameters': [values[3]]} } ], 'entity': 'evidence_items', @@ -239,13 +302,11 @@ $scope.pubmedField = _.find($scope.fields, { key: 'pubmed_id' }); - // {"operator":"AND","queries":[{"field":"pubmed_id","condition":{"name":"is","parameters":["25589621"]}},{"field":"gene_name","condition":{"name":"contains","parameters":["BRAF"]}},{"field":"variant_name","condition":{"name":"contains","parameters":["V600E"]}},{"field":"disease_doid","condition":{"name":"is","parameters":["9256"]}}]} - $scope.$watchGroup([ - 'model.suggestion.pubmed_id', - 'model.suggestion.gene.name', - 'model.suggestion.variant.name', - 'model.suggestion.disease.doid' + 'model.gene.name', + 'model.variant.name', + 'model.source_type', + 'model.source.citation_id' ], searchForDups); } }, @@ -268,12 +329,18 @@ vm.submit = function(req) { vm.error = {}; var reqObj = { - pubmed_id: req.suggestion.pubmed_id, + source: req.source, comment: req.comment }; - if(!_.isUndefined(req.suggestion.gene) && _.isObject(req.suggestion.gene)) {reqObj.gene_name = req.suggestion.gene.name;} - if(!_.isUndefined(req.suggestion.variant) && _.isObject(req.suggestion.variant)) {reqObj.variant_name = req.suggestion.variant.name;} - if(!_.isUndefined(req.suggestion.disease) && _.isObject(req.suggestion.disease)) {reqObj.disease_name = req.suggestion.disease.name;} + if(!_.isUndefined(req.gene) && _.isObject(req.gene)) { + reqObj.gene_name = req.gene.name; + } + if(!_.isUndefined(req.variant) && _.isObject(req.variant)) { + reqObj.variant_name = req.variant.name; + } + if(!_.isUndefined(req.disease) && _.isObject(req.disease)) { + reqObj.disease_name = req.disease.name; + } Sources.suggest(reqObj).then( function(response) { // success console.log('source suggestion submit success.'); diff --git a/src/app/views/suggest/source/suggestSource.tpl.html b/src/app/views/suggest/source/suggestSource.tpl.html index ab49266f4..d9f2ba7b8 100644 --- a/src/app/views/suggest/source/suggestSource.tpl.html +++ b/src/app/views/suggest/source/suggestSource.tpl.html @@ -19,7 +19,7 @@

Sugges

@@ -45,7 +45,7 @@

Sugges class="btn btn-default" ng-disabled="vm.form.$invalid" ng-click="vm.submit(vm.newSuggestion, vm.formOptions)"> - Submit Evidence for Inclusion + Submit Source for Evaluation

@@ -75,14 +75,14 @@

Thank you.

- - - - - - - - - - + + + + + + + + + + diff --git a/src/components/directives/assertionTag.less b/src/components/directives/assertionTag.less index 7e61db1d1..2cb921b3a 100644 --- a/src/components/directives/assertionTag.less +++ b/src/components/directives/assertionTag.less @@ -7,6 +7,7 @@ @tabPaddingV: .25em; .assertionTag { + display: inline-block; a { text-decoration: none; padding: @tabPaddingV @tabPaddingH; diff --git a/src/components/directives/typeaheadWrapper.js b/src/components/directives/typeaheadWrapper.js index d059df634..a027b8dac 100644 --- a/src/components/directives/typeaheadWrapper.js +++ b/src/components/directives/typeaheadWrapper.js @@ -13,6 +13,7 @@ { var template = ' + ID: CITATION ID  + CITATION: DESCRIPTIOn + diff --git a/src/components/forms/fieldTypes/publication.js b/src/components/forms/fieldTypes/publication.js index 19123af1c..0bd754bc6 100644 --- a/src/components/forms/fieldTypes/publication.js +++ b/src/components/forms/fieldTypes/publication.js @@ -1,29 +1,19 @@ (function() { 'use strict'; angular.module('civic.config') - .config(publicationConfig) - .controller('PublicationController', PublicationController); + .config(publicationConfig); // @ngInject function publicationConfig(formlyConfigProvider) { formlyConfigProvider.setType({ name: 'publication', extends: 'input', - wrapper: ['loader', 'pubdisplay', 'validationMessages', 'horizontalBootstrapHelp', 'bootstrapHasError'] + wrapper: ['loader', 'citation', 'validationMessages', 'horizontalBootstrapHelp', 'bootstrapHasError'] }); formlyConfigProvider.setType({ name: 'publication-multi', extends: 'input', - wrapper: ['loader','pubdisplay'] + wrapper: ['loader','citation'] }); } - - // @ngInject - function PublicationController($scope, Publications) { - console.log('PublicationController called.'); - $scope.validatePublication = function(pubmedId) { - return Publications.verify(pubmedId); - }; - } - })(); diff --git a/src/components/forms/fieldWrappers/basicFieldWrappers.js b/src/components/forms/fieldWrappers/basicFieldWrappers.js index 8d9540586..bb8e4d7c3 100644 --- a/src/components/forms/fieldWrappers/basicFieldWrappers.js +++ b/src/components/forms/fieldWrappers/basicFieldWrappers.js @@ -116,10 +116,10 @@ }); formlyConfigProvider.setWrapper({ - name: 'pubdisplay', + name: 'citation', template: [ '', - 'Citation: {{ to.data.description }}' + 'Citation: {{ to.data.citation }}' ].join(' ') }); diff --git a/src/components/services/ConfigService.js b/src/components/services/ConfigService.js index b1549979d..abc239aa1 100644 --- a/src/components/services/ConfigService.js +++ b/src/components/services/ConfigService.js @@ -301,6 +301,10 @@ 'Unknown': 'The variant origin is uncertain based on the available evidence.', 'N/A': 'The variant type (e.g., expression) is not compatible (or easily classified) with the CIViC concepts of variant origin.' }, + source_type: { + 'PubMed': 'Evidence item source uses a PubMed publication.', + 'ASCO': 'Evidence item source uses an ASCO abstract.' + }, evidence_type: { 'Predictive': 'Evidence pertains to a variant\'s effect on therapeutic response', 'Diagnostic': 'Evidence pertains to a variant\'s impact on patient diagnosis (cancer subtype)', @@ -404,7 +408,10 @@ 'Drug Names' : 'For predictive evidence, specify one or more drug names. Drugs specified must possess a PubChem ID (e.g., 44462760 for Dabrafenib).', 'Drug Interaction Type' : 'Please indicate whether the drugs specified above are substitutes, or are used in sequential or combination treatments.', 'Phenotypes' : 'Please provide any HPO phenotypes.', - 'Rating' : '

Please rate your evidence on a scale of one to five stars. Use the star rating descriptions for guidance.

', + 'Rating' : 'Please rate your evidence on a scale of one to five stars. Use the star rating descriptions for guidance.', + 'Source Type': 'CIViC accepts PubMed or ASCO Abstracts sources. Please indicate the source of the support for your evidence here.', + 'SourceASCO': 'Please enter an ASCO Web ID to specify your source. NOTE: The ASCO Web ID is not the ASCO abstract number. The ASCO Web ID can be found in the URL of the abstract, e.g. 160900 is the ASCO Web ID for Tyler Stewart, 2018, ASCO Annual Meeting, Abstract 9056 (https://meetinglibrary.asco.org/record/160900/abstract).', + 'SourcePubMed': 'Please enter the PubMed ID for your source.', 'Revision Description' : 'Please provide a short description of your edits to this Evidence record.', 'Additional Comments' : 'Please provide any additional comments you wish to make about this evidence item. This comment will appear as the first comment in this item\'s comment thread.', 'keepSourceStatus' : 'Check this box if you wish the originating source suggestion to keep its un-curated status. Otherwise, it will be marked as curated and removed from the source suggestion queues.', diff --git a/src/components/services/PublicationsService.js b/src/components/services/PublicationsService.js index 95655d708..249c6c90a 100644 --- a/src/components/services/PublicationsService.js +++ b/src/components/services/PublicationsService.js @@ -1,33 +1,32 @@ (function() { 'use strict'; angular.module('civic.services') + .factory('PublicationsResource', PublicationsResource) .factory('Publications', PublicationsService); // @ngInject function PublicationsResource($resource) { - return $resource('/api/sources', - {}, - { - query: { - method: 'GET', - isArray: true, - cache: true - }, - get: { - method: 'GET', - url: '/api/sources/existence/:pubmedId', - isArray: false, - cache: true - }, - verify: { - method: 'GET', - url: '/api/sources/existence/:pubmedId', - isArray: false, - cache: true - } + return $resource('/api/sources', { + }, { + query: { + method: 'GET', + isArray: true, + cache: true + }, + get: { + method: 'GET', + url: '/api/sources/existence/:citationId', + isArray: false, + cache: true + }, + verify: { + method: 'GET', + url: '/api/sources/existence/:citationId?source_type=:sourceType', + isArray: false, + cache: true } - ); + }); } // @ngInject @@ -53,16 +52,18 @@ }); } - function get(pubmedId) { - return PublicationsResource.get({pubmedId: pubmedId}).$promise + function get(citationId) { + return PublicationsResource.get({ + citationId: citationId + }).$promise .then(function(response) { angular.copy(response, item); return response.$promise; }); } - function verify(pubmedId) { - return PublicationsResource.verify({pubmedId: pubmedId}).$promise + function verify(reqObj) { + return PublicationsResource.verify(reqObj).$promise .then(function(response) { return response.$promise; });