From 282d38a74be2500eb6cb962ff4279040dadbf944 Mon Sep 17 00:00:00 2001 From: Don Naegely Date: Wed, 9 Apr 2014 13:51:57 -0400 Subject: [PATCH 01/85] Move template registration out of main.js Signed-off-by: Don Naegely --- varify/static/js/min/main.js | 2 +- varify/static/js/min/ui.js | 2 +- varify/static/js/min/ui/templates.js | 1 + varify/static/js/src/main.js | 47 +++------------------------- varify/static/js/src/ui.js | 1 + varify/static/js/src/ui/templates.js | 26 +++++++++++++++ 6 files changed, 35 insertions(+), 44 deletions(-) create mode 100644 varify/static/js/min/ui/templates.js create mode 100644 varify/static/js/src/ui/templates.js diff --git a/varify/static/js/min/main.js b/varify/static/js/min/main.js index c15fac88..8858d90f 100644 --- a/varify/static/js/min/main.js +++ b/varify/static/js/min/main.js @@ -1 +1 @@ -require({config:{tpl:{variable:"data"}},shim:{bootstrap:["jquery"],marionette:{deps:["backbone"],exports:"Marionette"},highcharts:{deps:["jquery"],exports:"Highcharts"}}},["cilantro","project/ui","project/csrf","tpl!../../templates/tables/header.html","tpl!../../templates/modals/result.html","tpl!../../templates/modals/phenotypes.html","tpl!../../templates/modals/phenotype/annotations.html","tpl!../../templates/modals/phenotype/annotationsItem.html","tpl!../../templates/modals/phenotype/diagnoses.html","tpl!../../templates/modals/phenotype/diagnosesItem.html","tpl!../../templates/modals/phenotype/empty.html","tpl!../../templates/controls/sift.html","tpl!../../templates/controls/polyphen.html","tpl!../../templates/workflows/results.html","tpl!../../templates/export/dialog.html","tpl!../../templates/sample/loader.html"],function(e,t,n,r,i,s,o,u,a,f,l,c,h,p,d,v){var m=2,g=111;e.config.set("varify.sample.concept",m),e.config.set("varify.sample.field",g);var y={url:e.config.get("url"),credentials:e.config.get("credentials")},b=function(){var t={view:{columns:[m]}},n;return e.session&&(n=e.session.data.views.session.get("json"))&&(t.view.ordering=n.ordering),t};e.templates.set("varify/export/dialog",d),e.templates.set("varify/tables/header",r),e.templates.set("varify/modals/result",i),e.templates.set("varify/modals/phenotype",s),e.templates.set("varify/modals/phenotype/annotations",o),e.templates.set("varify/modals/phenotype/annotationsItem",u),e.templates.set("varify/modals/phenotype/diagnoses",a),e.templates.set("varify/modals/phenotype/diagnosesItem",f),e.templates.set("varify/modals/phenotype/empty",l),e.templates.set("varify/controls/sift",c),e.templates.set("varify/controls/polyphen",h),e.templates.set("varify/workflows/results",p),e.templates.set("varify/sample/loader",v),e.config.set("fields.defaults.form.stats",!1),e.config.set("fields.types.number.form.chart",!1),e.config.set("fields.types.date.form.chart",!1),e.config.set("fields.types.time.form.chart",!1),e.config.set("fields.types.datetime.form.chart",!1),e.config.set("fields.instances.27.form.controls",["multiSelectionList"]),e.config.set("fields.instances.28.form.controls",["multiSelectionList"]),e.config.set("fields.instances.29.form.controls",["multiSelectionList"]),e.config.set("fields.instances.61.form.controls",["multiSelectionList"]),e.config.set("fields.instances.64.form.controls",["multiSelectionList"]),e.config.set("fields.instances.75.form.controls",["search"]),e.config.set("fields.instances.68.form.controls",["singleSelectionList"]),e.controls.set("Sift",t.SiftSelector),e.controls.set("PolyPhen",t.PolyPhenSelector),e.config.set("fields.instances.58.form.controls",["Sift"]),e.config.set("fields.instances.56.form.controls",["PolyPhen"]),e.config.set("fields.instances.110.form.controls",[{options:{isNullLabel:"Not In HGMD",isNotNullLabel:"In HGMD"},control:"nullSelector"}]);var w=function(t){return function(t){if(e.data==null)return;var n=_.map(t||[],function(t){if((currConcept=e.data.concepts.get(t.concept))!=null)return currConcept.get("name")}),r;return n?r="The following concepts are required: "+n.join(", "):r="There are 1 or more required concepts",e.notify({level:"error",message:r})}}(this);e.config.set("filters.required",[{concept:2}]),e.on(e.CONTEXT_INVALID,w),e.on(e.CONTEXT_REQUIRED,w),e.ready(function(){e.sessions.open(y).then(function(){e.config.set("session.defaults.data.preview",b),e.panels={concept:new e.ui.ConceptPanel({collection:this.data.concepts.queryable}),context:new e.ui.ContextPanel({model:this.data.contexts.session})},e.dialogs={exporter:new t.ExporterDialog({exporters:this.data.exporter}),columns:new e.ui.ConceptColumnsDialog({view:this.data.views.session,concepts:this.data.concepts.viewable}),query:new e.ui.EditQueryDialog({view:this.data.views.session,context:this.data.contexts.session,collection:this.data.queries}),deleteQuery:new e.ui.DeleteQueryDialog,resultDetails:new t.ResultDetails,phenotype:new t.Phenotype({context:this.data.contexts.session})};var n=[];$.each(e.panels,function(e,t){t.render(),n.push(t.el)}),$.each(e.dialogs,function(e,t){t.render(),n.push(t.el)});var r=$(e.config.get("main"));r.append.apply(r,n),e.workflows={query:new e.ui.QueryWorkflow({context:this.data.contexts.session,concepts:this.data.concepts.queryable}),results:new t.ResultsWorkflow({view:this.data.views.session,context:this.data.contexts.session,results:this.data.preview}),sampleload:new t.SampleLoader({context:this.data.contexts.session})};var i=[{id:"query",route:"query/",view:e.workflows.query},{id:"results",route:"results/",view:e.workflows.results},{id:"sample-load",route:"sample/:sample_id/",view:e.workflows.sampleload}];e.workflows.workspace=new e.ui.WorkspaceWorkflow({queries:this.data.queries,context:this.data.contexts.session,view:this.data.views.session,public_queries:this.data.public_queries}),i.push({id:"workspace",route:"workspace/",view:e.workflows.workspace}),e.workflows.queryload=new e.ui.QueryLoader({queries:this.data.queries,context:this.data.contexts.session,view:this.data.views.session}),i.push({id:"query-load",route:"results/:query_id/",view:e.workflows.queryload}),this.start(i)})})}) \ No newline at end of file +require({config:{tpl:{variable:"data"}},shim:{bootstrap:["jquery"],marionette:{deps:["backbone"],exports:"Marionette"},highcharts:{deps:["jquery"],exports:"Highcharts"}}},["cilantro","project/ui","project/csrf"],function(e,t,n){var r={url:e.config.get("url"),credentials:e.config.get("credentials")},i=function(){var t={view:{columns:[2]}},n;return(n=e.session.data.views.session.get("json"))!=null&&(t.view.ordering=n.ordering),t};e.config.set("fields.defaults.form.stats",!1),e.config.set("fields.types.number.form.chart",!1),e.config.set("fields.types.date.form.chart",!1),e.config.set("fields.types.time.form.chart",!1),e.config.set("fields.types.datetime.form.chart",!1),e.config.set("fields.instances.27.form.controls",["multiSelectionList"]),e.config.set("fields.instances.28.form.controls",["multiSelectionList"]),e.config.set("fields.instances.29.form.controls",["multiSelectionList"]),e.config.set("fields.instances.61.form.controls",["multiSelectionList"]),e.config.set("fields.instances.64.form.controls",["multiSelectionList"]),e.config.set("fields.instances.75.form.controls",["search"]),e.config.set("fields.instances.68.form.controls",["singleSelectionList"]),e.controls.set("Sift",t.SiftSelector),e.controls.set("PolyPhen",t.PolyPhenSelector),e.config.set("fields.instances.58.form.controls",["Sift"]),e.config.set("fields.instances.56.form.controls",["PolyPhen"]),e.config.set("fields.instances.110.form.controls",[{options:{isNullLabel:"Not In HGMD",isNotNullLabel:"In HGMD"},control:"nullSelector"}]),e.config.set("session.defaults.data.preview",i);var s=function(t){return function(t){if(e.data==null)return;var n=_.map(t||[],function(t){if((currConcept=e.data.concepts.get(t.concept))!=null)return currConcept.get("name")}),r;return n?r="The following concepts are required: "+n.join(", "):r="There are 1 or more required concepts",e.notify({level:"error",message:r})}}(this);e.config.set("query.concepts.required",[2]),e.on(e.CONTEXT_INVALID,s),e.on(e.CONTEXT_REQUIRED,s),e.ready(function(){e.sessions.open(r).then(function(){e.panels={concept:new e.ui.ConceptPanel({collection:this.data.concepts.queryable}),context:new e.ui.ContextPanel({model:this.data.contexts.session})},e.dialogs={exporter:new t.ExporterDialog({exporters:this.data.exporter}),columns:new e.ui.ConceptColumnsDialog({view:this.data.views.session,concepts:this.data.concepts.viewable}),query:new e.ui.EditQueryDialog({view:this.data.views.session,context:this.data.contexts.session,collection:this.data.queries}),resultDetails:new t.ResultDetails,phenotype:new t.Phenotype({context:this.data.contexts.session})};var n=[];$.each(e.panels,function(e,t){t.render(),n.push(t.el)}),$.each(e.dialogs,function(e,t){t.render(),n.push(t.el)});var r=$(e.config.get("main"));r.append.apply(r,n),e.workflows={query:new e.ui.QueryWorkflow({context:this.data.contexts.session,concepts:this.data.concepts.queryable}),results:new t.ResultsWorkflow({view:this.data.views.session,context:this.data.contexts.session,results:this.data.preview}),sampleload:new t.SampleLoader({context:this.data.contexts.session})};var i=[{id:"query",route:"query/",view:e.workflows.query},{id:"results",route:"results/",view:e.workflows.results},{id:"sample-load",route:"sample/:sample_id/",view:e.workflows.sampleload}];e.workflows.workspace=new e.ui.WorkspaceWorkflow({queries:this.data.queries,context:this.data.contexts.session,view:this.data.views.session,public_queries:this.data.public_queries}),i.push({id:"workspace",route:"workspace/",view:e.workflows.workspace}),e.workflows.queryload=new e.ui.QueryLoader({queries:this.data.queries,context:this.data.contexts.session,view:this.data.views.session}),i.push({id:"query-load",route:"results/:query_id/",view:e.workflows.queryload}),this.start(i)})})}) \ No newline at end of file diff --git a/varify/static/js/min/ui.js b/varify/static/js/min/ui.js index d6865ee7..653dccc9 100644 --- a/varify/static/js/min/ui.js +++ b/varify/static/js/min/ui.js @@ -1 +1 @@ -define(["underscore","./ui/controls","./ui/exporter","./ui/modals","./ui/sample","./ui/tables","./ui/workflows"],function(e){var t=Array.prototype.slice.call(arguments,1);return e.extend.apply(e,[{}].concat(t))}) \ No newline at end of file +define(["underscore","./ui/controls","./ui/exporter","./ui/modals","./ui/sample","./ui/tables","./ui/templates","./ui/workflows"],function(e){var t=Array.prototype.slice.call(arguments,1);return e.extend.apply(e,[{}].concat(t))}) \ No newline at end of file diff --git a/varify/static/js/min/ui/templates.js b/varify/static/js/min/ui/templates.js new file mode 100644 index 00000000..1f1ed411 --- /dev/null +++ b/varify/static/js/min/ui/templates.js @@ -0,0 +1 @@ +define(["cilantro","tpl!../../../templates/tables/header.html","tpl!../../../templates/modals/result.html","tpl!../../../templates/modals/phenotypes.html","tpl!../../../templates/controls/sift.html","tpl!../../../templates/controls/polyphen.html","tpl!../../../templates/workflows/results.html","tpl!../../../templates/export/dialog.html","tpl!../../../templates/sample/loader.html"],function(e,t,n,r,i,s,o,u,a){e.templates.set("varify/export/dialog",u),e.templates.set("varify/tables/header",t),e.templates.set("varify/modals/result",n),e.templates.set("varify/modals/phenotype",r),e.templates.set("varify/controls/sift",i),e.templates.set("varify/controls/polyphen",s),e.templates.set("varify/workflows/results",o),e.templates.set("varify/sample/loader",a)}) \ No newline at end of file diff --git a/varify/static/js/src/main.js b/varify/static/js/src/main.js index b4ca98cf..2aeedc8a 100644 --- a/varify/static/js/src/main.js +++ b/varify/static/js/src/main.js @@ -20,30 +20,8 @@ require({ }, [ 'cilantro', 'project/ui', - 'project/csrf', - 'tpl!../../templates/tables/header.html', - 'tpl!../../templates/modals/result.html', - 'tpl!../../templates/modals/phenotypes.html', - 'tpl!../../templates/modals/phenotype/annotations.html', - 'tpl!../../templates/modals/phenotype/annotationsItem.html', - 'tpl!../../templates/modals/phenotype/diagnoses.html', - 'tpl!../../templates/modals/phenotype/diagnosesItem.html', - 'tpl!../../templates/modals/phenotype/empty.html', - 'tpl!../../templates/controls/sift.html', - 'tpl!../../templates/controls/polyphen.html', - 'tpl!../../templates/workflows/results.html', - 'tpl!../../templates/export/dialog.html', - 'tpl!../../templates/sample/loader.html' -], function(c, ui, csrf, header, result, phenotype, annotations, annotationsItem, - diagnoses, diagnosesItem, empty, sift, polyphen, results, exportDialog, - sampleLoader) { - - var SAMPLE_CONCEPT_ID = 2, - SAMPLE_FIELD_ID = 111; - - // Add namespaced variables for reference in other modules - c.config.set('varify.sample.concept', SAMPLE_CONCEPT_ID); - c.config.set('varify.sample.field', SAMPLE_FIELD_ID); + 'project/csrf' +], function(c, ui, csrf) { // Session options var options = { @@ -54,33 +32,18 @@ require({ var augmentFixedView = function() { var newView = { view: { - columns: [SAMPLE_CONCEPT_ID] + columns: [2] } }; var json; - if (c.session && (json = c.session.data.views.session.get('json'))) { - newView.view.ordering = json.ordering; + if ((json = c.session.data.views.session.get('json')) != null) { + newView['view']['ordering'] = json['ordering']; } return newView; }; - // Define custom templates - c.templates.set('varify/export/dialog', exportDialog); - c.templates.set('varify/tables/header', header); - c.templates.set('varify/modals/result', result); - c.templates.set('varify/modals/phenotype', phenotype); - c.templates.set('varify/modals/phenotype/annotations', annotations); - c.templates.set('varify/modals/phenotype/annotationsItem', annotationsItem); - c.templates.set('varify/modals/phenotype/diagnoses', diagnoses); - c.templates.set('varify/modals/phenotype/diagnosesItem', diagnosesItem); - c.templates.set('varify/modals/phenotype/empty', empty); - c.templates.set('varify/controls/sift', sift); - c.templates.set('varify/controls/polyphen', polyphen); - c.templates.set('varify/workflows/results', results); - c.templates.set('varify/sample/loader', sampleLoader); - // Globally disable stats on all fields c.config.set('fields.defaults.form.stats', false); diff --git a/varify/static/js/src/ui.js b/varify/static/js/src/ui.js index 4031d898..afc02684 100644 --- a/varify/static/js/src/ui.js +++ b/varify/static/js/src/ui.js @@ -7,6 +7,7 @@ define([ './ui/modals', './ui/sample', './ui/tables', + './ui/templates', './ui/workflows' ], function(_) { diff --git a/varify/static/js/src/ui/templates.js b/varify/static/js/src/ui/templates.js new file mode 100644 index 00000000..e0a10d71 --- /dev/null +++ b/varify/static/js/src/ui/templates.js @@ -0,0 +1,26 @@ +/* global define */ + +define([ + 'cilantro', + 'tpl!../../../templates/tables/header.html', + 'tpl!../../../templates/modals/result.html', + 'tpl!../../../templates/modals/phenotypes.html', + 'tpl!../../../templates/controls/sift.html', + 'tpl!../../../templates/controls/polyphen.html', + 'tpl!../../../templates/workflows/results.html', + 'tpl!../../../templates/export/dialog.html', + 'tpl!../../../templates/sample/loader.html' +], function(c, header, result, phenotype, sift, polyphen, results, + exportDialog, sampleLoader) { + + // Define custom templates + c.templates.set('varify/export/dialog', exportDialog); + c.templates.set('varify/tables/header', header); + c.templates.set('varify/modals/result', result); + c.templates.set('varify/modals/phenotype', phenotype); + c.templates.set('varify/controls/sift', sift); + c.templates.set('varify/controls/polyphen', polyphen); + c.templates.set('varify/workflows/results', results); + c.templates.set('varify/sample/loader', sampleLoader); + +}); From d5010659158909c1483ec1a762ab8e97692dd19d Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Thu, 24 Apr 2014 14:42:40 -0400 Subject: [PATCH 02/85] Add ResultSet and ResultSetItem models with admin integration Signed-off-by: Byron Ruth --- varify/samples/admin.py | 14 +- ..._resultset_sample_name__add_resultsetit.py | 333 ++++++++++++++++++ 2 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 varify/samples/migrations/0034_auto__add_resultset__add_unique_resultset_sample_name__add_resultsetit.py diff --git a/varify/samples/admin.py b/varify/samples/admin.py index 55abf80e..98dae5aa 100644 --- a/varify/samples/admin.py +++ b/varify/samples/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from guardian.admin import GuardedModelAdmin -from vdw.samples.models import Project, Cohort, Batch, Sample, SampleManifest +from vdw.samples.models import Project, Cohort, Batch, Sample, \ + SampleManifest, ResultSet class ProjectAdmin(GuardedModelAdmin): @@ -76,8 +77,19 @@ def formatted_content(self, instance): formatted_content.short_description = 'Content' +class ResultSetAdmin(admin.ModelAdmin): + list_display = ('name', 'sample', 'user') + + fieldsets = ( + (None, { + 'fields': ('name', 'sample', 'user', 'description'), + }), + ) + + admin.site.register(Sample, SampleAdmin) admin.site.register(SampleManifest, SampleManifestAdmin) admin.site.register(Cohort, CohortAdmin) admin.site.register(Project, ProjectAdmin) admin.site.register(Batch, BatchAdmin) +admin.site.register(ResultSet, ResultSetAdmin) diff --git a/varify/samples/migrations/0034_auto__add_resultset__add_unique_resultset_sample_name__add_resultsetit.py b/varify/samples/migrations/0034_auto__add_resultset__add_unique_resultset_sample_name__add_resultsetit.py new file mode 100644 index 00000000..69cbcf32 --- /dev/null +++ b/varify/samples/migrations/0034_auto__add_resultset__add_unique_resultset_sample_name__add_resultsetit.py @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'ResultSet' + db.create_table('result_set', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('count', self.gf('django.db.models.fields.PositiveIntegerField')(default=0)), + ('created', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('modified', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('sample', self.gf('django.db.models.fields.related.ForeignKey')(related_name='result_sets', to=orm['samples.Sample'])), + ('name', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + )) + db.send_create_signal('samples', ['ResultSet']) + + # Adding unique constraint on 'ResultSet', fields ['sample', 'name'] + db.create_unique('result_set', ['sample_id', 'name']) + + # Adding model 'ResultSetItem' + db.create_table('result_set_item', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('added', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('removed', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('result', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['samples.Result'])), + ('set', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['samples.ResultSet'])), + )) + db.send_create_signal('samples', ['ResultSetItem']) + + # Adding unique constraint on 'ResultSetItem', fields ['result', 'set'] + db.create_unique('result_set_item', ['result_id', 'set_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'ResultSetItem', fields ['result', 'set'] + db.delete_unique('result_set_item', ['result_id', 'set_id']) + + # Removing unique constraint on 'ResultSet', fields ['sample', 'name'] + db.delete_unique('result_set', ['sample_id', 'name']) + + # Deleting model 'ResultSet' + db.delete_table('result_set') + + # Deleting model 'ResultSetItem' + db.delete_table('result_set_item') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'genome.chromosome': { + 'Meta': {'ordering': "['order']", 'object_name': 'Chromosome', 'db_table': "'chromosome'"}, + 'code': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '2'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '2', 'db_index': 'True'}) + }, + 'genome.genome': { + 'Meta': {'object_name': 'Genome', 'db_table': "'genome'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'released': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'genome.genotype': { + 'Meta': {'object_name': 'Genotype', 'db_table': "'genotype'"}, + 'code': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '3'}) + }, + 'literature.pubmed': { + 'Meta': {'object_name': 'PubMed', 'db_table': "'pubmed'"}, + 'pmid': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}) + }, + 'phenotypes.phenotype': { + 'Meta': {'object_name': 'Phenotype', 'db_table': "'phenotype'"}, + 'articles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['literature.PubMed']", 'symmetrical': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'hpo_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'term': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1000'}) + }, + 'samples.batch': { + 'Meta': {'ordering': "('project', 'label')", 'unique_together': "(('project', 'name'),)", 'object_name': 'Batch', 'db_table': "'batch'"}, + 'count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'investigator': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'batches'", 'to': "orm['samples.Project']"}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'samples.cohort': { + 'Meta': {'ordering': "('-order', 'name')", 'object_name': 'Cohort', 'db_table': "'cohort'"}, + 'allele_freq_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'autocreated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'batch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Batch']", 'null': 'True', 'blank': 'True'}), + 'count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Project']", 'null': 'True', 'blank': 'True'}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'samples': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['samples.Sample']", 'through': "orm['samples.CohortSample']", 'symmetrical': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'samples.cohortsample': { + 'Meta': {'object_name': 'CohortSample', 'db_table': "'cohort_sample'"}, + 'added': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_set': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Cohort']", 'db_column': "'cohort_id'"}), + 'removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'set_object': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Sample']", 'db_column': "'sample_id'"}) + }, + 'samples.cohortvariant': { + 'Meta': {'unique_together': "(('variant', 'cohort'),)", 'object_name': 'CohortVariant', 'db_table': "'cohort_variant'"}, + 'af': ('django.db.models.fields.FloatField', [], {'null': 'True', 'db_index': 'True'}), + 'cohort': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Cohort']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'variant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cohort_details'", 'to': "orm['variants.Variant']"}) + }, + 'samples.person': { + 'Meta': {'object_name': 'Person', 'db_table': "'person'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'mrn': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'proband': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'relations': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['samples.Person']", 'through': "orm['samples.Relation']", 'symmetrical': 'False'}), + 'sex': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}) + }, + 'samples.project': { + 'Meta': {'unique_together': "(('name',),)", 'object_name': 'Project', 'db_table': "'project'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'samples.relation': { + 'Meta': {'ordering': "('person', '-generation')", 'object_name': 'Relation', 'db_table': "'relation'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'generation': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'family'", 'to': "orm['samples.Person']"}), + 'relative': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relative_of'", 'to': "orm['samples.Person']"}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + }, + 'samples.result': { + 'Meta': {'unique_together': "(('sample', 'variant'),)", 'object_name': 'Result', 'db_table': "'sample_result'"}, + 'base_counts': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'baseq_rank_sum': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'coverage_alt': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'coverage_ref': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'downsampling': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'fisher_strand': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'genotype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['genome.Genotype']", 'null': 'True', 'blank': 'True'}), + 'genotype_quality': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'haplotype_score': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'homopolymer_run': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_dbsnp': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'mq': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'mq0': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'mq_rank_sum': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'phred_scaled_likelihood': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'quality': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'quality_by_depth': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'raw_read_depth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'read_depth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'read_pos_rank_sum': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'sample': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'results'", 'to': "orm['samples.Sample']"}), + 'spanning_deletions': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'strand_bias': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), + 'variant': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['variants.Variant']"}) + }, + 'samples.resultscore': { + 'Meta': {'object_name': 'ResultScore', 'db_table': "'result_score'"}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'rank': ('django.db.models.fields.IntegerField', [], {}), + 'result': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'score'", 'unique': 'True', 'to': "orm['samples.Result']"}), + 'score': ('django.db.models.fields.FloatField', [], {}) + }, + 'samples.resultset': { + 'Meta': {'unique_together': "(('sample', 'name'),)", 'object_name': 'ResultSet', 'db_table': "'result_set'"}, + 'count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'results': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['samples.Result']", 'through': "orm['samples.ResultSetItem']", 'symmetrical': 'False'}), + 'sample': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'result_sets'", 'to': "orm['samples.Sample']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'samples.resultsetitem': { + 'Meta': {'unique_together': "(('result', 'set'),)", 'object_name': 'ResultSetItem', 'db_table': "'result_set_item'"}, + 'added': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'removed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'result': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Result']"}), + 'set': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.ResultSet']"}) + }, + 'samples.sample': { + 'Meta': {'ordering': "('project', 'batch', 'label')", 'unique_together': "(('batch', 'name', 'version'),)", 'object_name': 'Sample', 'db_table': "'sample'"}, + 'batch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'samples'", 'to': "orm['samples.Batch']"}), + 'bio_sample': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'person': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'samples'", 'null': 'True', 'to': "orm['samples.Person']"}), + 'phenotype_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'samples'", 'to': "orm['samples.Project']"}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'vcf_colname': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'version': ('django.db.models.fields.IntegerField', [], {}) + }, + 'samples.samplemanifest': { + 'Meta': {'object_name': 'SampleManifest', 'db_table': "'sample_manifest'"}, + 'content': ('django.db.models.fields.TextField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'sample': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'manifest'", 'unique': 'True', 'to': "orm['samples.Sample']"}) + }, + 'samples.samplerun': { + 'Meta': {'object_name': 'SampleRun', 'db_table': "'sample_run'"}, + 'completed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'genome': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['genome.Genome']", 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'sample': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['samples.Sample']"}) + }, + 'variants.variant': { + 'Meta': {'unique_together': "(('chr', 'pos', 'ref', 'alt'),)", 'object_name': 'Variant', 'db_table': "'variant'"}, + 'alt': ('django.db.models.fields.TextField', [], {'db_index': 'True'}), + 'articles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['literature.PubMed']", 'db_table': "'variant_pubmed'", 'symmetrical': 'False'}), + 'chr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['genome.Chromosome']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'liftover': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'md5': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'phenotypes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['phenotypes.Phenotype']", 'through': "orm['variants.VariantPhenotype']", 'symmetrical': 'False'}), + 'pos': ('django.db.models.fields.IntegerField', [], {}), + 'ref': ('django.db.models.fields.TextField', [], {'db_index': 'True'}), + 'rsid': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['variants.VariantType']", 'null': 'True'}) + }, + 'variants.variantphenotype': { + 'Meta': {'object_name': 'VariantPhenotype', 'db_table': "'variant_phenotype'"}, + 'hgmd_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'phenotype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['phenotypes.Phenotype']"}), + 'variant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variant_phenotypes'", 'to': "orm['variants.Variant']"}) + }, + 'variants.varianttype': { + 'Meta': {'ordering': "['order']", 'object_name': 'VariantType', 'db_table': "'variant_type'"}, + 'code': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + } + } + + complete_apps = ['samples'] \ No newline at end of file From 86baeefe9dafe1d8f7c383f0cd25a7e974f1b9ce Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Thu, 24 Apr 2014 14:54:21 -0400 Subject: [PATCH 03/85] Update default modeltree with ResultSet route Signed-off-by: Byron Ruth --- varify/conf/global_settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/varify/conf/global_settings.py b/varify/conf/global_settings.py index 44f4dd69..7526bc09 100644 --- a/varify/conf/global_settings.py +++ b/varify/conf/global_settings.py @@ -362,6 +362,9 @@ }, { 'target': 'samples.cohortvariant', 'source': 'variants.variant', + }, { + 'target': 'samples.resultset', + 'source': 'samples.resultsetitem', }], 'excluded_routes': [{ 'target': 'genes.gene', From f3f4d1bd22674b06c13ba77c698a2763d3b3f29a Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Thu, 24 Apr 2014 15:15:40 -0400 Subject: [PATCH 04/85] Move contents of global_settings to settings The local_settings.py.sample file and README has been updated to reflect this change. Projects can import the settings and extend where needed. Signed-off-by: Byron Ruth --- README.md | 2 +- varify/conf/local_settings.py.sample | 34 +-- varify/conf/settings.py | 425 +++++++++++++++++++++++++-- 3 files changed, 411 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 8910b439..6eaa3a29 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ pip install solvebio ``` Then, make sure that the `SOLVEBIO_API_KEY` Django setting is set either -via an environment variable (see `global_settings.py`) or explicitly in your +via an environment variable (see `settings.py`) or explicitly in your `local_settings.py`. You can find your API key from your account page on the SolveBio website. diff --git a/varify/conf/local_settings.py.sample b/varify/conf/local_settings.py.sample index 14d4b51f..90d23d6e 100644 --- a/varify/conf/local_settings.py.sample +++ b/varify/conf/local_settings.py.sample @@ -1,5 +1,4 @@ -import os -from global_settings import PROJECT_PATH, LOGGING +from varify.conf.settings import * # Uncomment to put the application in non-debug mode. This is useful # for testing error handling and messages. @@ -39,36 +38,11 @@ RQ_QUEUES = { # Non-restricted email port for development, run in a terminal: # python -m smtpd -n -c DebuggingServer localhost:1025 EMAIL_PORT = 1025 -EMAIL_SUBJECT_PREFIX = '[varify] ' +EMAIL_SUBJECT_PREFIX = '[Varify] ' # This is used as a "seed" for various hashing algorithms. This must be set to # a very long random string (40+ characters) -SECRET_KEY = 'g0u!o7_mw=cfhrgb+#xupma&@99443u06xs19$rlmt#wyt9xve' - -# Uncomment for additional logging. If using the 'rotating_file' handler -# you must create the `logs` directory in the project root. -# LOGGING['handlers'].update({ -# 'stdout': { -# 'class': 'logging.StreamHandler', -# 'level': 'DEBUG', -# }, -# 'rotating_file': { -# 'class': 'logging.handlers.RotatingFileHandler', -# 'level': 'DEBUG', -# 'filename': os.path.join(PROJECT_PATH, 'logs/debug.log') -# 'maxBytes': 2048, -# 'backupCount': 5, -# }, -# }) -# -# LOGGING['loggers'].update({ -# 'django.db.backends': { -# 'handlers': ['rotating_file'], -# 'propagate': True, -# 'level': 'DEBUG', -# } -# - +SECRET_KEY = '' # Location of the Phenotype system PHENOTYPE_ENDPOINT = 'https://localhost/phenotype/sample/' @@ -77,4 +51,4 @@ PHENOTYPE_ENDPOINT = 'https://localhost/phenotype/sample/' # Replace with absolute locations of your certificates VARIFY_CERT = '/certs/varify.cert.pem' ROOT_CERTIFICATE = '/certs/ca.cert.pem' -VARIFY_KEY = '/private/varify.key.pem' \ No newline at end of file +VARIFY_KEY = '/private/varify.key.pem' diff --git a/varify/conf/settings.py b/varify/conf/settings.py index 431cbe2b..5c94ebf0 100644 --- a/varify/conf/settings.py +++ b/varify/conf/settings.py @@ -1,25 +1,412 @@ import os -from global_settings import * # noqa -try: - from local_settings import * # noqa -except ImportError: - import warnings - warnings.warn( - 'Local settings have not been found (varify.conf.local_settings)') +# Import global settings to make it easier to extend settings. +from django.conf.global_settings import * # noqa + +# Import the project module to calculate directories relative to the module +# location. +PROJECT_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), + '../..') + +# List all Django apps here. Note that standard Python libraries should not +# be added to this list since Django will not recognize them as apps anyway. +# An app is really only an "app" if a `models` module or package is defined. +# Read more about projects vs. apps here: +# https://docs.djangoproject.com/en/1.3/intro/tutorial01/#creating-models +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.humanize', + 'django.contrib.markup', + 'django.contrib.messages', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.staticfiles', + + 'varify', + + 'varify.raw', + 'varify.raw.sources', + + 'varify.assessments', + 'varify.genome', + 'varify.genes', + 'varify.variants', + 'varify.phenotypes', + 'varify.samples', + 'varify.literature', + + 'varify.support', + + 'varify.pipeline', + + 'sts', + 'haystack', + 'south', + 'registration', + 'siteauth', + 'tracking', + 'bootstrapform', + 'widget_tweaks', + 'django_rq', + 'django_rq_dashboard', + + 'serrano', + 'avocado', + 'avocado.export', + 'modeltree', + 'guardian', + 'reversion', +) + + +# +# ADMINISTRATIVE +# + +# Admins receive any error messages by email if DEBUG is False +ADMINS = () + +# Managers receive broken link emails if SEND_BROKEN_LINK_EMAILS is True +MANAGERS = ADMINS + +# List of IP addresses which will show debug comments +INTERNAL_IPS = ('127.0.0.1', '::1') + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + + +# +# DATABASES +# Each database can be specified here, but passwords should be in a separate +# file that is not versioned. Use ``local_settings.py``. +# + +DATABASES = {} + +# Database routers can useful for delegating database operations transparently +# to different databases depending on what data is being acted on. For Harvest +# instances that make use of an existing database, it is typically never +# desirable to create all the Harvest application tables in this database, but +# rather have a separate database for this purpose. That way the "data" +# database does not need to be changed. + +DATABASE_ROUTERS = {} + + +# +# LOCALITY +# + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = None + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = False + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = False + + +# +# STATIC AND MEDIA +# The application's static files should be placed in the STATIC_ROOT in +# addition to other static files found in third-party apps. The MEDIA_ROOT +# is intended for user uploaded files. +# + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = os.path.join(PROJECT_PATH, '_site/media') + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '/media/' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = os.path.join(PROJECT_PATH, '_site/static') + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# URL prefix for admin static files -- CSS, JavaScript and images. +# Make sure to use a trailing slash. +# Examples: "http://foo.com/static/admin/", "/static/admin/". +ADMIN_MEDIA_PREFIX = '/static/admin/' + +# Additional locations of static files +# Put strings here, like "/home/html/static" or "C:/www/django/static". +# Always use forward slashes, even on Windows. +# Don't forget to use absolute paths, not relative paths. +STATICFILES_DIRS = () + +# +# TEMPLATES +# + +# Project level templates and template directories that override +# third-party app templates. +TEMPLATE_DIRS = () + +# Context processors are simply functions that return a dict which augments the +# template context. +TEMPLATE_CONTEXT_PROCESSORS += ( + 'django.core.context_processors.request', + 'varify.context_processors.static', + 'varify.context_processors.alamut', +) + + +# +# URLS +# # FORCE_SCRIPT_NAME overrides the interpreted 'SCRIPT_NAME' provided by the # web server. since the URLs below are used for various purposes outside of # the WSGI application (static and media files), these need to be updated to -# reflect this alteration -if FORCE_SCRIPT_NAME: - ADMIN_MEDIA_PREFIX = os.path.join( - FORCE_SCRIPT_NAME, ADMIN_MEDIA_PREFIX[1:]) - - STATIC_URL = os.path.join(FORCE_SCRIPT_NAME, STATIC_URL[1:]) - MEDIA_URL = os.path.join(FORCE_SCRIPT_NAME, MEDIA_URL[1:]) - - LOGIN_URL = os.path.join(FORCE_SCRIPT_NAME, LOGIN_URL[1:]) - LOGOUT_URL = os.path.join(FORCE_SCRIPT_NAME, LOGOUT_URL[1:]) - LOGIN_REDIRECT_URL = os.path.join( - FORCE_SCRIPT_NAME, LOGIN_REDIRECT_URL[1:]) +# reflect this discrepancy. +FORCE_SCRIPT_NAME = '' + +ROOT_URLCONF = 'varify.conf.urls' + +LOGIN_URL = '/login/' +LOGOUT_URL = '/logout/' +LOGIN_REDIRECT_URL = '/workspace/' + +ALAMUT_URL = 'http://localhost:10000' + +# For non-publicly accessible applications, the siteauth app can be used to +# restrict access site-wide. +SITEAUTH_ACCESS_ORDER = 'allow/deny' + +SITEAUTH_ALLOW_URLS = ( + r'^$', + r'^log(in|out)/', + r'^password/reset/', + r'^(static|support|register|verify)/', + r'^api/', +) + +# +# MIDDLEWARE +# + +MIDDLEWARE_CLASSES = ( + 'django.middleware.gzip.GZipMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.transaction.TransactionMiddleware', + 'reversion.middleware.RevisionMiddleware', + 'serrano.middleware.SessionMiddleware', + 'siteauth.middleware.SiteAuthenticationMiddleware', +) + + +# +# EMAIL +# + +EMAIL_SUBJECT_PREFIX = '[Varify] ' +NO_REPLY_EMAIL = 'noreply@example.com' +SUPPORT_EMAIL = 'support@example.com' + +# +# LOGGING +# + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} + + +# +# CACHE +# + +# For production environments, the memcached backend is highly recommended +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': '127.0.0.1:11211', + 'KEY_PREFIX': 'varify', + 'VERSION': 1, + }, + 'varify.pipeline': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': '127.0.0.1:11211', + 'KEY_PREFIX': 'varify.pipeline', + 'VERSION': 1, + } +} + +# Default cache seconds for a resource, use the `cache_page` decorator to +# change the amount of time for a given resource. +CACHE_MIDDLEWARE_SECONDS = 60 + +# This is not necessary to set if the above `KEY_PREFIX` value is set since +# the `KEY_PREFIX` namespaces all cache set by this application +CACHE_MIDDLEWARE_KEY_PREFIX = '' + + +# +# AUTHENTICATION +# + +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'guardian.backends.ObjectPermissionBackend', +) + +# django-registration +REGISTRATION_ACTIVATION_DAYS = 0 +REGISTRATION_MODERATION = True + +# +# SESSIONS AND COOKIES +# + +CSRF_COOKIE_NAME = 'varify_csrftoken' + +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' +SESSION_COOKIE_AGE = 60 * 60 # 1 hour +SESSION_COOKIE_NAME = 'varify_sessionid' +SESSION_EXPIRE_AT_BROWSER_CLOSE = True +SESSION_SAVE_EVERY_REQUEST = False + + +# +# OTHER PROJECT SETTINGS +# + +USE_ETAGS = False + +SEND_BROKEN_LINK_EMAILS = False +IGNORABLE_404_URLS = ( + r'robots\.txt$', + r'favicon\.ico$', +) + + +# +# VARIOUS APP SETTINGS +# + +# The primary key of the ``Site`` object for the Sites Framework +SITE_ID = 1 + +TRACK_ANONYMOUS_USERS = False +TRACK_PAGEVIEWS = False +TRACK_IGNORE_URLS = ( + r'^(static|media|admin|tracking)/', +) + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': os.path.join(os.path.dirname(__file__), '../../whoosh.index'), + } +} + +# For django-guardian +ANONYMOUS_USER_ID = -1 + +MODELTREES = { + 'default': { + 'model': 'samples.result', + 'excluded_models': ['auth.user', 'avocado.datacontext'], + 'required_routes': [{ + 'target': 'genes.gene', + 'source': 'genes.transcript', + }, { + 'target': 'samples.cohort', + 'source': 'samples.cohortvariant', + }, { + 'source': 'genes.gene', + 'target': 'genes.genephenotype', + }, { + 'target': 'samples.cohortvariant', + 'source': 'variants.variant', + }, { + 'target': 'samples.resultset', + 'source': 'samples.resultsetitem', + }], + 'excluded_routes': [{ + 'target': 'genes.gene', + 'source': 'literature.pubmed', + 'symmetrical': True, + }], + }, +} + +AVOCADO = { + 'METADATA_MIGRATION_APP': 'varify', +} + +SERRANO = { + 'AUTH_REQUIRED': True, +} + +VARIFY_SAMPLE_DIRS = () + +# +# SOLVEBIO SETTINGS (django_solvebio) +# SolveBio integration is optional (see README.md for more info). + +try: + __import__('solvebio') +except ImportError: + pass +else: + INSTALLED_APPS += ('solvebio.contrib.django_solvebio', ) + + +# Get your API key from https://www.solvebio.com/account +SOLVEBIO_API_KEY = os.environ.get('SOLVEBIO_API_KEY', None) + +# You can optionally bypass DB lookups by hardcoding aliases. +# The dict key is the alias and the value may be an ID or +# full dataset name. [OPTIONAL] +SOLVEBIO_DATASET_ALIASES = { + 'clinvar': 'ClinVar/0.0.2/ClinVar' +} From 20156889b294cabf368de37f2ab7f7e8c4bda04c Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Thu, 24 Apr 2014 15:54:11 -0400 Subject: [PATCH 05/85] Revert "Move contents of global_settings to settings" This reverts commit e425d0d263d1361c3be92e13ccca5c928a7642db. Signed-off-by: Byron Ruth --- README.md | 2 +- varify/conf/global_settings.py | 2 +- varify/conf/local_settings.py.sample | 34 ++- varify/conf/settings.py | 425 ++------------------------- 4 files changed, 51 insertions(+), 412 deletions(-) diff --git a/README.md b/README.md index 6eaa3a29..8910b439 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ pip install solvebio ``` Then, make sure that the `SOLVEBIO_API_KEY` Django setting is set either -via an environment variable (see `settings.py`) or explicitly in your +via an environment variable (see `global_settings.py`) or explicitly in your `local_settings.py`. You can find your API key from your account page on the SolveBio website. diff --git a/varify/conf/global_settings.py b/varify/conf/global_settings.py index 7526bc09..47f73349 100644 --- a/varify/conf/global_settings.py +++ b/varify/conf/global_settings.py @@ -209,12 +209,12 @@ 'django.middleware.gzip.GZipMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', - 'serrano.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.transaction.TransactionMiddleware', 'reversion.middleware.RevisionMiddleware', + 'serrano.middleware.SessionMiddleware', 'siteauth.middleware.SiteAuthenticationMiddleware', ) diff --git a/varify/conf/local_settings.py.sample b/varify/conf/local_settings.py.sample index 90d23d6e..14d4b51f 100644 --- a/varify/conf/local_settings.py.sample +++ b/varify/conf/local_settings.py.sample @@ -1,4 +1,5 @@ -from varify.conf.settings import * +import os +from global_settings import PROJECT_PATH, LOGGING # Uncomment to put the application in non-debug mode. This is useful # for testing error handling and messages. @@ -38,11 +39,36 @@ RQ_QUEUES = { # Non-restricted email port for development, run in a terminal: # python -m smtpd -n -c DebuggingServer localhost:1025 EMAIL_PORT = 1025 -EMAIL_SUBJECT_PREFIX = '[Varify] ' +EMAIL_SUBJECT_PREFIX = '[varify] ' # This is used as a "seed" for various hashing algorithms. This must be set to # a very long random string (40+ characters) -SECRET_KEY = '' +SECRET_KEY = 'g0u!o7_mw=cfhrgb+#xupma&@99443u06xs19$rlmt#wyt9xve' + +# Uncomment for additional logging. If using the 'rotating_file' handler +# you must create the `logs` directory in the project root. +# LOGGING['handlers'].update({ +# 'stdout': { +# 'class': 'logging.StreamHandler', +# 'level': 'DEBUG', +# }, +# 'rotating_file': { +# 'class': 'logging.handlers.RotatingFileHandler', +# 'level': 'DEBUG', +# 'filename': os.path.join(PROJECT_PATH, 'logs/debug.log') +# 'maxBytes': 2048, +# 'backupCount': 5, +# }, +# }) +# +# LOGGING['loggers'].update({ +# 'django.db.backends': { +# 'handlers': ['rotating_file'], +# 'propagate': True, +# 'level': 'DEBUG', +# } +# + # Location of the Phenotype system PHENOTYPE_ENDPOINT = 'https://localhost/phenotype/sample/' @@ -51,4 +77,4 @@ PHENOTYPE_ENDPOINT = 'https://localhost/phenotype/sample/' # Replace with absolute locations of your certificates VARIFY_CERT = '/certs/varify.cert.pem' ROOT_CERTIFICATE = '/certs/ca.cert.pem' -VARIFY_KEY = '/private/varify.key.pem' +VARIFY_KEY = '/private/varify.key.pem' \ No newline at end of file diff --git a/varify/conf/settings.py b/varify/conf/settings.py index 5c94ebf0..431cbe2b 100644 --- a/varify/conf/settings.py +++ b/varify/conf/settings.py @@ -1,412 +1,25 @@ import os +from global_settings import * # noqa -# Import global settings to make it easier to extend settings. -from django.conf.global_settings import * # noqa - -# Import the project module to calculate directories relative to the module -# location. -PROJECT_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), - '../..') - -# List all Django apps here. Note that standard Python libraries should not -# be added to this list since Django will not recognize them as apps anyway. -# An app is really only an "app" if a `models` module or package is defined. -# Read more about projects vs. apps here: -# https://docs.djangoproject.com/en/1.3/intro/tutorial01/#creating-models -INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.humanize', - 'django.contrib.markup', - 'django.contrib.messages', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.staticfiles', - - 'varify', - - 'varify.raw', - 'varify.raw.sources', - - 'varify.assessments', - 'varify.genome', - 'varify.genes', - 'varify.variants', - 'varify.phenotypes', - 'varify.samples', - 'varify.literature', - - 'varify.support', - - 'varify.pipeline', - - 'sts', - 'haystack', - 'south', - 'registration', - 'siteauth', - 'tracking', - 'bootstrapform', - 'widget_tweaks', - 'django_rq', - 'django_rq_dashboard', - - 'serrano', - 'avocado', - 'avocado.export', - 'modeltree', - 'guardian', - 'reversion', -) - - -# -# ADMINISTRATIVE -# - -# Admins receive any error messages by email if DEBUG is False -ADMINS = () - -# Managers receive broken link emails if SEND_BROKEN_LINK_EMAILS is True -MANAGERS = ADMINS - -# List of IP addresses which will show debug comments -INTERNAL_IPS = ('127.0.0.1', '::1') - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - - -# -# DATABASES -# Each database can be specified here, but passwords should be in a separate -# file that is not versioned. Use ``local_settings.py``. -# - -DATABASES = {} - -# Database routers can useful for delegating database operations transparently -# to different databases depending on what data is being acted on. For Harvest -# instances that make use of an existing database, it is typically never -# desirable to create all the Harvest application tables in this database, but -# rather have a separate database for this purpose. That way the "data" -# database does not need to be changed. - -DATABASE_ROUTERS = {} - - -# -# LOCALITY -# - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# On Unix systems, a value of None will cause Django to use the same -# timezone as the operating system. -# If running in a Windows environment this must be set to the same as your -# system time zone. -TIME_ZONE = None - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = False - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale -USE_L10N = False - - -# -# STATIC AND MEDIA -# The application's static files should be placed in the STATIC_ROOT in -# addition to other static files found in third-party apps. The MEDIA_ROOT -# is intended for user uploaded files. -# - -# Absolute filesystem path to the directory that will hold user-uploaded files. -# Example: "/home/media/media.lawrence.com/media/" -MEDIA_ROOT = os.path.join(PROJECT_PATH, '_site/media') - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash. -# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" -MEDIA_URL = '/media/' - -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/home/media/media.lawrence.com/static/" -STATIC_ROOT = os.path.join(PROJECT_PATH, '_site/static') - -# URL prefix for static files. -# Example: "http://media.lawrence.com/static/" -STATIC_URL = '/static/' - -# URL prefix for admin static files -- CSS, JavaScript and images. -# Make sure to use a trailing slash. -# Examples: "http://foo.com/static/admin/", "/static/admin/". -ADMIN_MEDIA_PREFIX = '/static/admin/' - -# Additional locations of static files -# Put strings here, like "/home/html/static" or "C:/www/django/static". -# Always use forward slashes, even on Windows. -# Don't forget to use absolute paths, not relative paths. -STATICFILES_DIRS = () - -# -# TEMPLATES -# - -# Project level templates and template directories that override -# third-party app templates. -TEMPLATE_DIRS = () - -# Context processors are simply functions that return a dict which augments the -# template context. -TEMPLATE_CONTEXT_PROCESSORS += ( - 'django.core.context_processors.request', - 'varify.context_processors.static', - 'varify.context_processors.alamut', -) - - -# -# URLS -# +try: + from local_settings import * # noqa +except ImportError: + import warnings + warnings.warn( + 'Local settings have not been found (varify.conf.local_settings)') # FORCE_SCRIPT_NAME overrides the interpreted 'SCRIPT_NAME' provided by the # web server. since the URLs below are used for various purposes outside of # the WSGI application (static and media files), these need to be updated to -# reflect this discrepancy. -FORCE_SCRIPT_NAME = '' - -ROOT_URLCONF = 'varify.conf.urls' - -LOGIN_URL = '/login/' -LOGOUT_URL = '/logout/' -LOGIN_REDIRECT_URL = '/workspace/' - -ALAMUT_URL = 'http://localhost:10000' - -# For non-publicly accessible applications, the siteauth app can be used to -# restrict access site-wide. -SITEAUTH_ACCESS_ORDER = 'allow/deny' - -SITEAUTH_ALLOW_URLS = ( - r'^$', - r'^log(in|out)/', - r'^password/reset/', - r'^(static|support|register|verify)/', - r'^api/', -) - -# -# MIDDLEWARE -# - -MIDDLEWARE_CLASSES = ( - 'django.middleware.gzip.GZipMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.transaction.TransactionMiddleware', - 'reversion.middleware.RevisionMiddleware', - 'serrano.middleware.SessionMiddleware', - 'siteauth.middleware.SiteAuthenticationMiddleware', -) - - -# -# EMAIL -# - -EMAIL_SUBJECT_PREFIX = '[Varify] ' -NO_REPLY_EMAIL = 'noreply@example.com' -SUPPORT_EMAIL = 'support@example.com' - -# -# LOGGING -# - -# A sample logging configuration. The only tangible logging -# performed by this configuration is to send an email to -# the site admins on every HTTP 500 error. -# See http://docs.djangoproject.com/en/dev/topics/logging for -# more details on how to customize your logging configuration. -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'class': 'django.utils.log.AdminEmailHandler' - } - }, - 'loggers': { - 'django.request': { - 'handlers': ['mail_admins'], - 'level': 'ERROR', - 'propagate': True, - }, - } -} - - -# -# CACHE -# - -# For production environments, the memcached backend is highly recommended -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': '127.0.0.1:11211', - 'KEY_PREFIX': 'varify', - 'VERSION': 1, - }, - 'varify.pipeline': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': '127.0.0.1:11211', - 'KEY_PREFIX': 'varify.pipeline', - 'VERSION': 1, - } -} - -# Default cache seconds for a resource, use the `cache_page` decorator to -# change the amount of time for a given resource. -CACHE_MIDDLEWARE_SECONDS = 60 - -# This is not necessary to set if the above `KEY_PREFIX` value is set since -# the `KEY_PREFIX` namespaces all cache set by this application -CACHE_MIDDLEWARE_KEY_PREFIX = '' - - -# -# AUTHENTICATION -# - -AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'guardian.backends.ObjectPermissionBackend', -) - -# django-registration -REGISTRATION_ACTIVATION_DAYS = 0 -REGISTRATION_MODERATION = True - -# -# SESSIONS AND COOKIES -# - -CSRF_COOKIE_NAME = 'varify_csrftoken' - -SESSION_ENGINE = 'django.contrib.sessions.backends.cache' -SESSION_COOKIE_AGE = 60 * 60 # 1 hour -SESSION_COOKIE_NAME = 'varify_sessionid' -SESSION_EXPIRE_AT_BROWSER_CLOSE = True -SESSION_SAVE_EVERY_REQUEST = False - - -# -# OTHER PROJECT SETTINGS -# - -USE_ETAGS = False - -SEND_BROKEN_LINK_EMAILS = False -IGNORABLE_404_URLS = ( - r'robots\.txt$', - r'favicon\.ico$', -) - - -# -# VARIOUS APP SETTINGS -# - -# The primary key of the ``Site`` object for the Sites Framework -SITE_ID = 1 - -TRACK_ANONYMOUS_USERS = False -TRACK_PAGEVIEWS = False -TRACK_IGNORE_URLS = ( - r'^(static|media|admin|tracking)/', -) - -HAYSTACK_CONNECTIONS = { - 'default': { - 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', - 'PATH': os.path.join(os.path.dirname(__file__), '../../whoosh.index'), - } -} - -# For django-guardian -ANONYMOUS_USER_ID = -1 - -MODELTREES = { - 'default': { - 'model': 'samples.result', - 'excluded_models': ['auth.user', 'avocado.datacontext'], - 'required_routes': [{ - 'target': 'genes.gene', - 'source': 'genes.transcript', - }, { - 'target': 'samples.cohort', - 'source': 'samples.cohortvariant', - }, { - 'source': 'genes.gene', - 'target': 'genes.genephenotype', - }, { - 'target': 'samples.cohortvariant', - 'source': 'variants.variant', - }, { - 'target': 'samples.resultset', - 'source': 'samples.resultsetitem', - }], - 'excluded_routes': [{ - 'target': 'genes.gene', - 'source': 'literature.pubmed', - 'symmetrical': True, - }], - }, -} - -AVOCADO = { - 'METADATA_MIGRATION_APP': 'varify', -} - -SERRANO = { - 'AUTH_REQUIRED': True, -} - -VARIFY_SAMPLE_DIRS = () - -# -# SOLVEBIO SETTINGS (django_solvebio) -# SolveBio integration is optional (see README.md for more info). - -try: - __import__('solvebio') -except ImportError: - pass -else: - INSTALLED_APPS += ('solvebio.contrib.django_solvebio', ) - - -# Get your API key from https://www.solvebio.com/account -SOLVEBIO_API_KEY = os.environ.get('SOLVEBIO_API_KEY', None) - -# You can optionally bypass DB lookups by hardcoding aliases. -# The dict key is the alias and the value may be an ID or -# full dataset name. [OPTIONAL] -SOLVEBIO_DATASET_ALIASES = { - 'clinvar': 'ClinVar/0.0.2/ClinVar' -} +# reflect this alteration +if FORCE_SCRIPT_NAME: + ADMIN_MEDIA_PREFIX = os.path.join( + FORCE_SCRIPT_NAME, ADMIN_MEDIA_PREFIX[1:]) + + STATIC_URL = os.path.join(FORCE_SCRIPT_NAME, STATIC_URL[1:]) + MEDIA_URL = os.path.join(FORCE_SCRIPT_NAME, MEDIA_URL[1:]) + + LOGIN_URL = os.path.join(FORCE_SCRIPT_NAME, LOGIN_URL[1:]) + LOGOUT_URL = os.path.join(FORCE_SCRIPT_NAME, LOGOUT_URL[1:]) + LOGIN_REDIRECT_URL = os.path.join( + FORCE_SCRIPT_NAME, LOGIN_REDIRECT_URL[1:]) From 325aa9c55b94523cd96cfd928ec4025f79033ae6 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 10:19:21 -0400 Subject: [PATCH 06/85] Rename resource templates key_map field to aliases key_map was deprecated in django-preserialize and has been renamed to aliases. Signed-off-by: Byron Ruth --- varify/api/templates.py | 57 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/varify/api/templates.py b/varify/api/templates.py index 222c67fa..ef1d6a18 100644 --- a/varify/api/templates.py +++ b/varify/api/templates.py @@ -5,7 +5,7 @@ # into the parent object. Chromosome = { 'fields': ['chr'], - 'key_map': { + 'aliases': { 'chr': 'label', }, 'merge': True, @@ -62,7 +62,7 @@ GeneTranscript = { 'fields': ['transcript'], - 'key_map': { + 'aliases': { 'transcript': 'refseq_id', }, 'values_list': True, @@ -95,8 +95,9 @@ # Limited gene information to be embedded in a Transcript TranscriptGene = { - 'exclude': ['synonyms', 'transcripts', 'chr', 'families', 'id', 'name'], + 'exclude': ['synonyms', 'transcripts'], 'related': { + 'chr': Chromosome, 'articles': Article, } } @@ -104,7 +105,7 @@ # This version of the resource includes the gene this transcript represents Transcript = { 'fields': ['transcript', 'gene'], - 'key_map': { + 'aliases': { 'transcript': 'refseq_id', }, 'related': { @@ -118,7 +119,7 @@ # suitable. VariantType = { 'fields': ['type'], - 'key_map': { + 'aliases': { 'type': 'label', }, 'merge': True, @@ -138,7 +139,7 @@ # of a Variant ThousandG = { 'fields': ['all_af', 'amr_af', 'afr_af', 'asn_af', 'eur_af'], - 'key_map': { + 'aliases': { 'all_af': 'af', } } @@ -147,7 +148,7 @@ # of a Variant Evs = { 'fields': ['all_af', 'afr_af', 'eur_af', 'read_depth'], - 'key_map': { + 'aliases': { 'eur_af': 'ea_af', 'afr_af': 'aa_af', } @@ -157,7 +158,7 @@ # Lexicon, only use the label EffectRegion = { 'fields': ['region'], - 'key_map': { + 'aliases': { 'region': 'label', }, 'merge': True, @@ -166,7 +167,7 @@ # Lexicon, only use the label EffectImpact = { 'fields': ['impact'], - 'key_map': { + 'aliases': { 'impact': 'label', }, 'merge': True, @@ -174,12 +175,13 @@ # Extended lexicon, who other properties about the effect type Effect = { - 'fields': ['type', 'impact'], - 'key_map': { + 'fields': ['type', 'description', 'impact', 'region'], + 'aliases': { 'type': 'label', }, 'related': { 'impact': EffectImpact, + 'region': EffectRegion, }, 'merge': True, } @@ -188,10 +190,17 @@ # The variant effect has quite a few components. Since it does not include # Variant, it is assumed to be used in the context of a Variant VariantEffect = { - 'fields': ['transcript', 'amino_acid_change', - 'effect', 'hgvs_c', 'hgvs_p', 'segment'], + 'fields': ['transcript', 'codon_change', 'amino_acid_change', + 'functional_class', 'effect', 'hgvs_c', 'hgvs_p', 'segment'], 'related': { 'transcript': Transcript, + 'functional_class': { + 'fields': ['functional_class'], + 'aliases': { + 'functional_class': 'label', + }, + 'merge': True, + }, 'effect': Effect } } @@ -211,7 +220,7 @@ 'related': { 'cohort': { 'fields': ['name', 'size'], - 'key_map': { + 'aliases': { 'size': 'count', }, 'merge': True, @@ -228,7 +237,7 @@ 'phenotypes'], # Map to cleaner names - 'key_map': { + 'aliases': { '1000g': 'thousandg', 'phenotypes': 'variant_phenotypes', }, @@ -253,7 +262,7 @@ Project = { 'fields': ['project'], 'merge': True, - 'key_map': { + 'aliases': { 'project': 'label', }, } @@ -264,7 +273,7 @@ Batch = { 'fields': ['batch'], 'merge': True, - 'key_map': { + 'aliases': { 'batch': 'label', }, } @@ -274,8 +283,9 @@ # project and batch names are merged into the sample object to # remove excessive nesting. Sample = { - 'fields': [':pk', 'label', 'project'], + 'fields': [':pk', 'label', 'count', 'batch', 'project'], 'related': { + 'batch': Batch, 'project': Project, } } @@ -283,7 +293,7 @@ ResultVariant = { 'fields': ['variant_id', 'chr', 'pos', 'ref', 'alt'], - 'key_map': { + 'aliases': { 'variant_id': 'id', }, 'related': { @@ -295,7 +305,7 @@ Genotype = { 'fields': ['genotype', 'genotype_description'], - 'key_map': { + 'aliases': { 'genotype': 'value', 'genotype_description': 'label', }, @@ -318,9 +328,8 @@ 'homopolymer_run', 'notes', 'spanning_deletions', 'strand_bias', 'mq', 'mq0', 'mq_rank_sum', 'phred_scaled_likelihood', 'read_pos_rank_sum', 'in_dbsnp', - 'coverage_ref', 'coverage_alt', 'baseq_rank_sum', - 'genotype_quality', 'haplotype_score', 'quality_by_depth'], - 'key_map': { + 'coverage_ref', 'coverage_alt'], + 'aliases': { 'base_counts': 'base_count_map', 'read_depth_alt': 'coverage_alt', 'read_depth_ref': 'coverage_ref', @@ -338,7 +347,7 @@ SampleResultVariant = deepcopy(SampleResult) SampleResultVariant['related']['variant'] = { 'fields': ['variant_id'], - 'key_map': { + 'aliases': { 'variant_id': 'id', }, 'merge': True, From 63371c199cffabe324845a9d555f5ff01920370e Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 10:34:02 -0400 Subject: [PATCH 07/85] Add version and created fields to sample template Signed-off-by: Byron Ruth --- varify/api/templates.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/varify/api/templates.py b/varify/api/templates.py index ef1d6a18..8647119b 100644 --- a/varify/api/templates.py +++ b/varify/api/templates.py @@ -283,7 +283,8 @@ # project and batch names are merged into the sample object to # remove excessive nesting. Sample = { - 'fields': [':pk', 'label', 'count', 'batch', 'project'], + 'fields': [':pk', 'label', 'count', 'batch', 'project', 'version', + 'created'], 'related': { 'batch': Batch, 'project': Project, From 66563f9544ea82fb36a5299862f46cfac4e1fb1f Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 10:35:43 -0400 Subject: [PATCH 08/85] Add base sample resource classes for authorization The base classes limit the samples to what the requesting user is authorized to view based on the projects they are associated with. In addition, resource links have been updated to include the host. Signed-off-by: Byron Ruth --- varify/samples/resources.py | 108 +++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/varify/samples/resources.py b/varify/samples/resources.py index 8c01413a..e34aa8ce 100644 --- a/varify/samples/resources.py +++ b/varify/samples/resources.py @@ -10,6 +10,7 @@ from django.core.urlresolvers import reverse from django.views.decorators.cache import never_cache from django.conf import settings +from guardian.shortcuts import get_objects_for_user from preserialize.serialize import serialize from restlib2 import resources from serrano.resources.base import ThrottledResource @@ -38,33 +39,44 @@ def sample_posthook(instance, data, request): return data -class SampleResource(ThrottledResource): +class SampleBaseResource(ThrottledResource): model = Sample template = api.templates.Sample + def get_queryset(self, request, **kwargs): + projects = get_objects_for_user(request.user, 'samples.view_project') + return self.model.objects.filter(project__in=projects) + + def get_object(self, request, pk): + if not hasattr(request, 'instance'): + queryset = self.get_queryset(request) + + try: + instance = queryset.select_related('batch', 'project')\ + .get(pk=pk) + except self.model.DoesNotExist: + instance = None + + request.instance = instance + + return request.instance + + +class SampleResource(SampleBaseResource): def is_not_found(self, request, response, pk): - return not self.model.objects.filter(pk=pk).exists() + return not self.get_object(request, pk=pk) @api.cache_resource def get(self, request, pk): - try: - sample = self.model.objects.select_related( - 'batch', 'project').get(pk=pk) - except self.model.DoesNotExist: - raise Http404 - + instance = self.get_object(request, pk=pk) posthook = functools.partial(sample_posthook, request=request) - return serialize(sample, posthook=posthook, **self.template) + return serialize(instance, posthook=posthook, **self.template) -class SamplesResource(ThrottledResource): - model = Sample - - template = api.templates.Sample - +class SamplesResource(SampleBaseResource): def get(self, request): - samples = self.model.objects.all() + samples = self.get_queryset(request) posthook = functools.partial(sample_posthook, request=request) return serialize(samples, posthook=posthook, **self.template) @@ -106,16 +118,20 @@ def get(self, request, project, batch, sample): return data -class SampleResultsResource(ThrottledResource): +class SampleResultBaseResource(ThrottledResource): + def is_not_found(self, request, response, pk): + projects = get_objects_for_user(request.user, 'samples.view_project') + return not Sample.objects.filter(project__in=projects, pk=pk).exists() + + +class SampleResultsResource(SampleResultBaseResource): "Paginated view of results for a sample." model = Result template = api.templates.SampleResult - def is_not_found(self, request, response, pk): - return not Sample.objects.filter(pk=pk).exists() - def get(self, request, pk): + uri = request.build_absolute_uri page = request.GET.get('page', 1) related = ['sample', 'variant', 'variant__chr', 'genotype'] @@ -142,39 +158,49 @@ def get(self, request, pk): obj['_links'] = { 'self': { 'rel': 'self', - 'href': reverse('api:samples:variant', - kwargs={'pk': obj['id']}) + 'href': uri(reverse('api:samples:variant', + kwargs={'pk': obj['id']})) }, 'sample': { 'rel': 'related', - 'href': reverse('api:samples:sample', - kwargs={'pk': obj['sample']['id']}) + 'href': uri(reverse('api:samples:sample', + kwargs={'pk': obj['sample']['id']})) }, 'variant': { 'rel': 'related', - 'href': reverse('api:variants:variant', - kwargs={'pk': obj['variant_id']}), + 'href': uri(reverse('api:variants:variant', + kwargs={'pk': obj['variant_id']})), } } obj.pop('variant_id') - links = {} + links = { + 'self': { + 'rel': 'self', + 'href': uri(reverse('api:samples:variants', + kwargs={'pk': pk})), + } + } + if page.number != 1: links['prev'] = { 'rel': 'prev', - 'href': "{0}?page={1}".format( + 'href': uri('{0}?page={1}'.format( reverse('api:samples:variants', kwargs={'pk': pk}), - str(page.number - 1)) + str(page.number - 1))) } + if page.number < paginator.num_pages - 1: links['next'] = { 'rel': 'next', - 'href': "{0}?page={1}".format( + 'href': uri('{0}?page={1}'.format( reverse('api:samples:variants', kwargs={'pk': pk}), - str(page.number + 1)) + str(page.number + 1))) } + if links: resp['_links'] = links + return resp @@ -187,6 +213,7 @@ def is_not_found(self, request, response, pk): return not self.model.objects.filter(pk=pk).exists() def _cache_data(self, request, pk, key): + uri = request.build_absolute_uri related = ['sample', 'variant', 'genotype', 'score'] try: @@ -199,18 +226,18 @@ def _cache_data(self, request, pk, key): data['_links'] = { 'self': { 'rel': 'self', - 'href': reverse('api:samples:variant', - kwargs={'pk': data['id']}) + 'href': uri(reverse('api:samples:variant', + kwargs={'pk': data['id']})) }, 'sample': { 'rel': 'related', - 'href': reverse('api:samples:sample', - kwargs={'pk': data['sample']['id']}) + 'href': uri(reverse('api:samples:sample', + kwargs={'pk': data['sample']['id']})) }, 'variant': { 'rel': 'related', - 'href': reverse('api:variants:variant', - kwargs={'pk': data['variant_id']}), + 'href': uri(reverse('api:variants:variant', + kwargs={'pk': data['variant_id']})), } } @@ -271,19 +298,19 @@ class PhenotypeResource(ThrottledResource): def get(self, request, sample_id): recalculate = request.GET.get('recalculate_rankings') - if recalculate == "true": + if recalculate == 'true': try: management.call_command('samples', 'gene-ranks', sample_id, force=True) except Exception: - log.exception("Error recalculating gene rankings") - return HttpResponse("Error recalculating gene rankings", + log.exception('Error recalculating gene rankings') + return HttpResponse('Error recalculating gene rankings', status=500) endpoint = getattr(settings, 'PHENOTYPE_ENDPOINT', None) if not endpoint: - log.error("PHENOTYPE_ENDPOINT setting could not be found.") + log.error('PHENOTYPE_ENDPOINT setting could not be found.') return HttpResponse(status=500) endpoint = endpoint.format(sample_id) @@ -342,6 +369,7 @@ def get(self, request, year, month, day, name): return response + sample_resource = never_cache(SampleResource()) samples_resource = never_cache(SamplesResource()) named_sample_resource = never_cache(NamedSampleResource()) From d223a5983659de29ea95a01e7e996a308c875538 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 11:52:26 -0400 Subject: [PATCH 09/85] Implement SampleDialog for selecting a sample The dialog lists samples available to the user in a table which is sortable and can be filtered. Clicking a row will select the sample which then can be saved. Signed-off-by: Byron Ruth --- varify/static/js/src/models/sample.js | 13 +- varify/static/js/src/ui/modals.js | 3 +- varify/static/js/src/ui/modals/sample.js | 312 +++++++++++++++++++++ varify/static/js/src/ui/templates.js | 12 +- varify/static/stylesheets/css/style.css | 2 +- varify/static/stylesheets/scss/style.scss | 63 +++++ varify/static/templates/modals/sample.html | 12 + varify/static/templates/sample/row.html | 3 + varify/static/templates/sample/table.html | 15 + 9 files changed, 429 insertions(+), 6 deletions(-) create mode 100644 varify/static/js/src/ui/modals/sample.js create mode 100644 varify/static/templates/modals/sample.html create mode 100644 varify/static/templates/sample/row.html create mode 100644 varify/static/templates/sample/table.html diff --git a/varify/static/js/src/models/sample.js b/varify/static/js/src/models/sample.js index 9ac60a5f..825a2112 100644 --- a/varify/static/js/src/models/sample.js +++ b/varify/static/js/src/models/sample.js @@ -10,8 +10,19 @@ define([ } }); + + var Samples = Backbone.Collection.extend({ + model: Sample, + + url: function() { + return '/api/samples/'; + } + }); + + return { - Sample: Sample + Sample: Sample, + Samples: Samples }; }); diff --git a/varify/static/js/src/ui/modals.js b/varify/static/js/src/ui/modals.js index 82950328..371d893a 100644 --- a/varify/static/js/src/ui/modals.js +++ b/varify/static/js/src/ui/modals.js @@ -3,7 +3,8 @@ define([ 'underscore', './modals/result', - './modals/phenotype' + './modals/phenotype', + './modals/sample' ], function(_) { var mods = [].slice.call(arguments, 1); diff --git a/varify/static/js/src/ui/modals/sample.js b/varify/static/js/src/ui/modals/sample.js new file mode 100644 index 00000000..5a929aac --- /dev/null +++ b/varify/static/js/src/ui/modals/sample.js @@ -0,0 +1,312 @@ +/* global define */ + +define([ + 'jquery', + 'underscore', + 'marionette', + 'cilantro', + '../../models' +], function($, _, Marionette, c, models) { + + + var SampleRow = Marionette.ItemView.extend({ + tagName: 'tr', + + template: 'varify/sample/row', + + modelEvents: { + 'change:selected': 'renderSelected', + 'change:filtered': 'renderFiltered' + }, + + events: { + click: 'triggerSelected' + }, + + serializeData: function() { + var data = this.model.pick('label', 'project', 'batch'), + loaded = new Date(this.model.get('created')); + + data.loaded = loaded.getFullYear() + '-' + (loaded.getMonth() + 1) + + '-' + loaded.getDate(); + return data; + }, + + renderSelected: function() { + this.$el.toggleClass('selected', !!this.model.get('selected')); + }, + + renderFiltered: function() { + this.$el.toggleClass('filtered', !!this.model.get('filtered')); + }, + + triggerSelected: function(event) { + event.preventDefault(); + event.stopPropagation(); + this.model.trigger('select', this.model); + }, + + onRender: function() { + this.renderSelected(); + this.renderFiltered(); + } + }); + + + var SampleTable = Marionette.CompositeView.extend({ + template: 'varify/sample/table', + + itemView: SampleRow, + + itemViewContainer: 'tbody', + + ui: { + input: 'input', + loader: '[data-target=loader]' + }, + + events: { + 'input @ui.input': '_handleFilter', + 'click thead th': 'handleSort' + }, + + collectionEvents: { + 'sort': '_renderChildren', + 'reset': 'handleFilter hideLoader' + }, + + initialize: function() { + this._handleFilter = _.debounce(this.handleFilter, c.INPUT_DELAY); + }, + + onRender: function() { + var _this = this; + + _.defer(function() { + _this.ui.input.focus(); + }); + }, + + hideLoader: function() { + this.ui.loader.hide(); + }, + + handleFilter: function() { + this.applyFilter(this.ui.input.val()); + }, + + handleSort: function(event) { + if (!this.collection.length) return; + this.applySort($(event.target).data('sort')); + }, + + applyFilter: function(value) { + var regexp = new RegExp(value, 'i'); + + this.collection.each(function(model) { + var filtered = true; + + if (regexp.test(model.get('label'))) { + filtered = false; + } else if (regexp.test(model.get('batch'))) { + filtered = false; + } else if (regexp.test(model.get('project'))) { + filtered = false; + } + + model.set('filtered', filtered); + }); + }, + + applySort: function(attr) { + var dir = 'asc'; + + // Already sorted by the attribute, cycle direction + if (this.collection._sortAttr === attr) { + dir = this.collection._sortDir === 'asc' ? 'desc' : 'asc'; + } + + var p; + + this.$('[data-sort=' + this.collection._sortAttr + ']') + .removeClass(this.collection._sortDir); + this.$('[data-sort=' + attr + ']').addClass(dir); + + // Reference for cycling + this.collection._sortAttr = attr; + this.collection._sortDir = dir; + + // Custom parser + if (attr === 'created') { + p = function(v) { + return (new Date(v)).getTime(); + }; + } + else { + p = function(v) { + return v; + }; + } + + this.collection.comparator = function(m1, m2) { + var v1 = p(m1.get(attr)), v2 = p(m2.get(attr)); + if (v1 < v2) return (dir === 'asc' ? 1 : -1); + if (v1 > v2) return (dir === 'asc' ? -1 : 1); + return 0; + }; + + this.collection.sort(); + } + }); + + + var SampleDialog = Marionette.Layout.extend({ + id: 'sample-dialog', + + className: 'modal hide', + + template: 'varify/modals/sample', + + regions: { + samples: '[data-target=samples-region]' + }, + + ui: { + selectedSample: '.modal-footer [data-target=selected-sample]', + saveButton: '[data-target=save]' + }, + + events: { + 'click @ui.saveButton': 'handleSaveSample' + }, + + regionViews: { + samples: SampleTable + }, + + initialize: function() { + this.data = {}; + + if (!(this.data.context = this.options.context)) { + throw new Error('context model required'); + } + + // Define (get or create) the internal filter for the sample concept + this.data.filter = this.data.context.define({ + concept: c.config.get('varify.sample.concept'), + field: c.config.get('varify.sample.field') + }); + + this.data.samples = new models.Samples(); + + // Flag the currently selected sample if one exists onces the samples + // load + this.listenTo(this.data.samples, 'reset', this.getSelected); + this.listenTo(this.data.samples, 'select', this.setSelected); + + this.data.samples.fetch({reset: true}); + }, + + onRender: function() { + this.$el.modal({ + backdrop: 'static', + keyboard: false, + show: false + }); + + var samples = new this.regionViews.samples({ + collection: this.data.samples + }); + + this.samples.show(samples); + }, + + // Get the currently selected sample from the context and updates the + // state of the collection. + getSelected: function() { + var value = this.data.filter.get('value'), + operator = this.data.filter.get('operator'); + + var model; + + if (value) { + // Unpack in case the IN operator is being used + if (operator === 'in') value = value[0]; + + // Parse { value: ..., label: ... } format + if (typeof value === 'object') value = value.value; + + // Find by id if value is a number, otherwise assume the label + if (typeof value === 'number') { + model = this.data.samples.get(value); + } + else { + model = this.data.samples.findWhere({label: value}); + // Ensure the value is only sample id. This is primarily to + // migrate legacy filters to the new format. + if (model) { + this.data.filter.set('value', model.id, {trigger: false}); + } + } + } + + this.data.samples.trigger('select', model); + }, + + setSelected: function(model) { + this.data.samples.each(function(m) { + m.set('selected', model && m.id === model.id); + }); + + this.renderSelectedSample(); + }, + + renderSelectedSample: function() { + var model = this.data.samples.findWhere({selected: true}); + + var html; + + if (model) { + html = 'Current: ' + model.get('label') + + ' from project ' + + model.get('project') + ' (' + model.get('batch') + ')'; + + } + else { + html = 'Please select a sample.'; + } + + this.ui.selectedSample.html(html); + this.ui.saveButton.prop('disabled', !model); + }, + + handleSaveSample: function(event) { + event.preventDefault(); + + var model = this.data.samples.findWhere({selected: true}); + + this.data.filter.set({ + operator: 'exact', + value: model.id + }); + + if (this.data.filter.hasChanged()) { + this.data.filter.apply(); + } + this.close(); + }, + + open: function() { + this.$el.modal('show'); + }, + + close: function() { + this.$el.modal('hide'); + } + }); + + return { + SampleDialog: SampleDialog + }; + +}); diff --git a/varify/static/js/src/ui/templates.js b/varify/static/js/src/ui/templates.js index e0a10d71..6371e929 100644 --- a/varify/static/js/src/ui/templates.js +++ b/varify/static/js/src/ui/templates.js @@ -5,22 +5,28 @@ define([ 'tpl!../../../templates/tables/header.html', 'tpl!../../../templates/modals/result.html', 'tpl!../../../templates/modals/phenotypes.html', + 'tpl!../../../templates/modals/sample.html', 'tpl!../../../templates/controls/sift.html', 'tpl!../../../templates/controls/polyphen.html', 'tpl!../../../templates/workflows/results.html', 'tpl!../../../templates/export/dialog.html', - 'tpl!../../../templates/sample/loader.html' -], function(c, header, result, phenotype, sift, polyphen, results, - exportDialog, sampleLoader) { + 'tpl!../../../templates/sample/loader.html', + 'tpl!../../../templates/sample/table.html', + 'tpl!../../../templates/sample/row.html' +], function(c, header, result, phenotype, sample, sift, polyphen, results, + exportDialog, sampleLoader, sampleTable, sampleRow) { // Define custom templates c.templates.set('varify/export/dialog', exportDialog); c.templates.set('varify/tables/header', header); c.templates.set('varify/modals/result', result); c.templates.set('varify/modals/phenotype', phenotype); + c.templates.set('varify/modals/sample', sample); c.templates.set('varify/controls/sift', sift); c.templates.set('varify/controls/polyphen', polyphen); c.templates.set('varify/workflows/results', results); c.templates.set('varify/sample/loader', sampleLoader); + c.templates.set('varify/sample/table', sampleTable); + c.templates.set('varify/sample/row', sampleRow); }); diff --git a/varify/static/stylesheets/css/style.css b/varify/static/stylesheets/css/style.css index 86fca6e5..0619f4f7 100644 --- a/varify/static/stylesheets/css/style.css +++ b/varify/static/stylesheets/css/style.css @@ -1 +1 @@ -body,#concept-panel,#context-panel{padding-top:40px}#review-notification{margin:0;margin-top:10px}.unsortable-column{cursor:auto;pointer-events:none}.table-region{margin-top:10px !important}.export-options-modal{width:720px !important;margin-left:-360px !important;max-height:720px;bottom:5%;top:5%}.export-options-modal .tab-content{position:absolute;bottom:65px;top:105px;left:15px;right:15px}.export-options-modal .modal-body{position:static;overflow:scroll}.export-options-modal .modal-footer{position:absolute;left:0;right:0;bottom:0}.result-details-modal{width:90%;top:5%;left:5%;margin:0;max-height:800px;bottom:5%}.result-details-modal .tab-content{position:absolute;width:96.5%;top:60px;bottom:60px}.result-details-modal .modal-footer{position:absolute;left:0;right:0;bottom:0}.result-details-modal .modal-header{border:0px;padding:0 10px 0 0}.result-details-modal .modal-body{position:static}.result-details-modal #knowledge-capture-feedback-container{height:400px;text-align:center}.result-details-modal #knowledge-capture-form{margin:0}.result-details-modal #knowledge-capture-form .radio{padding-left:30px}.result-details-modal #knowledge-capture-form .nested-radio{padding-left:60px}.result-details-modal #knowledge-capture-form .inline{display:inline}.result-details-modal #knowledge-capture-form label.inline{padding-left:15px}.result-details-modal #knowledge-capture-form textarea{width:75%;resize:none;margin:0}.result-details-modal #knowledge-capture-form select,.result-details-modal #knowledge-capture-form h4{margin:5px 0px 5px 0px}.result-details-modal #error-container{margin:0;margin-bottom:10px;padding-top:5px;padding-bottom:5px}.result-details-modal #error-container h5{margin:0}.result-details-modal #audit-trail-button{float:left}.result-details-modal .expandable-details-item{height:100%;position:relative;padding-bottom:20px}.result-details-modal .expand-collapse-container{position:absolute;bottom:0;width:100%;background:white}.result-details-modal .popover{max-height:250px;max-width:200px}.result-details-modal .popover-content>div{max-height:200px;overflow-y:scroll}.phenotype-detail-modal .content-display .horizontal-list-display{display:inline-flex}.variant-container{-webkit-transition-property:margin,display,0.6s,ease-in-out;-moz-transition-property:margin,display,0.6s,ease-in-out;-ms-transition-property:margin,display,0.6s,ease-in-out;-o-transition-property:margin,display,0.6s,ease-in-out;transition-property:margin,display,0.6s,ease-in-out;-webkit-transition-duration:0.15s;-moz-transition-duration:0.15s;-ms-transition-duration:0.15s;-o-transition-duration:0.15s;transition-duration:0.15s;-webkit-transition-timing-function:ease-out;-moz-transition-timing-function:ease-out;-ms-transition-timing-function:ease-out;-o-transition-timing-function:ease-out;transition-timing-function:ease-out;-webkit-transition-delay:0;-moz-transition-delay:0;-ms-transition-delay:0;-o-transition-delay:0;transition-delay:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;font-size:14px;cursor:pointer;height:18px;margin:0;margin-bottom:0;border-top:0;border-bottom:1px solid #eee;background-color:#f9f9f9;text-shadow:0 1px 1px #fff;padding:0;font-weight:bold}.variant-container td{padding-top:5px;padding-bottom:5px;padding-right:10px;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.variant-container td small{font-weight:normal;color:#999}.variant-container td:first-child{padding-left:10px}.variant-container .variant-effects{max-width:250px}.variant-container .hgvs-c{max-width:150px}.variant-container .genotype{max-width:100px}.variant-container .pathogenicity,.variant-container .assessment-category{font-size:12px;white-space:nowrap;overflow:visible}.variant-container .flags-container{text-align:right}.variant-container .flags{font-size:11px;border-right:0;padding:0;min-width:0;max-width:100%}.variant-container .flags>.flag{padding:0 5px}.variant-container .flags>.flag.muted{color:#ddd}.variant-container .flags>.flag:first-child{padding-left:0}.variant-container .flags>.flag:last-child{padding-right:0}.variant-container li small{color:#999}.variant-container .sample-result{border-bottom:1px solid #ccc}.variant-container:first-child{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;-ms-border-radius:3px 3px 0 0;-o-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-top:1px solid #bbb}.variant-container:last-child{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;-ms-border-radius:0 0 3px 3px;-o-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}#incidental-category-radios{border-right:1px solid #a9a9a9}#incidental-findings-label{padding-top:35px}#variant-details-content{margin-right:30px}#variant-details-content .assessments-table-container{border-top:2px solid #eee}.assessment-details-table{border:none;margin-bottom:30px}.assessment-details-table .no-border{border:none}.assessment-details-table th{text-align:left}.assessment-details-table th,.assessment-details-table td{padding-right:20px}.assessment-details-table tbody>tr{border-top:1px solid #eee} +body,#concept-panel,#context-panel{padding-top:40px}#review-notification{margin:0;margin-top:10px}.unsortable-column{cursor:auto;pointer-events:none}.table-region{margin-top:10px !important}.export-options-modal{width:720px !important;margin-left:-360px !important;max-height:720px;bottom:5%;top:5%}.export-options-modal .tab-content{position:absolute;bottom:65px;top:105px;left:15px;right:15px}.export-options-modal .modal-body{position:static;overflow:scroll}.export-options-modal .modal-footer{position:absolute;left:0;right:0;bottom:0}.result-details-modal{width:90%;top:5%;left:5%;margin:0;max-height:800px;bottom:5%}.result-details-modal .tab-content{position:absolute;width:96.5%;top:60px;bottom:60px}.result-details-modal .modal-footer{position:absolute;left:0;right:0;bottom:0}.result-details-modal .modal-header{border:0px;padding:0 10px 0 0}.result-details-modal .modal-body{position:static}.result-details-modal #knowledge-capture-feedback-container{height:400px;text-align:center}.result-details-modal #knowledge-capture-form{margin:0}.result-details-modal #knowledge-capture-form .radio{padding-left:30px}.result-details-modal #knowledge-capture-form .nested-radio{padding-left:60px}.result-details-modal #knowledge-capture-form .inline{display:inline}.result-details-modal #knowledge-capture-form label.inline{padding-left:15px}.result-details-modal #knowledge-capture-form textarea{width:75%;resize:none;margin:0}.result-details-modal #knowledge-capture-form select,.result-details-modal #knowledge-capture-form h4{margin:5px 0px 5px 0px}.result-details-modal #error-container{margin:0;margin-bottom:10px;padding-top:5px;padding-bottom:5px}.result-details-modal #error-container h5{margin:0}.result-details-modal #audit-trail-button{float:left}.result-details-modal .expandable-details-item{height:100%;position:relative;padding-bottom:20px}.result-details-modal .expand-collapse-container{position:absolute;bottom:0;width:100%;background:white}.result-details-modal .popover{max-height:250px;max-width:200px}.result-details-modal .popover-content>div{max-height:200px;overflow-y:scroll}.variant-container{-webkit-transition-property:margin,display,0.6s,ease-in-out;-moz-transition-property:margin,display,0.6s,ease-in-out;-ms-transition-property:margin,display,0.6s,ease-in-out;-o-transition-property:margin,display,0.6s,ease-in-out;transition-property:margin,display,0.6s,ease-in-out;-webkit-transition-duration:0.15s;-moz-transition-duration:0.15s;-ms-transition-duration:0.15s;-o-transition-duration:0.15s;transition-duration:0.15s;-webkit-transition-timing-function:ease-out;-moz-transition-timing-function:ease-out;-ms-transition-timing-function:ease-out;-o-transition-timing-function:ease-out;transition-timing-function:ease-out;-webkit-transition-delay:0;-moz-transition-delay:0;-ms-transition-delay:0;-o-transition-delay:0;transition-delay:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;-o-border-radius:0;border-radius:0;font-size:14px;cursor:pointer;height:18px;margin:0;margin-bottom:0;border-top:0;border-bottom:1px solid #eee;background-color:#f9f9f9;text-shadow:0 1px 1px #fff;padding:0;font-weight:bold}.variant-container td{padding-top:5px;padding-bottom:5px;padding-right:10px;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;text-overflow:ellipsis}.variant-container td small{font-weight:normal;color:#999}.variant-container td:first-child{padding-left:10px}.variant-container .variant-effects{max-width:250px}.variant-container .hgvs-c{max-width:150px}.variant-container .genotype{max-width:100px}.variant-container .pathogenicity,.variant-container .assessment-category{font-size:12px;white-space:nowrap;overflow:visible}.variant-container .flags-container{text-align:right}.variant-container .flags{font-size:11px;border-right:0;padding:0;min-width:0;max-width:100%}.variant-container .flags>.flag{padding:0 5px}.variant-container .flags>.flag.muted{color:#ddd}.variant-container .flags>.flag:first-child{padding-left:0}.variant-container .flags>.flag:last-child{padding-right:0}.variant-container li small{color:#999}.variant-container .sample-result{border-bottom:1px solid #ccc}.variant-container:first-child{-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;-ms-border-radius:3px 3px 0 0;-o-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-top:1px solid #bbb}.variant-container:last-child{-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;-ms-border-radius:0 0 3px 3px;-o-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px}#incidental-category-radios{border-right:1px solid #a9a9a9}#incidental-findings-label{padding-top:35px}#variant-details-content{margin-right:30px}#variant-details-content .assessments-table-container{border-top:2px solid #eee}.assessment-details-table{border:none;margin-bottom:30px}.assessment-details-table .no-border{border:none}.assessment-details-table th{text-align:left}.assessment-details-table th,.assessment-details-table td{padding-right:20px}.assessment-details-table tbody>tr{border-top:1px solid #eee}#sample-dialog .modal-footer{text-align:left}#sample-dialog .modal-footer [data-target=selected-sample]{margin-top:5px;font-size:14px}#sample-dialog .modal-footer button{float:right}#sample-dialog table.table thead th{cursor:pointer;position:relative;width:200px}#sample-dialog table.table thead th:hover{background-color:#eee}#sample-dialog table.table thead th .icon-sort-up,#sample-dialog table.table thead th .icon-sort-down{position:absolute;right:8px;top:12px}#sample-dialog table.table thead th.desc .icon-sort-up{display:none}#sample-dialog table.table thead th.asc .icon-sort-down{display:none}#sample-dialog table.table thead th:first-child{border-left:1px solid #ddd}#sample-dialog table.table tbody tr{cursor:pointer}#sample-dialog table.table tbody tr.filtered{display:none}#sample-dialog table.table tbody tr.selected td{background-color:lightYellow;font-weight:bold} diff --git a/varify/static/stylesheets/scss/style.scss b/varify/static/stylesheets/scss/style.scss index fc9e4bac..e9cf2aa3 100644 --- a/varify/static/stylesheets/scss/style.scss +++ b/varify/static/stylesheets/scss/style.scss @@ -291,3 +291,66 @@ body, #concept-panel, #context-panel { border-top: 1px solid #eee; } } + +#sample-dialog { + .modal-footer { + text-align: left; + + [data-target=selected-sample] { + margin-top: 5px; + font-size: 14px; + } + + button { + float: right; + } + } + + table.table { + + thead th { + cursor: pointer; + position: relative; + width: 200px; + + &:hover { + background-color: #eee; + } + + .icon-sort-up, .icon-sort-down { + position: absolute; + right: 8px; + top: 12px; + } + + &.desc { + .icon-sort-up { + display: none; + } + } + + &.asc { + .icon-sort-down { + display: none; + } + } + + &:first-child { + border-left: 1px solid #ddd; + } + } + + tbody tr { + cursor: pointer; + + &.filtered { + display: none; + } + + &.selected td { + background-color: lightYellow; + font-weight: bold; + } + } + } +} diff --git a/varify/static/templates/modals/sample.html b/varify/static/templates/modals/sample.html new file mode 100644 index 00000000..481e9997 --- /dev/null +++ b/varify/static/templates/modals/sample.html @@ -0,0 +1,12 @@ + + + + + diff --git a/varify/static/templates/sample/row.html b/varify/static/templates/sample/row.html new file mode 100644 index 00000000..90f94c96 --- /dev/null +++ b/varify/static/templates/sample/row.html @@ -0,0 +1,3 @@ +<%= data.label %> +<%= data.project %> (<%= data.batch %>) +<%= data.loaded %> diff --git a/varify/static/templates/sample/table.html b/varify/static/templates/sample/table.html new file mode 100644 index 00000000..82c2b14d --- /dev/null +++ b/varify/static/templates/sample/table.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + +
Sample Project (Batch) Loaded
+ +
Loading samples...
From ed124bf0ef214253b0d506dd481f24b21aecab34 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 11:55:44 -0400 Subject: [PATCH 10/85] Add config constants for sample concept and field ids Signed-off-by: Byron Ruth --- varify/static/js/src/main.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/varify/static/js/src/main.js b/varify/static/js/src/main.js index 2aeedc8a..1357141f 100644 --- a/varify/static/js/src/main.js +++ b/varify/static/js/src/main.js @@ -22,6 +22,12 @@ require({ 'project/ui', 'project/csrf' ], function(c, ui, csrf) { + var SAMPLE_CONCEPT_ID = 2, + SAMPLE_FIELD_ID = 111; + + // Add namespaced variables for reference in other modules + c.config.set('varify.sample.concept', SAMPLE_CONCEPT_ID); + c.config.set('varify.sample.field', SAMPLE_FIELD_ID); // Session options var options = { @@ -32,7 +38,7 @@ require({ var augmentFixedView = function() { var newView = { view: { - columns: [2] + columns: [SAMPLE_CONCEPT_ID] } }; @@ -114,7 +120,7 @@ require({ // Mark the Sample concept as required and display a notification to the // user when it is not populated. - c.config.set('filters.required', [{concept: 2}]); + c.config.set('filters.required', [{concept: SAMPLE_CONCEPT_ID}]); c.on(c.CONTEXT_INVALID, notifyRequired); c.on(c.CONTEXT_REQUIRED, notifyRequired); From b9f06da8c7a64c3959feecdd33bb6ddf15c90ebd Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 12:54:25 -0400 Subject: [PATCH 11/85] Integrate sample dialog in main module The sample dialog is initialized and available at `cilantro.dialogs.sample`. The notifyRequired handler has been replaced to open the sample dialog. Signed-off-by: Byron Ruth --- varify/static/js/src/main.js | 48 +++++++++++++----------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/varify/static/js/src/main.js b/varify/static/js/src/main.js index 1357141f..35a30a9e 100644 --- a/varify/static/js/src/main.js +++ b/varify/static/js/src/main.js @@ -18,10 +18,13 @@ require({ } } }, [ + 'jquery', + 'underscore', 'cilantro', 'project/ui', 'project/csrf' -], function(c, ui, csrf) { +], function($, _, c, ui) { + var SAMPLE_CONCEPT_ID = 2, SAMPLE_FIELD_ID = 111; @@ -90,39 +93,18 @@ require({ control: 'nullSelector' }]); - // A simple handler for CONTEXT_REQUIRED and CONTEXT_INVALID events that - // tells the user which concept is required(when possible) or prints a - // generic message in the case the concept name could not be found. - var notifyRequired = (function(_this) { - return function(concepts) { - if (c.data == null) return; - - var names = _.map(concepts || [], function(concept) { - if ((currConcept = c.data.concepts.get(concept.concept)) != null) { - return currConcept.get('name'); - } - }); - - var message; - if (names) { - message = 'The following concepts are required: ' + names.join(', '); - } - else { - message = 'There are 1 or more required concepts'; - } - - return c.notify({ - level: 'error', - message: message - }); - }; - })(this); + // If no sample is selected, open the dialog to select one + var showSampleDialog = function(required) { + if (_.findWhere(required, {concept: SAMPLE_CONCEPT_ID})) { + c.dialogs.sample.open(); + } + }; // Mark the Sample concept as required and display a notification to the // user when it is not populated. c.config.set('filters.required', [{concept: SAMPLE_CONCEPT_ID}]); - c.on(c.CONTEXT_INVALID, notifyRequired); - c.on(c.CONTEXT_REQUIRED, notifyRequired); + c.on(c.CONTEXT_INVALID, showSampleDialog); + c.on(c.CONTEXT_REQUIRED, showSampleDialog); // Open the default session when Cilantro is ready c.ready(function() { @@ -168,10 +150,14 @@ require({ deleteQuery: new c.ui.DeleteQueryDialog(), - resultDetails: new ui.ResultDetails, + resultDetails: new ui.ResultDetails(), phenotype: new ui.Phenotype({ context: this.data.contexts.session + }), + + sample: new ui.SampleDialog({ + context: this.data.contexts.session }) }; From c2839ff6c258985200852d51fa998035de3b02a8 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 13:18:48 -0400 Subject: [PATCH 12/85] Validate context on sync, open dialog on concept focus Signed-off-by: Byron Ruth --- varify/static/js/src/main.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/varify/static/js/src/main.js b/varify/static/js/src/main.js index 35a30a9e..8ca85ea9 100644 --- a/varify/static/js/src/main.js +++ b/varify/static/js/src/main.js @@ -119,6 +119,11 @@ require({ // the user's view. c.config.set('session.defaults.data.preview', augmentFixedView); + // Ensure the session context is valid + this.data.contexts.once('sync', function() { + this.session.validate(); + }); + // Panels are defined in their own namespace since they shared // across workflows c.panels = { From 6347b23583ba92b7a677a9092b1c8295db575bf6 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 13:19:19 -0400 Subject: [PATCH 13/85] Ensure c.session is defined for view fix Signed-off-by: Byron Ruth --- varify/static/js/src/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/varify/static/js/src/main.js b/varify/static/js/src/main.js index 8ca85ea9..0cd95861 100644 --- a/varify/static/js/src/main.js +++ b/varify/static/js/src/main.js @@ -46,8 +46,8 @@ require({ }; var json; - if ((json = c.session.data.views.session.get('json')) != null) { - newView['view']['ordering'] = json['ordering']; + if (c.session && (json = c.session.data.views.session.get('json')) != null) { + newView.view.ordering = json.ordering; } return newView; From 27d6d44f04a8a6fc5d174313da83198c3e54ae60 Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 13:20:36 -0400 Subject: [PATCH 14/85] Add data migration to mark sample concept as not queryable This has the effect in Cilantro that it will not be displayed in the list of available query concepts. The sample dialog is the new method for selecting a sample. Signed-off-by: Byron Ruth --- .../0004_remove_sample_as_queryable.py | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 varify/migrations/0004_remove_sample_as_queryable.py diff --git a/varify/migrations/0004_remove_sample_as_queryable.py b/varify/migrations/0004_remove_sample_as_queryable.py new file mode 100644 index 00000000..f1ff825c --- /dev/null +++ b/varify/migrations/0004_remove_sample_as_queryable.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + orms['DataConcept'].objects\ + .filter(app_name='samples', model_name='sample', field_name='id')\ + .update(queryable=False) + + def backwards(self, orm): + "Write your backwards methods here." + orms['DataConcept'].objects\ + .filter(app_name='samples', model_name='sample', field_name='id')\ + .update(queryable=True) + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'avocado.datacategory': { + 'Meta': {'ordering': "('parent__order', 'parent__name', 'order', 'name')", 'object_name': 'DataCategory'}, + 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.FloatField', [], {'null': 'True', 'db_column': "'_order'", 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['avocado.DataCategory']"}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'avocado.dataconcept': { + 'Meta': {'ordering': "('category__order', 'category__name', 'order', 'name')", 'object_name': 'DataConcept'}, + 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['avocado.DataCategory']", 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'fields': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'concepts'", 'symmetrical': 'False', 'through': "orm['avocado.DataConceptField']", 'to': "orm['avocado.DataField']"}), + 'formatter_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'concepts+'", 'null': 'True', 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ident': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'internal': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'name_plural': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.FloatField', [], {'null': 'True', 'db_column': "'_order'", 'blank': 'True'}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'queryable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'concepts+'", 'blank': 'True', 'to': "orm['sites.Site']"}), + 'sortable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'viewable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'avocado.dataconceptfield': { + 'Meta': {'ordering': "('order', 'name')", 'object_name': 'DataConceptField'}, + 'concept': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'concept_fields'", 'to': "orm['avocado.DataConcept']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'field': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'concept_fields'", 'to': "orm['avocado.DataField']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'name_plural': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.FloatField', [], {'null': 'True', 'db_column': "'_order'", 'blank': 'True'}) + }, + 'avocado.datacontext': { + 'Meta': {'object_name': 'DataContext'}, + 'accessed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 4, 29, 0, 0)'}), + 'count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_column': "'_count'"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'forks'", 'null': 'True', 'to': "orm['avocado.DataContext']"}), + 'session': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'tree': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'datacontext+'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'avocado.datafield': { + 'Meta': {'ordering': "('category__order', 'category__name', 'order', 'name')", 'unique_together': "(('app_name', 'model_name', 'field_name'),)", 'object_name': 'DataField'}, + 'app_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['avocado.DataCategory']", 'null': 'True', 'blank': 'True'}), + 'code_field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'data_version': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'enumerable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'fields+'", 'null': 'True', 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'label_field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'model_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'name_plural': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.FloatField', [], {'null': 'True', 'db_column': "'_order'", 'blank': 'True'}), + 'order_field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'search_field_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'fields+'", 'blank': 'True', 'to': "orm['sites.Site']"}), + 'translator': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'unit': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'unit_plural': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}) + }, + 'avocado.dataquery': { + 'Meta': {'object_name': 'DataQuery'}, + 'accessed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'context_json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'distinct_count': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'forks'", 'null': 'True', 'to': "orm['avocado.DataQuery']"}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'record_count': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'session': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'shared_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'shareddataquery+'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'tree': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'dataquery+'", 'null': 'True', 'to': "orm['auth.User']"}), + 'view_json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}) + }, + 'avocado.dataview': { + 'Meta': {'object_name': 'DataView'}, + 'accessed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 4, 29, 0, 0)'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}), + 'keywords': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'forks'", 'null': 'True', 'to': "orm['avocado.DataView']"}), + 'session': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'template': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'dataview+'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'avocado.revision': { + 'Meta': {'ordering': "('-timestamp',)", 'object_name': 'Revision'}, + 'changes': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'data': ('jsonfield.fields.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+revision'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['avocado', 'varify'] + symmetrical = True From c678f8ccf8e4384cf2ad62834415fb9d2da35e0f Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 14:39:12 -0400 Subject: [PATCH 15/85] Change 500 responses to use resource render method Signed-off-by: Byron Ruth --- varify/samples/resources.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/varify/samples/resources.py b/varify/samples/resources.py index e34aa8ce..d2827fec 100644 --- a/varify/samples/resources.py +++ b/varify/samples/resources.py @@ -13,6 +13,7 @@ from guardian.shortcuts import get_objects_for_user from preserialize.serialize import serialize from restlib2 import resources +from restlib2.http import codes from serrano.resources.base import ThrottledResource from varify.variants.resources import VariantResource from varify import api @@ -304,14 +305,15 @@ def get(self, request, sample_id): force=True) except Exception: log.exception('Error recalculating gene rankings') - return HttpResponse('Error recalculating gene rankings', - status=500) + return self.render(request, { + 'message': 'Error recalculating gene rankings', + }, status=codes.server_error) endpoint = getattr(settings, 'PHENOTYPE_ENDPOINT', None) if not endpoint: log.error('PHENOTYPE_ENDPOINT setting could not be found.') - return HttpResponse(status=500) + return self.render(request, '', status=codes.server_error) endpoint = endpoint.format(sample_id) @@ -321,7 +323,7 @@ def get(self, request, sample_id): except requests.exceptions.SSLError: raise PermissionDenied except requests.exceptions.ConnectionError: - return HttpResponse(status=500) + return self.render(request, '', status=codes.server_error) except requests.exceptions.RequestException: raise Http404 @@ -345,7 +347,7 @@ def get(self, request, year, month, day, name): if not endpoint: log.error('PEDIGREE_ENDPOINT setting could not be found.') - return HttpResponse(status=500) + return self.render(request, '', status=codes.server_error) endpoint = endpoint.format(year, month, day, name) @@ -356,7 +358,7 @@ def get(self, request, year, month, day, name): except requests.exceptions.SSLError: raise PermissionDenied except requests.exceptions.ConnectionError: - return HttpResponse(status=500) + return self.render(request, '', status=codes.server_error) except requests.exceptions.RequestException: raise Http404 From 959d5c4abb6831da9a88dc49a196eb6f08a586ca Mon Sep 17 00:00:00 2001 From: Byron Ruth Date: Tue, 29 Apr 2014 14:40:38 -0400 Subject: [PATCH 16/85] Add description to comparator parse function Remove unnecessary whitespace Signed-off-by: Byron Ruth --- varify/static/js/src/ui/modals/sample.js | 12 ++++++------ varify/static/stylesheets/scss/style.scss | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/varify/static/js/src/ui/modals/sample.js b/varify/static/js/src/ui/modals/sample.js index 5a929aac..a5c85dfb 100644 --- a/varify/static/js/src/ui/modals/sample.js +++ b/varify/static/js/src/ui/modals/sample.js @@ -126,8 +126,6 @@ define([ dir = this.collection._sortDir === 'asc' ? 'desc' : 'asc'; } - var p; - this.$('[data-sort=' + this.collection._sortAttr + ']') .removeClass(this.collection._sortDir); this.$('[data-sort=' + attr + ']').addClass(dir); @@ -136,20 +134,22 @@ define([ this.collection._sortAttr = attr; this.collection._sortDir = dir; - // Custom parser + // Parse function for handling the different sort attributes. + var parse; + if (attr === 'created') { - p = function(v) { + parse = function(v) { return (new Date(v)).getTime(); }; } else { - p = function(v) { + parse = function(v) { return v; }; } this.collection.comparator = function(m1, m2) { - var v1 = p(m1.get(attr)), v2 = p(m2.get(attr)); + var v1 = parse(m1.get(attr)), v2 = parse(m2.get(attr)); if (v1 < v2) return (dir === 'asc' ? 1 : -1); if (v1 > v2) return (dir === 'asc' ? -1 : 1); return 0; diff --git a/varify/static/stylesheets/scss/style.scss b/varify/static/stylesheets/scss/style.scss index e9cf2aa3..ed5a4d0b 100644 --- a/varify/static/stylesheets/scss/style.scss +++ b/varify/static/stylesheets/scss/style.scss @@ -307,7 +307,6 @@ body, #concept-panel, #context-panel { } table.table { - thead th { cursor: pointer; position: relative; From 6a9b616e3ae63e3a71e7f60d5f4a62945268f231 Mon Sep 17 00:00:00 2001 From: Don Naegely Date: Tue, 29 Apr 2014 15:00:14 -0400 Subject: [PATCH 17/85] Fix sample concept lookup in queryable migration The previous lookup was using DataField fields. The migration now uses the name field of the DataConcept to find the sample concept(s) Signed-off-by: Don Naegely --- varify/migrations/0004_remove_sample_as_queryable.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/varify/migrations/0004_remove_sample_as_queryable.py b/varify/migrations/0004_remove_sample_as_queryable.py index f1ff825c..8320a227 100644 --- a/varify/migrations/0004_remove_sample_as_queryable.py +++ b/varify/migrations/0004_remove_sample_as_queryable.py @@ -8,14 +8,12 @@ class Migration(DataMigration): def forwards(self, orm): "Write your forwards methods here." - orms['DataConcept'].objects\ - .filter(app_name='samples', model_name='sample', field_name='id')\ + orms['DataConcept'].objects.filter(name='Sample')\ .update(queryable=False) def backwards(self, orm): "Write your backwards methods here." - orms['DataConcept'].objects\ - .filter(app_name='samples', model_name='sample', field_name='id')\ + orms['DataConcept'].objects.filter(name='Sample')\ .update(queryable=True) models = { From af8813258a2c7980b5bd92e92a55cb43ebc985b0 Mon Sep 17 00:00:00 2001 From: Don Naegely Date: Mon, 28 Apr 2014 12:14:15 -0400 Subject: [PATCH 18/85] Refactor summary and effects details sections Signed-off-by: Don Naegely --- varify/static/js/src/ui/modals/result.js | 511 ++---------------- varify/static/js/src/ui/tables/row.js | 28 +- varify/static/js/src/ui/tables/row/genes.js | 29 + varify/static/js/src/ui/templates.js | 56 +- varify/static/js/src/ui/variant.js | 18 + varify/static/js/src/ui/variant/articles.js | 0 varify/static/js/src/ui/variant/cohorts.js | 0 varify/static/js/src/ui/variant/effects.js | 138 +++++ .../static/js/src/ui/variant/frequencies.js | 0 varify/static/js/src/ui/variant/phenotypes.js | 0 varify/static/js/src/ui/variant/scores.js | 0 varify/static/js/src/ui/variant/summary.js | 92 ++++ varify/static/js/src/utils.js | 18 + varify/static/templates/modals/result.html | 147 +---- .../templates/modals/result/assessment.html | 127 +++++ varify/static/templates/tables/row.html | 10 + varify/static/templates/variant/articles.html | 0 varify/static/templates/variant/cohorts.html | 0 .../static/templates/variant/effect-item.html | 21 + .../static/templates/variant/effect-type.html | 3 + varify/static/templates/variant/effects.html | 3 + .../static/templates/variant/frequencies.html | 0 .../static/templates/variant/phenotypes.html | 0 varify/static/templates/variant/scores.html | 0 varify/static/templates/variant/summary.html | 51 ++ 25 files changed, 632 insertions(+), 620 deletions(-) create mode 100644 varify/static/js/src/ui/tables/row/genes.js create mode 100644 varify/static/js/src/ui/variant.js create mode 100644 varify/static/js/src/ui/variant/articles.js create mode 100644 varify/static/js/src/ui/variant/cohorts.js create mode 100644 varify/static/js/src/ui/variant/effects.js create mode 100644 varify/static/js/src/ui/variant/frequencies.js create mode 100644 varify/static/js/src/ui/variant/phenotypes.js create mode 100644 varify/static/js/src/ui/variant/scores.js create mode 100644 varify/static/js/src/ui/variant/summary.js create mode 100644 varify/static/templates/modals/result/assessment.html create mode 100644 varify/static/templates/tables/row.html create mode 100644 varify/static/templates/variant/articles.html create mode 100644 varify/static/templates/variant/cohorts.html create mode 100644 varify/static/templates/variant/effect-item.html create mode 100644 varify/static/templates/variant/effect-type.html create mode 100644 varify/static/templates/variant/effects.html create mode 100644 varify/static/templates/variant/frequencies.html create mode 100644 varify/static/templates/variant/phenotypes.html create mode 100644 varify/static/templates/variant/scores.html create mode 100644 varify/static/templates/variant/summary.html diff --git a/varify/static/js/src/ui/modals/result.js b/varify/static/js/src/ui/modals/result.js index f49a7b4c..8503632c 100644 --- a/varify/static/js/src/ui/modals/result.js +++ b/varify/static/js/src/ui/modals/result.js @@ -3,74 +3,13 @@ define([ 'jquery', 'underscore', + 'backbone', 'marionette', - 'cilantro', - '../../models', '../../utils', - '../../templates', -], function($, _, Marionette, c, models, utils, Templates) { - - var DetailsTab = Marionette.ItemView.extend({ - template: function() {}, - - initialize: function() { - _.bindAll(this, 'fetchMetricsError', 'fetchMetricsSuccess', - 'hidePopover'); - - - this.metrics = this.options.metrics; - - this.$content = $('
'); - this.$el.append(this.$content); - this.$el.attr('id', 'variant-details-content'); - - c.on('resultModal:detailsTabClosed', this.hidePopover); - c.on('resultModal:closed', this.hidePopover); - }, - - events: { - 'click': 'hidePopover', - 'click .assessment-details-table .icon-plus': 'expandAssessmentRow', - 'click .assessment-details-table .icon-minus': 'collapseAssessmentRow' - }, - - hidePopover: function(event) { - if (event && event.target) { - $('.cohort-sample-popover').not(event.target).popover('hide'); - } - else { - $('.cohort-sample-popover').popover('hide'); - } - }, - - expandAssessmentRow: function(event) { - // Figure out which row we clicked on. - var row = $(event.target).closest('tr'); - - // Lookup the details row. - var detailsRow = $('#' + row.attr('id') + '-details'); - - // Hide the expand(+) control, show the collapse(-) control, and - // show the details row. - detailsRow.show(); - $(event.target).addClass('hide'); - row.find('.icon-minus').removeClass('hide'); - }, - - collapseAssessmentRow: function(event) { - // Figure out which row we clicked on. - var row = $(event.target).closest('tr'); - - // Lookup the details row. - var detailsRow = $('#' + row.attr('id') + '-details'); - - // Show the expand(+) control, hide the collapse(-) control, and - // hide the details row. - detailsRow.hide(); - $(event.target).addClass('hide'); - row.find('.icon-plus').removeClass('hide'); - }, + '../variant' +], function($, _, Backbone, Marionette, utils, variant) { + /* renderCohorts: function(attrs) { var content = []; content.push('

Cohorts

'); @@ -134,80 +73,6 @@ define([ return content.join(''); }, - renderSummary: function(resultAttrs, variantAttrs) { - var bases, content, hgmdLinks, key, labelClass; - - content = []; - content.push('

' + resultAttrs.sample.label + ' in ' + - resultAttrs.sample.project + '

'); - content.push('
    '); - content.push('
  • Variant Result ID ' + - resultAttrs.id + '
  • '); - - labelClass = utils.depthClass(resultAttrs.read_depth); - content.push('
  • Coverage ' + resultAttrs.read_depth + - 'x (' + - resultAttrs.read_depth_ref + '/' + - resultAttrs.read_depth_alt + ')
  • '); - content.push('
  • Raw Coverage '); - - if (resultAttrs.raw_read_depth != null) { - content.push('' + resultAttrs.raw_read_depth + 'x'); - } - else { - content.push('n/a'); - } - - content.push('
  • '); - - labelClass = utils.qualityClass(resultAttrs.quality); - content.push('
  • Quality ' + resultAttrs.quality + '
  • '); - content.push('
  • Genotype ' + - resultAttrs.genotype_value + ' (' + - resultAttrs.genotype_description + ')
  • '); - content.push('
  • Base Counts '); - - if (resultAttrs.base_counts) { - bases = []; - - for (key in resultAttrs.base_counts) { - bases.push('' + key + '=' + resultAttrs.base_counts[key]); - } - - content.push(bases.sort().join(', ')); - } - else { - content.push('n/a'); - } - - content.push('
  • '); - content.push('
  • Position ' + - (Templates.genomicPosition(variantAttrs.chr, variantAttrs.pos)) + - '
  • '); - content.push('
  • Genes ' + - (Templates.geneLinks(variantAttrs.uniqueGenes)) + '
  • '); - - hgmdLinks = Templates.hgmdLinks(variantAttrs.phenotypes); - if (hgmdLinks) { - content.push('
  • HGMD IDs ' + hgmdLinks + '
  • '); - } - - if (variantAttrs.rsid) { - content.push('
  • dbSNP ' + - (Templates.dbSNPLink(variantAttrs.rsid)) + '
  • '); - } - - content.push('
'); - content.push('Query Alamut'); - - return content.join(''); - }, - renderFrequencies: function(attrs) { var content, evs, tg; @@ -259,114 +124,6 @@ define([ return content.join(''); }, - renderEffects: function(attrs) { - var content, eff, effs, gene, labelClass, type, hasEffects, groupedEffects; - - content = []; - content.push('

Effects

'); - - hasEffects = false; - _.each(attrs.effects, function(eff) { - if (eff.transcript != null) hasEffects = true; - }); - - if (hasEffects) { - content.push('
    '); - - groupedEffects = _.groupBy(attrs.effects, 'type'); - for (type in groupedEffects) { - effs = groupedEffects[type]; - - content.push('
  • '); - - labelClass = utils.priorityClass(utils.effectImpactPriority(effs[0].impact)); - content.push('' + type + ''); - - content.push('
      '); - - for (var i = 0; i < effs.length; i++) { - eff = effs[i]; - - content.push('
    • '); - content.push('' + - eff.transcript.transcript + ' '); - - if (attrs.uniqueGenes.length > 1 && (gene = eff.transcript.gene)) { - content.push('for ' + gene.symbol + ' '); - } - - content.push('
        '); - - if (eff.hgvs_c || eff.segment) { - content.push('
      • '); - } - if (eff.hgvs_c) { - content.push('' + eff.hgvs_c + ' '); - } - if (eff.segment) { - content.push('' + eff.segment + ' '); - } - if (eff.hgvs_c || eff.segment) { - content.push('
      • '); - } - - if (eff.hgvs_p || eff.amino_acid_change) { - var changeString = eff.hgvs_p || eff.amino_acid_change; - - /* - * Key is the basic physicochemical property of the amino acid - * And each character in the value string are the categorizations - * Inspecting the hgvs_p will tell us about the protein change - * For more info refer to - * https://www.ncbi.nlm.nih.gov/Class/Structure/aa/aa_explorer.cgi - */ - var changes = { - 'Non-Polar': 'IFVLWMAGP', - 'Polar': 'CYTSNQ', - 'Negative': 'ED', - 'Positive': 'HKR' - } - var initialAminoAcid = changeString[2], - finalAminoAcid = changeString[changeString.length - 1], - initialProperty = '', - finalProperty = ''; - - // Check if there was a change in the hgvs_p - for (var change in changes) { - if (changes[change].indexOf(initialAminoAcid) != -1) { - initialProperty = change; - } - if (changes[change].indexOf(finalAminoAcid) != -1) { - finalProperty = '→' + change; - } - } - - content.push('
      • ' + changeString); - - // Only display if the protein actually changed - if (initialProperty && finalProperty) { - content.push('
        ' + initialProperty + finalProperty); - } - - content.push('
      • '); - } - - content.push('
      '); - } - - content.push('
    '); - } - - content.push('
'); - } - else { - content.push('

No SNPEff effects known

'); - } - - return content.join(''); - }, - _renderPhenotypeCollection: function(phenotypes) { var content, phenotype, sorted, zpad; @@ -628,136 +385,9 @@ define([ } }); +*/ - var AssessmentTab = Marionette.ItemView.extend({ - template: function () {}, - - el: '#knowledge-capture-content', - - initialize: function() { - _.bindAll(this, 'onAssessmentFetchSuccess', 'onAssessmentFetchError'); - }, - - update: function(model) { - // If this is the first update call then we need to intialize the - // UI elements so we can reference them in the success/error - // handlers in the fetch call we are about to make. - if (this.model == null) { - this.formContainer = $('#knowledge-capture-form-container'); - this.feedbackContainer = $('#knowledge-capture-feedback-container'); - this.saveButton = $('#save-assessment-button'); - this.auditButton = $('#audit-trail-button'); - this.errorContainer = $('#error-container'); - this.errorMsg = $('#error-message'); - $('.alert-error > .close').on('click', this.closeFormErrorsClicked); - } - - this.formContainer.hide(); - this.feedbackContainer.show(); - this.errorContainer.hide(); - - this.model = model; - this.model.fetch({ - error: this.onAssessmentFetchError, - success: this.onAssessmentFetchSuccess - }); - }, - - onAssessmentFetchError: function() { - this.formContainer.hide(); - this.feedbackContainer.hide(); - this.errorContainer.show(); - this.errorMsg.html('
Error retrieving knowledge capture data.
'); - this.saveButton.hide(); - return this.auditButton.hide(); - }, - - onAssessmentFetchSuccess: function() { - this.errorContainer.hide(); - this.feedbackContainer.hide(); - this.formContainer.show(); - return this.render(); - }, - - closeFormErrorsClicked: function(event) { - $(event.target).parent().hide(); - }, - - isValid: function() { - var valid = true; - - this.model.set({ - evidence_details: $('#evidence-details').val(), - sanger_requested: $('input[name=sanger-radio]:checked').val(), - pathogenicity: $('input[name=pathogenicity-radio]:checked').val(), - assessment_category: $('input[name=category-radio]:checked').val(), - mother_result: $('#mother-results').val(), - father_result: $('#father-results').val() - }); - - this.errorContainer.hide(); - this.errorMsg.html(''); - - if (_.isEmpty(this.model.get('pathogenicity'))) { - valid = false; - this.errorMsg.append('
Please select a pathogenicity.
'); - } - - if (_.isEmpty(this.model.get('assessment_category'))) { - valid = false; - this.errorMsg.append('
Please select a category.
'); - } - - if (_.isEmpty(this.model.get('mother_result'))) { - valid = false; - this.errorMsg.append('
Please select a result from the "Mother" dropdown.
'); - } - - if (_.isEmpty(this.model.get('father_result'))) { - valid = false; - this.errorMsg.append('
Please select a result from the "Father" dropdown.
'); - } - - if (this.model.get('sanger_requested') == null) { - valid = false; - this.errorMsg.append('
Please select one of the "Sanger Requested" options.
'); - } - - if (!valid) { - this.errorContainer.show(); - } - - return valid; - }, - - // Checks the radio button with the supplied name and value(all other - // radios with that name are unchecked). - setRadioChecked: function(name, value) { - var checkedRadio, radios; - - // Lookup all the radio buttons using the supplied name - radios = $('input:radio[name=' + name + ']'); - // Uncheck any current selection - $(radios.prop('checked', false)); - // Check the correct radio button based on the supplied value - checkedRadio = $(radios.filter('[value=' + value + ']')); - $(checkedRadio.prop('checked', true)); - $(checkedRadio.change()); - }, - - render: function() { - this.setRadioChecked('category-radio', this.model.get('assessment_category')); - this.setRadioChecked('pathogenicity-radio', this.model.get('pathogenicity')); - this.setRadioChecked('sanger-radio', this.model.get('sanger_requested')); - - $('#mother-results').val(this.model.get('mother_result')); - $('#father-results').val(this.model.get('father_result')); - $('#evidence-details').val(this.model.get('evidence_details')); - } - - }); - - var ResultDetails = Marionette.ItemView.extend({ + var ResultDetails = Marionette.Layout.extend({ className: 'result-details-modal modal hide', // Fairly arbitray, mostly chosen because it was close to normal height @@ -769,83 +399,30 @@ define([ template: 'varify/modals/result', ui: { - variantDetailsTabContent: '#variant-details-content', - saveButton: '#save-assessment-button', - auditTrailButton: '#audit-trail-button' - }, - - events: { - 'click [data-action=close-result-modal]': 'close', - 'click #save-assessment-button': 'saveAndClose', - 'click #variant-details-link': 'hideButtons', - 'click #knowledge-capture-link': 'knowledgeCaptureTabClicked', - 'click [data-target=expand-collapse-link]': 'toggleExpandedState' - }, - - initialize: function() { - _.bindAll(this, 'onSaveError', 'onSaveSuccess'); - - this.assessmentTab = new AssessmentTab; + expandableRows: '[data-target=expandable-details-row]', + expandLinks: '[data-target=expand-collapse-link]' }, - knowledgeCaptureTabClicked: function() { - c.trigger('resultModal:detailsTabClosed'); - this.showButtons(); + regions: { + summary: '[data-target=summary]', + effects: '[data-target=effects]' }, - //Add/remove 'hide' class rather than calling - //show()/hide() to keep height consistent - hideButtons: function() { - this.ui.saveButton.addClass('hide'); - this.ui.auditTrailButton.addClass('hide'); - }, - - showButtons: function() { - this.ui.saveButton.removeClass('hide'); - this.ui.auditTrailButton.removeClass('hide'); - }, - - saveAndClose: function(event) { - if (this.assessmentTab.isValid()) { - this.assessmentTab.model.save(null, {success: this.onSaveSuccess, error: this.onSaveError}); - this.close(); - } + events: { + 'click [data-action=close-result-modal]': 'close', + 'click @ui.expandLinks': 'toggleExpandedState' }, close: function() { - c.trigger('resultModal:closed'); this.$el.modal('hide'); }, - onSaveError: function(model, response) { - $('#review-notification').html('Error saving knowledge capture data.'); - $('#review-notification').addClass('alert-error'); - this.showNotification(); - }, - - onSaveSuccess: function(model, response) { - $('#review-notification').html('Saved changes.'); - $('#review-notification').addClass('alert-success'); - this.showNotification(); - this.selectedSummaryView.model.fetch(); - }, - - showNotification: function() { - $('#review-notification').show(); - setTimeout(this.hideNotification, 5000); - }, - - hideNotification: function() { - $('#review-notification').removeClass('alert-error alert-success'); - $('#review-notification').hide(); - }, - onRender: function() { this.$el.modal({ show: false, keyboard: false, backdrop: 'static' - }) + }); }, /* @@ -871,7 +448,8 @@ define([ for (var i = 0; i < element.children.length; i++) { child = element.children[i]; - if ((child.offsetTop + child.offsetHeight) > this.maxExpandableHeight) { + if ((child.offsetTop + child.offsetHeight) > + this.maxExpandableHeight) { hasOverflow = true; break; } @@ -916,47 +494,36 @@ define([ } }, - open: function(summaryView, result) { - var assessmentModel, metrics; - - this.selectedSummaryView = summaryView; + open: function(result) { this.model = result; - metrics = new models.AssessmentMetrics({}, { - variant_id: result.get('variant').id, - result_id: result.id - }); - - this.detailsTab = new DetailsTab({ - model: result, - metrics: metrics - }); - - this.ui.variantDetailsTabContent.html(this.detailsTab.render); - - // Create a new view for the knowledge capture form - assessmentModel = new models.Assessment({ - sample_result: this.model.id - }); - - if (this.model.get('assessment') != null) { - // We normally would set assessmentModel.id, but, due to - // a change(https://github.com/jashkenas/backbone/pull/2878) in - // Backbone, we need to set this on attributes rather than - // access id directly like we used to. See notes and changes on - // the pull request for more details. - assessmentModel.set(assessmentModel.idAttribute, this.model.get('assessment').id); - } - - this.assessmentTab.update(assessmentModel); + this.summary.show(new variant.Summary({ + model: this.model + })); + + // We exclude effects that don't have a transcript because the + // minimum required data we need to display an effect is held + // within the transcript object. + this.effects.show(new variant.Effects({ + collection: new Backbone.Collection(utils.groupByType( + _.filter(this.model.get('variant').effects, function(effect) { + return effect.transcript !== null; + }) + )) + })); + + this.phenotypes.show(new variant.Phenotyopes({ + collection: new Backbone.Collection( + this.model.get('phenotypes')) + })); this.$el.modal('show'); // Reset the row and item heights and overflow styles as they may // have been toggled previously. - $('[data-target=expandable-details-row]').css('height', '' + this.maxExpandableHeight + 'px') + this.ui.expandableRows.css('height', '' + this.maxExpandableHeight + 'px') .css('overflow', 'hidden'); - $('[data-target=expand-collapse-link]').text(this.showMoreText); + this.ui.expandLinks.text(this.showMoreText); this._checkForOverflow(); } diff --git a/varify/static/js/src/ui/tables/row.js b/varify/static/js/src/ui/tables/row.js index bfa96385..87ff3f51 100644 --- a/varify/static/js/src/ui/tables/row.js +++ b/varify/static/js/src/ui/tables/row.js @@ -1,12 +1,13 @@ /* global define */ define([ + 'underscore', 'jquery', 'marionette', 'cilantro', '../../models', '../../templates' -], function($, Marionette, c, models, Templates) { +], function(_, $, Marionette, c, models, Templates) { var ResultRow = Marionette.ItemView.extend({ className: 'area-container variant-container', @@ -20,10 +21,26 @@ define([ }, onClick: function() { - c.dialogs.resultDetails.open(this, this.model); + c.dialogs.resultDetails.open(this.model); }, - onRender: function() { + initialize: function() { + _.bindAll(this, 'onSync'); + + this.data = {}; + + if (!(this.data.resultPk = this.options.resultPk)) { + throw new Error('result pk required'); + } + + this.model = new models.Result({ + id: this.data.resultPk + }); + + this.model.on('sync', this.onSync); + }, + + onSync: function() { var $condensedFlags, $gene, $genomicPosition, $genotype, $hgvsC, $hgvsP, $phenotypeScore, $variantEffects, assessment, resultScore, variant; @@ -68,7 +85,12 @@ define([ this.$el.empty(); return this.$el.append($gene, $hgvsP, $variantEffects, $hgvsC, $genotype, $genomicPosition, $phenotypeScore, $condensedFlags); + }, + + onRender: function() { + this.model.fetch(); } + }); var EmptyResultRow = c.ui.LoadView.extend({ diff --git a/varify/static/js/src/ui/tables/row/genes.js b/varify/static/js/src/ui/tables/row/genes.js new file mode 100644 index 00000000..ca54951e --- /dev/null +++ b/varify/static/js/src/ui/tables/row/genes.js @@ -0,0 +1,29 @@ +/* global define */ + +define([ + 'marionette' +], function(Marionette) { + + var EmptyGene = Marionette.ItemView.extend({ + template: 'varify/tables/row/genes/empty' + }); + + var GeneItem = Marionette.ItemView.extend({ + template: 'varify/tables/row/genes/item' + }); + + var GeneList = Marionette.CollectionView.extend({ + template: function() {}, + + itemView: GeneItem, + + emptyView: EmptyGene + }); + + return { + EmptyGene: EmptyGene, + GeneItem: GeneItem, + GeneList: GeneList + }; + +}); diff --git a/varify/static/js/src/ui/templates.js b/varify/static/js/src/ui/templates.js index 6371e929..1605b07a 100644 --- a/varify/static/js/src/ui/templates.js +++ b/varify/static/js/src/ui/templates.js @@ -2,31 +2,57 @@ define([ 'cilantro', - 'tpl!../../../templates/tables/header.html', - 'tpl!../../../templates/modals/result.html', - 'tpl!../../../templates/modals/phenotypes.html', - 'tpl!../../../templates/modals/sample.html', - 'tpl!../../../templates/controls/sift.html', 'tpl!../../../templates/controls/polyphen.html', - 'tpl!../../../templates/workflows/results.html', + 'tpl!../../../templates/controls/sift.html', 'tpl!../../../templates/export/dialog.html', + 'tpl!../../../templates/modals/phenotypes.html', + 'tpl!../../../templates/modals/result.html', + 'tpl!../../../templates/modals/sample.html', 'tpl!../../../templates/sample/loader.html', + 'tpl!../../../templates/sample/row.html', 'tpl!../../../templates/sample/table.html', - 'tpl!../../../templates/sample/row.html' -], function(c, header, result, phenotype, sample, sift, polyphen, results, - exportDialog, sampleLoader, sampleTable, sampleRow) { + 'tpl!../../../templates/tables/header.html', + 'tpl!../../../templates/variant/articles.html', + 'tpl!../../../templates/variant/cohorts.html', + 'tpl!../../../templates/variant/effect-item.html', + 'tpl!../../../templates/variant/effect-type.html', + 'tpl!../../../templates/variant/effects.html', + 'tpl!../../../templates/variant/frequencies.html', + 'tpl!../../../templates/variant/phenotypes.html', + 'tpl!../../../templates/variant/scores.html', + 'tpl!../../../templates/variant/summary.html', + 'tpl!../../../templates/workflows/results.html' +], function(c, polyphen, sift, exportDialog, phenotype, result, sample, + sampleLoader, sampleRow, sampleTable, header, variantArticles, + variantCohorts, effectItem, effectType, variantEffects, + variantFrequencies, variantPhenotypes, variantScores, + variantSummary, results) { // Define custom templates + c.templates.set('varify/controls/polyphen', polyphen); + c.templates.set('varify/controls/sift', sift); + c.templates.set('varify/export/dialog', exportDialog); - c.templates.set('varify/tables/header', header); - c.templates.set('varify/modals/result', result); + c.templates.set('varify/modals/phenotype', phenotype); + c.templates.set('varify/modals/result', result); c.templates.set('varify/modals/sample', sample); - c.templates.set('varify/controls/sift', sift); - c.templates.set('varify/controls/polyphen', polyphen); - c.templates.set('varify/workflows/results', results); + c.templates.set('varify/sample/loader', sampleLoader); - c.templates.set('varify/sample/table', sampleTable); c.templates.set('varify/sample/row', sampleRow); + c.templates.set('varify/sample/table', sampleTable); + + c.templates.set('varify/tables/header', header); + + c.templates.set('varify/variant/articles', variantArticles); + c.templates.set('varify/variant/cohorts', variantCohorts); + c.templates.set('varify/variant/effect-item', effectItem); + c.templates.set('varify/variant/effect-type', effectType); + c.templates.set('varify/variant/effects', variantEffects); + c.templates.set('varify/variant/frequencies', variantFrequencies); + c.templates.set('varify/variant/phenotypes', variantPhenotypes); + c.templates.set('varify/variant/scores', variantScores); + c.templates.set('varify/variant/summary', variantSummary); + c.templates.set('varify/workflows/results', results); }); diff --git a/varify/static/js/src/ui/variant.js b/varify/static/js/src/ui/variant.js new file mode 100644 index 00000000..2232a762 --- /dev/null +++ b/varify/static/js/src/ui/variant.js @@ -0,0 +1,18 @@ +/* global define */ + +define([ + 'underscore', + './variant/articles', + './variant/cohorts', + './variant/effects', + './variant/frequencies', + './variant/phenotypes', + './variant/scores', + './variant/summary', +], function(_) { + + var mods = [].slice.call(arguments, 1); + + return _.extend.apply(null, [{}].concat(mods)); + +}); diff --git a/varify/static/js/src/ui/variant/articles.js b/varify/static/js/src/ui/variant/articles.js new file mode 100644 index 00000000..e69de29b diff --git a/varify/static/js/src/ui/variant/cohorts.js b/varify/static/js/src/ui/variant/cohorts.js new file mode 100644 index 00000000..e69de29b diff --git a/varify/static/js/src/ui/variant/effects.js b/varify/static/js/src/ui/variant/effects.js new file mode 100644 index 00000000..6075ef99 --- /dev/null +++ b/varify/static/js/src/ui/variant/effects.js @@ -0,0 +1,138 @@ +/* global define */ + +define([ + 'underscore', + 'cilantro', + 'backbone', + 'marionette', + '../../utils' +], function(_, c, Backbone, Marionette, utils) { + + var EffectItem = Marionette.ItemView.extend({ + tagName: 'li', + + template: 'varify/variant/effect-item', + + templateHelpers: function() { + var hgvsc = this.model.get('hgvs_c') || '', + segment = this.model.get('segment') || '', + hgvscOrSegment = ''; + + if (hgvsc) { + hgvscOrSegment = hgvsc + ' ' + segment; + } + else { + hgvscOrSegment = segment; + } + + var gene, transcript = this.model.get('transcript'), + uniqueGenes = this.model.get('uniqueGenes'); + + if (uniqueGenes && uniqueGenes.length && transcript) { + gene = transcript.gene; + } + + var proteinChange, changeString = + this.model.get('hgvs_p') || this.model.get('amino_acid_change'); + + if (changeString) { + /* + * Key is the basic physicochemical property of the amino acid + * And each character in the value string are the categorizations + * Inspecting the hgvs_p will tell us about the protein change + * For more info refer to + * https://www.ncbi.nlm.nih.gov/Class/Structure/aa/aa_explorer.cgi + */ + + var changes = { + 'Non-Polar': 'IFVLWMAGP', + 'Polar': 'CYTSNQ', + 'Negative': 'ED', + 'Positive': 'HKR' + }; + var initialAminoAcid = changeString[2], + finalAminoAcid = changeString[changeString.length - 1], + initialProperty = '', + finalProperty = ''; + + // Check if there was a change in the hgvs_p + for (var change in changes) { + if (changes[change].indexOf(initialAminoAcid) !== -1) { + initialProperty = change; + } + if (changes[change].indexOf(finalAminoAcid) !== -1) { + finalProperty = '→' + change; + } + } + + if (initialProperty && finalProperty) { + proteinChange = initialProperty + finalProperty; + } + } + + return { + hgvscOrSegment: hgvscOrSegment, + gene: gene, + changeString: changeString, + proteinChange: proteinChange + }; + } + }); + + var EffectType = Marionette.CompositeView.extend({ + tagName: 'ul', + + className: 'unstyled', + + template: 'varify/variant/effect-type', + + itemViewContainer: '[data-target=items]', + + itemView: EffectItem, + + templateHelpers: function() { + var priorityClass = ''; + + if (this.collection.length) { + priorityClass = utils.priorityClass( + utils.effectImpactPriority( + this.collection.models[0].get('impact'))); + } + + return { + priorityClass: priorityClass + }; + } + }); + + var NoEffectsView = c.ui.EmptyView.extend({ + className: 'muted', + + icon: '', + + message: 'No SNPEff effects known', + + align: 'left' + }); + + var Effects = Marionette.CompositeView.extend({ + template: 'varify/variant/effects', + + emptyView: NoEffectsView, + + itemViewContainer: '[data-target=items]', + + itemView: EffectType, + + itemViewOptions: function(model) { + return { + collection: new Backbone.Collection(model.get('effects')) + }; + } + }); + + return { + Effects: Effects + }; + +}); diff --git a/varify/static/js/src/ui/variant/frequencies.js b/varify/static/js/src/ui/variant/frequencies.js new file mode 100644 index 00000000..e69de29b diff --git a/varify/static/js/src/ui/variant/phenotypes.js b/varify/static/js/src/ui/variant/phenotypes.js new file mode 100644 index 00000000..e69de29b diff --git a/varify/static/js/src/ui/variant/scores.js b/varify/static/js/src/ui/variant/scores.js new file mode 100644 index 00000000..e69de29b diff --git a/varify/static/js/src/ui/variant/summary.js b/varify/static/js/src/ui/variant/summary.js new file mode 100644 index 00000000..261cd934 --- /dev/null +++ b/varify/static/js/src/ui/variant/summary.js @@ -0,0 +1,92 @@ +/* global define */ + +define([ + 'underscore', + 'marionette', + '../../utils', + 'cilantro/utils/numbers' +], function(_, Marionette, utils, Numbers) { + + var Summary = Marionette.ItemView.extend({ + template: 'varify/variant/summary', + + serializeData: function() { + // Serialize the data normally and then we will update it as needed + var data = Marionette.ItemView.prototype.serializeData.apply( + this, arguments); + + // Mark the class to use when displaying the read depth to visually + // indicate magnitude of read depth. + data.read_depth_class = utils.depthClass(data.read_depth); // jshint ignore:line + + data.read_depth_ref = data.read_depth_ref || '--'; // jshint ignore:line + data.read_depth_alt = data.read_depth_alt || '--'; // jshint ignore:line + + if (data.raw_read_depth) { + data.raw_read_depth_class = utils.depthClass(data.raw_read_depth); + data.raw_read_depth = '' + data.raw_read_depth + 'x'; + } + else { + data.raw_read_depth = 'n/a'; + data.raw_read_depth_class = 'muted'; + } + + if (data.quality) { + data.quality_class = utils.qualityClass(data.quality); + } + else { + data.quality = 'n/a'; + } + + data.genotype_value = data.genotype_value || 'n/a'; + + var bases = []; + for (var key in data.base_counts || {}) { + bases.push('' + key + '=' + data.base_counts[key]); + } + + if (bases.length) { + data.base_counts = bases.sort().join(', '); + } + else { + data.base_counts = 'n/a'; + } + + data.pchr = Numbers.toDelimitedNumber(data.variant.pos); + + // TODO: Gene link list could probably be its own collection view + var name, gene, genes = []; + for (var i = 0; i < data.variant.uniqueGenes.length; i++) { + gene = data.variant.uniqueGenes[i]; + name = gene.name || ''; + + if (gene.hgnc_id) { + genes.push('' + gene.symbol + ''); + } + else { + genes.push('' + gene.symbol + ''); + } + } + data.genes = genes.join(', '); + + var hgmd = _.pluck( + _.where(data.variant.phenotypes, function(phenotype) { + return phenotype.hgmd_id != null; + }), 'hgmd_id'); + + if (hgmd.length) { + data.hgmd = hgmd.join(', '); + } + else { + data.hgmd = 'n/a'; + } + + return data; + } + }); + + return { + Summary: Summary + }; + +}); diff --git a/varify/static/js/src/utils.js b/varify/static/js/src/utils.js index d26e8d8e..435a3e51 100644 --- a/varify/static/js/src/utils.js +++ b/varify/static/js/src/utils.js @@ -17,6 +17,23 @@ define([ }; + var groupByType = function(effects) { + var data = []; + + if (effects && effects.length) { + var groupedEffects = _.groupBy(effects, 'type'); + for (var type in groupedEffects) { + data.push({ + type: type, + effects: groupedEffects[type] + }); + } + } + + return data; + }; + + var effectImpactPriority = function(impact) { var priority; @@ -203,6 +220,7 @@ define([ depthClass: depthClass, effectImpactPriority: effectImpactPriority, getRootUrl: getRootUrl, + groupByType: groupByType, parseISO8601UTC: parseISO8601UTC, priorityClass: priorityClass, qualityClass: qualityClass, diff --git a/varify/static/templates/modals/result.html b/varify/static/templates/modals/result.html index 3f963453..f1f3f3b7 100644 --- a/varify/static/templates/modals/result.html +++ b/varify/static/templates/modals/result.html @@ -1,138 +1,25 @@ -