gridftp: fixed height file list
[qcg-portal.git] / qcg / models.py
index b41fe85..316f56e 100644 (file)
@@ -4,12 +4,12 @@ from django.core.urlresolvers import reverse
 from django.db import models
 from django.utils.timezone import now
 from pyqcg.service import Job as QcgJob, Task as QcgTask
 from django.db import models
 from django.utils.timezone import now
 from pyqcg.service import Job as QcgJob, Task as QcgTask
-from pyqcg.utils import JobStatus, TaskStatus, TaskType, AllocationType, EPRUtils
+from pyqcg.utils import JobStatus, TaskStatus, TaskType, AllocationType, EPRUtils, DescriptionType
 
 from qcg.utils import username_from_dn, get_attributes
 
 
 
 from qcg.utils import username_from_dn, get_attributes
 
 
-__all__ = ['User', 'Job', 'Task', 'Allocation', 'NodeInfo']
+__all__ = ['User', 'Job', 'Task', 'Allocation', 'NodeInfo', 'JobTemplate']
 
 
 class User(AbstractUser):
 
 
 class User(AbstractUser):
@@ -20,6 +20,10 @@ class User(AbstractUser):
         return Task.objects.filter(job__owner=self)
 
 
         return Task.objects.filter(job__owner=self)
 
 
+DESC_TYPE_CHOICES = list(enumerate(field for field in dir(DescriptionType) if not field.startswith('__')))
+DESC_TYPE_CHOICES_REVERSED = {v: k for k, v in DESC_TYPE_CHOICES}
+
+
 class Job(models.Model):
     STATUS_CHOICES = list(enumerate(field for field in dir(JobStatus) if not field.startswith('__')))
     STATUS_CHOICES_REVERSED = {v: k for k, v in STATUS_CHOICES}
 class Job(models.Model):
     STATUS_CHOICES = list(enumerate(field for field in dir(JobStatus) if not field.startswith('__')))
     STATUS_CHOICES_REVERSED = {v: k for k, v in STATUS_CHOICES}
@@ -31,6 +35,8 @@ class Job(models.Model):
     status = models.IntegerField(u"Status", choices=STATUS_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
     status = models.IntegerField(u"Status", choices=STATUS_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
+    description_type = models.IntegerField(u"Typ opisu", choices=DESC_TYPE_CHOICES, blank=True, null=True)
+    qcg_description = models.TextField(u"Opis QCG", blank=True, default='')
     project = models.TextField(u"Projekt", blank=True, default='')
     submission_time = models.DateTimeField(u"Data wysłania")
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
     project = models.TextField(u"Projekt", blank=True, default='')
     submission_time = models.DateTimeField(u"Data wysłania")
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
@@ -61,29 +67,27 @@ class Job(models.Model):
 
     @staticmethod
     def qcg_map(qcg_job, user=None):
 
     @staticmethod
     def qcg_map(qcg_job, user=None):
-        attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'submission_time', 'finish_time',
-                                         'project', 'purged'))
+        attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'qcg_description', 'submission_time',
+                                         'finish_time', 'project', 'purged'))
 
         attrs['epr'] = EPRUtils.serialize_epr(qcg_job.epr)
         attrs['status'] = Job.STATUS_CHOICES_REVERSED[qcg_job.status]
 
         attrs['epr'] = EPRUtils.serialize_epr(qcg_job.epr)
         attrs['status'] = Job.STATUS_CHOICES_REVERSED[qcg_job.status]
+        attrs['description_type'] = DESC_TYPE_CHOICES_REVERSED[qcg_job.description_type]
         attrs['proxy_lifetime'] = now() + qcg_job.proxy_lifetime
 
         username = username_from_dn(qcg_job.user_dn)
         if user is not None:
             if user.username != username:
         attrs['proxy_lifetime'] = now() + qcg_job.proxy_lifetime
 
         username = username_from_dn(qcg_job.user_dn)
         if user is not None:
             if user.username != username:
