fix bottom margin of modal body
[qcg-portal.git] / qcg / models.py
index 8c2be2d..580944c 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
+from pyqcg.utils import JobStatus, TaskStatus, TaskType, AllocationType, EPRUtils
 
 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):
@@ -55,7 +55,7 @@ class Job(models.Model):
     @property
     def qcg_job(self):
         if self._job is None:
     @property
     def qcg_job(self):
         if self._job is None:
-            self._job = QcgJob(self.epr)
+            self._job = QcgJob(EPRUtils.deserialize_epr(self.epr))
 
         return self._job
 
 
         return self._job
 
@@ -64,26 +64,23 @@ class Job(models.Model):
         attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'submission_time', 'finish_time',
                                          'project', 'purged'))
 
         attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'submission_time', 'finish_time',
                                          'project', 'purged'))
 
-        attrs['epr'] = str(qcg_job.epr)
+        attrs['epr'] = EPRUtils.serialize_epr(qcg_job.epr)
         attrs['status'] = Job.STATUS_CHOICES_REVERSED[qcg_job.status]
         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['status'] = Job.STATUS_CHOICES_REVERSED[qcg_job.status]
         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('')
+                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):
@@ -128,35 +125,30 @@ class Task(models.Model):
 
     @property
     def qcg_task(self):
 
     @property
     def qcg_task(self):
-        if self._qcg_task is None:
-            self._qcg_task = QcgTask(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 = get_attributes(qcg_task, ('task_id', 'status_description', 'note', 'description', 'submission_time',
                                           'start_time', 'finish_time', 'reserved_time_slot', 'purged'))
 
-        attrs['epr'] = str(qcg_task.epr)
+        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['proxy_lifetime'] = now() + qcg_task.proxy_lifetime
 
         attrs['status'] = Task.STATUS_CHOICES_REVERSED[qcg_task.status]
         attrs['type'] = Task.TYPE_CHOICES_REVERSED[qcg_task.type]
         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:
@@ -169,13 +161,21 @@ class Task(models.Model):
 
     @property
     def short_host_names(self):
 
     @property
     def short_host_names(self):
-        return {alloc.host_name.split('.')[0] for alloc in self.allocations.all()}
+        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):
 
 
 class Allocation(models.Model):
+    STATUS_CHOICES = list(enumerate(field for field in dir(AllocationType) if not field.startswith('__')))
+    STATUS_CHOICES_REVERSED = {v: k for k, v in STATUS_CHOICES}
+
     task = models.ForeignKey(Task, verbose_name='Zadanie', related_name='allocations')
 
     host_name = models.CharField(u"Host", max_length=100)
     task = models.ForeignKey(Task, verbose_name='Zadanie', related_name='allocations')
 
     host_name = models.CharField(u"Host", max_length=100)
+    status = models.IntegerField(u"Status", choices=STATUS_CHOICES)
     status_description = models.TextField(u"Opis statusu", blank=True, default='')
     processes_count = models.PositiveIntegerField(u"Liczba procesów")
     slots_count = models.PositiveIntegerField(u"Liczba rdzeni")
     status_description = models.TextField(u"Opis statusu", blank=True, default='')
     processes_count = models.PositiveIntegerField(u"Liczba procesów")
     slots_count = models.PositiveIntegerField(u"Liczba rdzeni")
@@ -190,6 +190,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"
@@ -201,10 +202,15 @@ class Allocation(models.Model):
 
     @staticmethod
     def qcg_map(qcg_allocation):
 
     @staticmethod
     def qcg_map(qcg_allocation):
-        return 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'))
+        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',
+                                                'working_directory'))
+
+        attrs['status'] = Allocation.STATUS_CHOICES_REVERSED[qcg_allocation.status]
+
+        return attrs
 
 
 class NodeInfo(models.Model):
 
 
 class NodeInfo(models.Model):
@@ -227,6 +233,23 @@ class NodeInfo(models.Model):
 
     @staticmethod
     def qcg_map(qcg_node):
 
     @staticmethod
     def qcg_map(qcg_node):
-        return {'name': qcg_node.name,
-                # FIXME
-                'count': qcg_node.slots_count.intValue() if qcg_node.slots_count is not None else None}
+        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})