Attempt to be robust to not having an alignment in our RunXml file
[htsworkflow.git] / htsworkflow / frontend / experiments / models.py
index 044dee617b5b7705df063f03a6abf9e097aa601e..5152c406e2fc55c87bd997ed7d4ee14197339859 100644 (file)
@@ -10,7 +10,7 @@ from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
 from django.core import urlresolvers
 from django.db import models
-from django.db.models.signals import post_init
+from django.db.models.signals import post_init, pre_save
 
 from htsworkflow.frontend.samples.models import Library
 from htsworkflow.util.conversion import parse_flowcell_id
@@ -48,10 +48,35 @@ RUN_STATUS_REVERSE_MAP = dict(((v, k) for k, v in RUN_STATUS_CHOICES))
 class ClusterStation(models.Model):
     """List of cluster stations"""
     name = models.CharField(max_length=50, unique=True)
+    isdefault = models.BooleanField(default=False, null=False)
+
+    class Meta:
+        ordering = ["-isdefault", "name"]
 
     def __unicode__(self):
         return unicode(self.name)
 
+    @classmethod
+    def default(cls):
+        d = cls.objects.filter(isdefault=True).all()
+        if len(d) > 0:
+            return d[0]
+        d = cls.objects.order_by('-id').all()
+        if len(d) > 0:
+            return d[0]
+        return None
+
+    @staticmethod
+    def update_isdefault(sender, instance, **kwargs):
+        """Clear default if needed
+        """
+        if instance.isdefault:
+            for c in ClusterStation.objects.filter(isdefault=True).all():
+                if c.id != instance.id:
+                    c.isdefault = False
+                    c.save()
+
+pre_save.connect(ClusterStation.update_isdefault, sender=ClusterStation)
 
 class Sequencer(models.Model):
     """Sequencers we've owned
@@ -61,10 +86,11 @@ class Sequencer(models.Model):
     serial_number = models.CharField(max_length=50, db_index=True)
     model = models.CharField(max_length=255)
     active = models.BooleanField(default=True, null=False)
+    isdefault = models.BooleanField(default=False, null=False)
     comment = models.CharField(max_length=255)
 
     class Meta:
-        ordering = ["-active", "name"]
+        ordering = ["-isdefault", "-active", "name"]
 
     def __unicode__(self):
         name = [unicode(self.name)]
@@ -77,6 +103,28 @@ class Sequencer(models.Model):
         return ('htsworkflow.frontend.experiments.views.sequencer',
                 [self.id])
 
+    @classmethod
+    def default(cls):
+        d = cls.objects.filter(isdefault=True).all()
+        if len(d) > 0:
+            return d[0]
+        d = cls.objects.order_by('active', '-id').all()
+        if len(d) > 0:
+            return d[0]
+        return None
+
+    @staticmethod
+    def update_isdefault(sender, instance, **kwargs):
+        """Clear default if needed
+        """
+        if instance.isdefault:
+            for s in Sequencer.objects.filter(isdefault=True).all():
+                if s.id != instance.id:
+                    s.isdefault = False
+                    s.save()
+
+pre_save.connect(Sequencer.update_isdefault, sender=Sequencer)
+
 
 class FlowCell(models.Model):
     flowcell_id = models.CharField(max_length=20, unique=True, db_index=True)
@@ -96,8 +144,8 @@ class FlowCell(models.Model):
                                        null=True,
                                        blank=True)
 
-    cluster_station = models.ForeignKey(ClusterStation, default=3)
-    sequencer = models.ForeignKey(Sequencer, default=1)
+    cluster_station = models.ForeignKey(ClusterStation, default=ClusterStation.default)
+    sequencer = models.ForeignKey(Sequencer, default=Sequencer.default)
 
     notes = models.TextField(blank=True)
 
@@ -161,32 +209,34 @@ class FlowCell(models.Model):
         result_home_dir = os.path.join(settings.RESULT_HOME_DIR, '')
         run_xml_re = re.compile(glob.fnmatch.translate('run*.xml'))
 
-        dataruns = dict([(x.result_dir, x) for x in self.datarun_set.all()])
-
         result_dirs = []
         for dirpath, dirnames, filenames in os.walk(result_root):
             for filename in filenames:
                 if run_xml_re.match(filename):
                     # we have a run directory
                     relative_pathname = get_relative_pathname(dirpath)
-                    cached_run = dataruns.get(relative_pathname, None)
-                    now = datetime.datetime.now()
-                    if (cached_run is None):
-                        self.import_data_run(relative_pathname, filename)
-                    elif (now - cached_run.last_update_time).days > \
-                             RESCAN_DELAY:
-                        self.import_data_run(relative_pathname,
-                                             filename, cached_run)
-
-    def import_data_run(self, relative_pathname, run_xml_name, run=None):
+                    self.import_data_run(relative_pathname, filename)
+
+    def import_data_run(self, relative_pathname, run_xml_name, force=False):
         """Given a result directory import files"""
+        now = datetime.datetime.now()
         run_dir = get_absolute_pathname(relative_pathname)
         run_xml_path = os.path.join(run_dir, run_xml_name)
-        run_xml_data = runfolder.load_pipeline_run_xml(run_xml_path)
-        LOGGER.debug("Importing run from %s" % (relative_pathname,))
 
-        if run is None:
+        runs = DataRun.objects.filter(result_dir = relative_pathname)
+        if len(runs) == 0:
             run = DataRun()
+            created = True
+        elif len(runs) > 1:
+            raise RuntimeError("Too many data runs for %s" % (
+                relative_pathname,))
+        else:
+            run = runs[0]
+            created = False
+
+        if created or force or (now-run.last_update_time).days > RESCAN_DELAY:
+            LOGGER.debug("Importing run from %s" % (relative_pathname,))
+            run_xml_data = runfolder.load_pipeline_run_xml(run_xml_path)
             run.flowcell = self
             run.status = RUN_STATUS_REVERSE_MAP['DONE']
             run.result_dir = relative_pathname
@@ -198,13 +248,15 @@ class FlowCell(models.Model):
             run.image_version = run_xml_data.image_analysis.version
             run.basecall_software = run_xml_data.bustard.software
             run.basecall_version = run_xml_data.bustard.version
-            run.alignment_software = run_xml_data.gerald.software
-            run.alignment_version = run_xml_data.gerald.version
+            # we're frequently not running alignments
+            if run_xml_data.gerald:
+                run.alignment_software = run_xml_data.gerald.software
+                run.alignment_version = run_xml_data.gerald.version
 
-        run.last_update_time = datetime.datetime.now()
-        run.save()
+            run.last_update_time = datetime.datetime.now()
+            run.save()
 
-        run.update_result_files()
+            run.update_result_files()
 
 
 # FIXME: should we automatically update dataruns?