Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
models.py 5.05 KiB
from django.db import models
from django.urls import reverse
from django.utils.timezone import datetime
from django.db.models import Sum

# constants
datetime_format_string = '%Y-%m-%dT%H:%M:%SZ'

TASK_TYPE_OBSERVATION = 'observation'
TASK_TYPE_DATAPRODUCT = 'dataproduct'

TYPE_VISIBILITY = 'visibility'

# common model functions
def get_sum_from_dataproduct_field(taskID, field):
    """
    sum the values of a field for certain taskID. (used to sum total of dataproduct sizes)
    :param taskID:
    :param field:
    :return:
    """
    query = field + '__sum'
    sum_value = DataProduct.objects.filter(taskID=taskID).aggregate(Sum(field))[query]
    if sum_value == None:
        sum_value = 0.0
    return sum_value

"""
a base class of both Observation and Dataproducts
"""
class TaskObject(models.Model):
    name = models.CharField(max_length=100, default="unknown")
    task_type = models.CharField(max_length=20, default=TASK_TYPE_DATAPRODUCT)

    taskID = models.CharField('runId', db_index=True, max_length=30, blank=True, null=True)
    creationTime = models.DateTimeField(default=datetime.utcnow, blank=True)

    new_status = models.CharField(max_length=50, default="defined", null=True)
    data_location = models.CharField(max_length=255, default="unknown",null=True, blank=True)

    # my_status is 'platgeslagen', because django-filters can not filter on a related property,
    # and I need services to be able to filter on a status to execute their tasks.
    my_status = models.CharField(db_index=True, max_length=50,default="defined")
    node = models.CharField(max_length=10, null=True, blank=True)
    quality = models.CharField(max_length=30, default="unknown")
    metadata = models.JSONField(null=True,blank=True)

    def __str__(self):
        return str(self.id)


class Status(models.Model):
    name = models.CharField(max_length=50, default="unknown")
    timestamp = models.DateTimeField(default=datetime.utcnow, blank=True)
    taskObject = models.ForeignKey(TaskObject, related_name='status_history', on_delete=models.CASCADE, null=False)

    @property
    def property_taskID(self):
        return self.taskObject.taskID

    @property
    def property_task_type(self):
        return self.taskObject.task_type

    # the representation of the value in the REST API
    def __str__(self):
        formatedDate = self.timestamp.strftime(datetime_format_string)
        return str(self.name)+' ('+str(formatedDate)+')'


class Observation(TaskObject):
    starttime = models.DateTimeField('start time', null=True)
    endtime = models.DateTimeField('end time', null=True)

    # can be used to distinguish types of observations, like for ARTS.
    observing_mode = models.CharField(max_length=50, default="imaging")

    # json object containing unmodelled parameters that are used by the 'executor' service
    # to create the parset based on a template and these parameters
    field_name = models.CharField(max_length=50, null=True)
    field_ra = models.FloatField('field_ra', null = True)
    field_dec = models.FloatField('field_dec', null = True)
    skip_auto_ingest = models.BooleanField(default=False)

    progress = models.CharField(max_length=40, default="0%", null=True, blank=True)

    # this translates a view-name (from urls.py) back to a url, to avoid hardcoded url's in the html templates
    # bad : <td><a href="/atdb/observations/{{ observation.id }}/" target="_blank">{{ observation.taskID }} </a> </td>
    # good: <td><a href="{{ observation.get_absolute_url }}" target="_blank">{{ observation.taskID }} </a> </td>
    def get_absolute_url(self):
        return reverse('observation-detail-view-api', kwargs={'pk': self.pk})

    @property
    def duration(self):
        try:
            duration = (self.endtime - self.starttime).seconds
        except:
            # to prevent crash for invalid observations that do not have a starttime
            duration = 0
        return duration

    @property
    def size(self):
        # sum the sizes of all dataproducts with this taskID. In Mb
        size = get_sum_from_dataproduct_field(self.taskID,'size')
        return size

    def __str__(self):
        return str(self.taskID)


class DataProduct(TaskObject):
    # properties
    filename = models.CharField(max_length=200, default="unknown")
    description = models.CharField(max_length=255, default="unknown")
    dataproduct_type = models.CharField('type', default=TYPE_VISIBILITY, max_length=50)
    size = models.BigIntegerField(default=0)

    # relationships
    parent = models.ForeignKey(Observation, related_name='generated_dataproducts', on_delete=models.CASCADE, null=False)

    # this translates a view-name (from urls.py) back to a url, to avoid hardcoded url's in the html templates
    # bad : <td><a href="/atdb/observations/{{ observation.id }}/" target="_blank">{{ observation.taskID }} </a> </td>
    # good: <td><a href="{{ observation.get_absolute_url }}" target="_blank">{{ observation.taskID }} </a> </td>
    def get_absolute_url(self):
        return reverse('dataproduct-detail-view-api', kwargs={'pk': self.pk})

    def __str__(self):
        return self.filename