-                raise ValueError('Username does not match!')
+                raise ValueError('Username does not match ({} vs. {})!'.format(repr(user.username), repr(username)))
             attrs['owner'] = user
         else:
             attrs['owner'] = User.objects.get(username=username)
 
         return attrs
 
             attrs['owner'] = user
         else:
             attrs['owner'] = User.objects.get(username=username)
 
         return attrs
 
-    @classmethod
-    def from_qcg(cls, qcg_job):
-        job = cls(**cls.qcg_map(qcg_job))
-        job._job = qcg_job
-
-        return job
+    @property
+    def terminated(self):
+        return self.get_status_display() in [JobStatus.FINISHED, JobStatus.FAILED, JobStatus.CANCELED]
 
 
 class Task(models.Model):
 
 
 class Task(models.Model):
@@ -103,6 +107,7 @@ class Task(models.Model):
     type = models.IntegerField(u"Typ", choices=TYPE_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
     type = models.IntegerField(u"Typ", choices=TYPE_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
+    description_type = models.IntegerField(u"Typ opisu", choices=DESC_TYPE_CHOICES, blank=True, null=True)
     submission_time = models.DateTimeField(u"Data wysłania")
     start_time = models.DateTimeField(u"Data rozpoczęcia", blank=True, null=True)
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
     submission_time = models.DateTimeField(u"Data wysłania")
     start_time = models.DateTimeField(u"Data rozpoczęcia", blank=True, null=True)
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
@@ -128,35 +133,31 @@ class Task(models.Model):
 
     @property
     def qcg_task(self):
 
     @property
     def qcg_task(self):
-        if self._qcg_task is None:
-            self._qcg_task = QcgTask(EPRUtils.deserialize_epr(self.epr))
+        if self._task is None:
+            self._task = QcgTask(EPRUtils.deserialize_epr(self.epr))
 
 
-        return self._qcg_task
+        return self._task
 
     @staticmethod
 
     @staticmethod
-    def qcg_map(qcg_task, jobs=None):
+    def qcg_map(qcg_task, job=None):
         attrs = get_attributes(qcg_task, ('task_id', 'status_description', 'note', 'description', 'submission_time',
                                           'start_time', 'finish_time', 'reserved_time_slot', 'purged'))
 
         attrs['epr'] = EPRUtils.serialize_epr(qcg_task.epr)
         attrs['status'] = Task.STATUS_CHOICES_REVERSED[qcg_task.status]
         attrs['type'] = Task.TYPE_CHOICES_REVERSED[qcg_task.type]
         attrs = get_attributes(qcg_task, ('task_id', 'status_description', 'note', 'description', 'submission_time',
                                           'start_time', 'finish_time', 'reserved_time_slot', 'purged'))
 
         attrs['epr'] = EPRUtils.serialize_epr(qcg_task.epr)
         attrs['status'] = Task.STATUS_CHOICES_REVERSED[qcg_task.status]
         attrs['type'] = Task.TYPE_CHOICES_REVERSED[qcg_task.type]
+        attrs['description_type'] = DESC_TYPE_CHOICES_REVERSED[qcg_task.description_type]
         attrs['proxy_lifetime'] = now() + qcg_task.proxy_lifetime
 
         attrs['proxy_lifetime'] = now() + qcg_task.proxy_lifetime
 
-        if jobs is not None and qcg_task.job_id in jobs:
-            attrs['job'] = jobs[qcg_task.job_id]
+        if job is not None:
+            if qcg_task.job_id != job.job_id:
+                raise ValueError('Job id does not match ({} vs. {})!'.format(repr(qcg_task.job_id), repr(job.job_id)))
+            attrs['job'] = job
         else:
             attrs['job'] = Job.objects.get(job_id=qcg_task.job_id)
 
         return attrs
 
         else:
             attrs['job'] = Job.objects.get(job_id=qcg_task.job_id)
 
         return attrs
 
-    @classmethod
-    def from_qcg(cls, qcg_task):
-        task = cls(**cls.qcg_map(qcg_task))
-        task._task = qcg_task
-
-        return task
-
     @property
     def reserved_time_slot(self):
         if self.reserved_time_start or self.reserved_time_finish:
     @property
     def reserved_time_slot(self):
         if self.reserved_time_start or self.reserved_time_finish:
@@ -171,6 +172,10 @@ class Task(models.Model):
     def short_host_names(self):
         return {alloc.host_name.split('.', 1)[0] for alloc in self.allocations.all()}
 
     def short_host_names(self):
         return {alloc.host_name.split('.', 1)[0] for alloc in self.allocations.all()}
 
+    @property
+    def terminated(self):
+        return self.get_status_display() in [TaskStatus.FINISHED, TaskStatus.FAILED, TaskStatus.CANCELED]
+
 
 class Allocation(models.Model):
     STATUS_CHOICES = list(enumerate(field for field in dir(AllocationType) if not field.startswith('__')))
 
 class Allocation(models.Model):
     STATUS_CHOICES = list(enumerate(field for field in dir(AllocationType) if not field.startswith('__')))
@@ -194,6 +199,7 @@ class Allocation(models.Model):
     efficiency = models.IntegerField(u"Efektywność", blank=True, null=True)  # ??
     comment = models.TextField(u"Komentarz", blank=True, default='')
     memory_usage = models.PositiveIntegerField(u"Użycie pamięci", blank=True, null=True)
     efficiency = models.IntegerField(u"Efektywność", blank=True, null=True)  # ??
     comment = models.TextField(u"Komentarz", blank=True, default='')
     memory_usage = models.PositiveIntegerField(u"Użycie pamięci", blank=True, null=True)
+    working_directory = models.CharField(u"Katalog roboczy", max_length=1024, blank=True, default='')
 
     class Meta:
         verbose_name = u"Alokacja"
 
     class Meta:
         verbose_name = u"Alokacja"
@@ -208,7 +214,8 @@ class Allocation(models.Model):
         attrs = get_attributes(qcg_allocation, ('host_name', 'status_description', 'processes_count', 'slots_count',
                                                 'processes_group_id', 'submission_time', 'estimated_start_time',
                                                 'finish_time', 'local_submission_time', 'local_start_time',
         attrs = get_attributes(qcg_allocation, ('host_name', 'status_description', 'processes_count', 'slots_count',
                                                 'processes_group_id', 'submission_time', 'estimated_start_time',
                                                 'finish_time', 'local_submission_time', 'local_start_time',
-                                                'local_finish_time', 'purged', 'efficiency', 'comment', 'memory_usage'))
+                                                'local_finish_time', 'purged', 'efficiency', 'comment', 'memory_usage',
+                                                'working_directory'))
 
         attrs['status'] = Allocation.STATUS_CHOICES_REVERSED[qcg_allocation.status]
 
 
         attrs['status'] = Allocation.STATUS_CHOICES_REVERSED[qcg_allocation.status]
 
@@ -236,3 +243,22 @@ class NodeInfo(models.Model):
     @staticmethod
     def qcg_map(qcg_node):
         return {'name': qcg_node.name, 'count': qcg_node.slots_count}
     @staticmethod
     def qcg_map(qcg_node):
         return {'name': qcg_node.name, 'count': qcg_node.slots_count}
+
+
+class JobTemplate(models.Model):
+    name = models.CharField(u"Nazwa", max_length=100)
+    description = models.TextField(u"Opis")
+
+    owner = models.ForeignKey(User, verbose_name=u"Właściciel", related_name='templates')
+    created = models.DateTimeField(u"Utworzono", auto_now_add=True)
+    updated = models.DateTimeField(u"Uaktualniono", auto_now=True)
+
+    class Meta:
+        verbose_name = u"Szablon zadania"
+        verbose_name_plural = u"Szablony zadania"
+
+    def __unicode__(self):
+        return u"{} ({})".format(self.name, self.owner)
+
+    def get_absolute_url(self):
+        return reverse('template_submit', kwargs={'template_id': self.id})