Skip to content
Snippets Groups Projects
Commit ab93d0fe authored by Nico Vermaas's avatar Nico Vermaas
Browse files

complete overhaul of the datamodel

parent c29b322f
No related branches found
No related tags found
No related merge requests found
Pipeline #8012 passed
Showing
with 287 additions and 607 deletions
...@@ -2,7 +2,7 @@ from atdb.settings.base import * ...@@ -2,7 +2,7 @@ from atdb.settings.base import *
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ["80.101.27.83","localhost","192.168.178.32","127.0.0.1"] ALLOWED_HOSTS = ["*"]
CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_ALLOW_ALL = True
##################################################### #####################################################
...@@ -56,7 +56,3 @@ DATABASES = { ...@@ -56,7 +56,3 @@ DATABASES = {
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [] AUTH_PASSWORD_VALIDATORS = []
ALTA_HOST = "http://localhost:8000/altapi"
ALTA_USER = "atdb_write"
ALTA_PASS = "7VVJruVn8W1n"
\ No newline at end of file
from django.contrib import admin from django.contrib import admin
from .models import Status, Observation from .models import Status, Task, Workflow, LogEntry
admin.site.register(Status) admin.site.register(Status)
admin.site.register(Observation) admin.site.register(Task)
\ No newline at end of file admin.site.register(Workflow)
admin.site.register(LogEntry)
\ No newline at end of file
VERSION = "Version 1.0.2 (27 oct 2018)" VERSION = "Version 1.0.0 (14 jan 2020)"
OBSERVATIONS_PER_PAGE = 50 TASKS_PER_PAGE = 50
# Generated by Django 3.1.4 on 2020-12-22 13:24
import datetime
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='TaskObject',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='unknown', max_length=100)),
('task_type', models.CharField(default='dataproduct', max_length=20)),
('taskID', models.CharField(blank=True, db_index=True, max_length=30, null=True, verbose_name='runId')),
('creationTime', models.DateTimeField(blank=True, default=datetime.datetime.utcnow)),
('new_status', models.CharField(default='defined', max_length=50, null=True)),
('data_location', models.CharField(blank=True, default='unknown', max_length=255, null=True)),
('my_status', models.CharField(db_index=True, default='defined', max_length=50)),
('node', models.CharField(blank=True, max_length=10, null=True)),
('quality', models.CharField(default='unknown', max_length=30)),
],
),
migrations.CreateModel(
name='Observation',
fields=[
('taskobject_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='taskdatabase.taskobject')),
('starttime', models.DateTimeField(null=True, verbose_name='start time')),
('endtime', models.DateTimeField(null=True, verbose_name='end time')),
('observing_mode', models.CharField(default='imaging', max_length=50)),
('field_name', models.CharField(max_length=50, null=True)),
('field_ra', models.FloatField(null=True, verbose_name='field_ra')),
('field_dec', models.FloatField(null=True, verbose_name='field_dec')),
('skip_auto_ingest', models.BooleanField(default=False)),
('progress', models.CharField(default='', max_length=40, null=True)),
],
bases=('taskdatabase.taskobject',),
),
migrations.CreateModel(
name='Status',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='unknown', max_length=50)),
('timestamp', models.DateTimeField(blank=True, default=datetime.datetime.utcnow)),
('taskObject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='status_history', to='taskdatabase.taskobject')),
],
),
migrations.CreateModel(
name='DataProduct',
fields=[
('taskobject_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='taskdatabase.taskobject')),
('filename', models.CharField(default='unknown', max_length=200)),
('description', models.CharField(default='unknown', max_length=255)),
('dataproduct_type', models.CharField(default='visibility', max_length=50, verbose_name='type')),
('size', models.BigIntegerField(default=0)),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='generated_dataproducts', to='taskdatabase.observation')),
],
bases=('taskdatabase.taskobject',),
),
]
# Generated by Django 3.1.4 on 2020-12-22 13:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('taskdatabase', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='taskobject',
name='metadata',
field=models.JSONField(blank=True, null=True),
),
]
# Generated by Django 3.1.4 on 2021-01-14 10:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('taskdatabase', '0002_taskobject_metadata'),
]
operations = [
migrations.AlterField(
model_name='observation',
name='progress',
field=models.CharField(blank=True, default='0%', max_length=40, null=True),
),
migrations.AlterField(
model_name='taskobject',
name='task_type',
field=models.CharField(default='observation', max_length=20),
),
migrations.DeleteModel(
name='DataProduct',
),
]
...@@ -6,81 +6,60 @@ from django.db.models import Sum ...@@ -6,81 +6,60 @@ from django.db.models import Sum
# constants # constants
datetime_format_string = '%Y-%m-%dT%H:%M:%SZ' datetime_format_string = '%Y-%m-%dT%H:%M:%SZ'
TASK_TYPE_OBSERVATION = 'observation' class Workflow(models.Model):
repository = models.CharField(max_length=100, blank=True, null=True, default="unknown")
commit_id = models.CharField(max_length=30, blank=True, null=True)
path = models.CharField(max_length=100, blank=True, null=True, default="unknown")
TYPE_VISIBILITY = 'visibility' class LogEntry(models.Model):
step_name = models.CharField(max_length=30, blank=True, null=True, default="unknown")
class TaskObject(models.Model):
name = models.CharField(max_length=100, default="unknown")
task_type = models.CharField(max_length=20, default=TASK_TYPE_OBSERVATION)
taskID = models.CharField('runId', db_index=True, max_length=30, blank=True, null=True) class Task(models.Model):
taskID = models.CharField(db_index=True, max_length=30, blank=True, null=True)
task_type = models.CharField(max_length=20, default="task")
project = models.CharField(max_length=100, blank=True, null=True, default="unknown")
sas_id = models.CharField(max_length=30, blank=True, null=True)
priority = models.IntegerField(default=0)
purge_policy = models.CharField(max_length=5, default="no", blank=True, null=True)
workflow_id = models.CharField(max_length=12, blank=True, null=True)
inputs = models.JSONField(null=True, blank=True)
outputs = models.JSONField(null=True, blank=True)
skip = models.BooleanField(default=False)
creationTime = models.DateTimeField(default=datetime.utcnow, blank=True) creationTime = models.DateTimeField(default=datetime.utcnow, blank=True)
new_status = models.CharField(max_length=50, default="defined", null=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") 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): def __str__(self):
return str(self.id) return str(self.id)
# 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('task-detail-view-api', kwargs={'pk': self.pk})
class Status(models.Model): class Status(models.Model):
name = models.CharField(max_length=50, default="unknown") name = models.CharField(max_length=50, default="unknown")
timestamp = models.DateTimeField(default=datetime.utcnow, blank=True) timestamp = models.DateTimeField(default=datetime.utcnow, blank=True)
taskObject = models.ForeignKey(TaskObject, related_name='status_history', on_delete=models.CASCADE, null=False) task = models.ForeignKey(Task, related_name='status_history', on_delete=models.CASCADE, null=False)
@property @property
def property_taskID(self): def property_taskID(self):
return self.taskObject.taskID return self.task.taskID
@property @property
def property_task_type(self): def property_task_type(self):
return self.taskObject.task_type return self.task.task_type
# the representation of the value in the REST API # the representation of the value in the REST API
def __str__(self): def __str__(self):
formatedDate = self.timestamp.strftime(datetime_format_string) formatedDate = self.timestamp.strftime(datetime_format_string)
return str(self.name)+' ('+str(formatedDate)+')' 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
def __str__(self):
return str(self.taskID)
from rest_framework import serializers from rest_framework import serializers
from .models import Observation, Status, TaskObject from .models import Status, Task, Workflow, LogEntry
import logging
class TaskSerializer(serializers.ModelSerializer):
status_history = serializers.StringRelatedField(
many=True,
required=False,
)
class Meta:
model = Task
fields = ('id','task_type','taskID',
'project','sas_id','priority','purge_policy','skip','workflow_id',
'my_status','new_status',
'inputs','outputs','status_history')
logger = logging.getLogger(__name__)
class StatusSerializer(serializers.ModelSerializer): class StatusSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Status model = Status
fields = ('id','name','timestamp','property_taskID','property_task_type') fields = "__all__"
class ObservationSerializer(serializers.ModelSerializer): class WorkflowSerializer(serializers.ModelSerializer):
status_history = serializers.StringRelatedField(
many=True,
required=False,
)
class Meta: class Meta:
model = Observation model = Workflow
fields = ('id','task_type', 'name','taskID', fields = "__all__"
'field_name','field_ra','field_dec',
'creationTime','starttime','endtime', 'duration', 'size',
'my_status','new_status','status_history','generated_dataproducts',
'data_location', 'node',
'skip_auto_ingest','observing_mode',
'quality','metadata','progress')
class LogEntrySerializer(serializers.ModelSerializer):
class Meta:
model = LogEntry
fields = "__all__"
\ No newline at end of file
...@@ -8,7 +8,7 @@ import time ...@@ -8,7 +8,7 @@ import time
import datetime import datetime
import logging import logging
from .common import timeit from .common import timeit
from ..models import Observation from ..models import Task
DATE_FORMAT = "%Y-%m-%d" DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%Y-%m-%d %H:%M:%SZ" TIME_FORMAT = "%Y-%m-%d %H:%M:%SZ"
...@@ -16,45 +16,10 @@ DJANGO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" ...@@ -16,45 +16,10 @@ DJANGO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_delta_in_minutes(timestamp1,timestamp2):
"""
return minutes between 2 dates
:param timestamp1:
:param timestamp2:
:return:
"""
date1 = datetime.datetime.strptime(timestamp1, TIME_FORMAT)
date2 = datetime.datetime.strptime(timestamp2, TIME_FORMAT)
minutes = (date2 - date1).total_seconds() / 60.0
return int(minutes)
def get_minutes_left(target_time):
"""
Determine how many minutes is left until the target_time is reached
:param target_timestring: The target time defined as string "YYYY-MM-DDThh:mm:ss"
:return: minutes left
A negative number of minutes means that the current time has already reached the target_time,
there is no time left.
A positive number of minutes means that there is still time left
"""
now = datetime.datetime.utcnow()
# convert the time to the same formats
target_time = datetime.datetime.strptime(target_time.strftime('%Y-%m-%d %H:%M:%SZ'), TIME_FORMAT)
# Convert to Unix timestamp
d1_ts = time.mktime(now.timetuple())
d2_ts = time.mktime(target_time.timetuple())
minutes_left = round(((d2_ts - d1_ts) / 60), 2)
return minutes_left
@timeit @timeit
def get_next_taskid(timestamp, taskid_postfix): def get_next_taskid(timestamp, taskid_postfix):
""" """
get the observation with a starttime closest to now. generate a new taskid based on timestamp, with an optional postfix
example url to directly access this function from the REST API:
/atdb/get_next_observation?my_status=scheduled&observing_mode=imaging
:param timestamp: timestamp on which the taskid is based :param timestamp: timestamp on which the taskid is based
:param taskid_postfix: optional addition to the tasked, like 190405001_IMG :param taskid_postfix: optional addition to the tasked, like 190405001_IMG
:return: taskid :return: taskid
...@@ -74,7 +39,7 @@ def get_next_taskid(timestamp, taskid_postfix): ...@@ -74,7 +39,7 @@ def get_next_taskid(timestamp, taskid_postfix):
# check if this taskid already exists. If it does, increase the counter and try again # check if this taskid already exists. If it does, increase the counter and try again
logger.info('checking taskid ' + str(taskid) + '..') logger.info('checking taskid ' + str(taskid) + '..')
found = Observation.objects.filter(taskID=taskid).count() found = Task.objects.filter(taskID=taskid).count()
if found==0: if found==0:
return taskid return taskid
...@@ -82,36 +47,4 @@ def get_next_taskid(timestamp, taskid_postfix): ...@@ -82,36 +47,4 @@ def get_next_taskid(timestamp, taskid_postfix):
return -1 return -1
@timeit
def get_next_observation(my_status, observing_mode, datawriter):
"""
get the observation with a starttime closest to now
example url to directly access this function from the REST API:
/atdb/get_next_observation?my_status=scheduled&observing_mode=imaging
:param my_status: status to search for (probably 'scheduled'
:param observing_mode: imaging or arts
:param datawriter: wcudata1 or wcudata2 (to be implemented later)
:return:
"""
logger.info("get_next_observation("+my_status+','+observing_mode+','+str(datawriter)+")")
# query the Observations table
# disregard the observations which starttime are more than 1 minute in the past
# nowisch = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
try:
# observations = Observation.objects.filter(my_status=my_status).filter(observing_mode__icontains=observing_mode)
# nv: this should be all to enable datawriter2, but test first before activating
observations = Observation.objects.filter(my_status=my_status).filter(observing_mode__icontains=observing_mode).filter(data_location__icontains=datawriter)
# extract the earliest observation and its relevant properties to return to the view
earliest_observation = observations.earliest('starttime')
taskID = earliest_observation.taskID
minutes_left = get_minutes_left(earliest_observation.starttime)
except:
taskID = None
minutes_left = None
return taskID,minutes_left
...@@ -5,7 +5,7 @@ from django.core.signals import request_started, request_finished ...@@ -5,7 +5,7 @@ from django.core.signals import request_started, request_finished
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from taskdatabase.models import TaskObject, Observation, Status from taskdatabase.models import Task, Status
from . import jobs from . import jobs
""" """
...@@ -28,9 +28,9 @@ def request_finished_handler(sender, **kwargs): ...@@ -28,9 +28,9 @@ def request_finished_handler(sender, **kwargs):
#--- Task signals------------- #--- Task signals-------------
@receiver(pre_save, sender=Observation) @receiver(pre_save, sender=Task)
def pre_save_observation_handler(sender, **kwargs): def pre_save_task_handler(sender, **kwargs):
logger.info("SIGNAL : pre_save Observation(" + str(kwargs.get('instance')) + ")") logger.info("SIGNAL : pre_save Task(" + str(kwargs.get('instance')) + ")")
handle_pre_save(sender, **kwargs) handle_pre_save(sender, **kwargs)
...@@ -56,7 +56,7 @@ def handle_pre_save(sender, **kwargs): ...@@ -56,7 +56,7 @@ def handle_pre_save(sender, **kwargs):
myTaskObject.my_status = new_status myTaskObject.my_status = new_status
# add the new to the status history by brewing a status object out of it # add the new to the status history by brewing a status object out of it
myStatus = Status(name=new_status, taskObject=myTaskObject) myStatus = Status(name=new_status, task=myTaskObject)
myStatus.save() myStatus.save()
# temporarily disconnect the post_save handler to save the dataproduct (again) and avoiding recursion. # temporarily disconnect the post_save handler to save the dataproduct (again) and avoiding recursion.
...@@ -71,15 +71,15 @@ def handle_pre_save(sender, **kwargs): ...@@ -71,15 +71,15 @@ def handle_pre_save(sender, **kwargs):
jobs.dispatchJob(myTaskObject, new_status) jobs.dispatchJob(myTaskObject, new_status)
@receiver(post_save, sender=Observation) @receiver(post_save, sender=Task)
def post_save_observation_handler(sender, **kwargs): def post_save_task_handler(sender, **kwargs):
#logger.info("SIGNAL : post_save Observation(" + str(kwargs.get('instance')) + ")") #logger.info("SIGNAL : post_save Task(" + str(kwargs.get('instance')) + ")")
handle_post_save(sender, **kwargs) handle_post_save(sender, **kwargs)
def handle_post_save(sender, **kwargs): def handle_post_save(sender, **kwargs):
""" """
pre_save handler for both Observation and Dataproduct. To create and write its initial status pre_save handler for both Task. To create and write its initial status
:param (in) sender: The model class that sends the trigger :param (in) sender: The model class that sends the trigger
:param (in) kwargs: The instance of the object that sends the trigger. :param (in) kwargs: The instance of the object that sends the trigger.
""" """
...@@ -94,7 +94,7 @@ def handle_post_save(sender, **kwargs): ...@@ -94,7 +94,7 @@ def handle_post_save(sender, **kwargs):
myTaskObject.my_status = myTaskObject.new_status myTaskObject.my_status = myTaskObject.new_status
# add the new to the status history by brewing a status object out of it # add the new to the status history by brewing a status object out of it
myStatus = Status(name=myTaskObject.new_status, taskObject=myTaskObject) myStatus = Status(name=myTaskObject.new_status, task=myTaskObject)
myStatus.save() myStatus.save()
# temporarily disconnect the post_save handler to save the dataproduct (again) and avoiding recursion. # temporarily disconnect the post_save handler to save the dataproduct (again) and avoiding recursion.
...@@ -106,10 +106,10 @@ def handle_post_save(sender, **kwargs): ...@@ -106,10 +106,10 @@ def handle_post_save(sender, **kwargs):
def connect_signals(): def connect_signals():
#logger.info("connect_signals") #logger.info("connect_signals")
pre_save.connect(pre_save_observation_handler, sender=Observation) pre_save.connect(pre_save_task_handler, sender=Task)
post_save.connect(post_save_observation_handler, sender=Observation) post_save.connect(post_save_task_handler, sender=Task)
def disconnect_signals(): def disconnect_signals():
#logger.info("disconnect_signals") #logger.info("disconnect_signals")
pre_save.disconnect(pre_save_observation_handler, sender=Observation) pre_save.disconnect(pre_save_task_handler, sender=Task)
post_save.disconnect(post_save_observation_handler, sender=Observation) post_save.disconnect(post_save_task_handler, sender=Task)
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
<div class="container-fluid details-container"> <div class="container-fluid details-container">
<h4>Observations</h4> <h4>Tasks</h4>
{% include 'taskdatabase/pagination.html' %} {% include 'taskdatabase/pagination.html' %}
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12 col-lg-12"> <div class="col-sm-12 col-md-12 col-lg-12">
{% if my_observations %} {% if my_tasks %}
<div class="panel panel-success"> <div class="panel panel-success">
<div class="panel-body"> <div class="panel-body">
...@@ -18,24 +18,23 @@ ...@@ -18,24 +18,23 @@
<thead> <thead>
<tr> <tr>
<th width="3%">TaskID</th> <th width="3%">TaskID</th>
<th width="10%">Observing Mode</th> <th width="10%">task_type</th>
<th>Name</th> <th>Project</th>
<th>Status</th> <th>Status</th>
<th>Created</th> <th>Created</th>
<th>Stop Time</th>
<th>Size</th>
<th>Quality</th> <th>Quality</th>
<th>Size</th>
<th>Details</th> <th>Details</th>
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% include 'taskdatabase/observations.html' %} {% include 'taskdatabase/tasks.html' %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p>No recent Observations.</p> <p>No recent Tasks.</p>
{% endif %} {% endif %}
</div> </div>
...@@ -45,7 +44,7 @@ ...@@ -45,7 +44,7 @@
</div> </div>
{% include 'taskdatabase/pagination.html' %} {% include 'taskdatabase/pagination.html' %}
</div> </div>
<p class="footer"> Version 1.0.0 (14 jan 2021 - 11:30) <p class="footer"> Version 1.0.0 (15 jan 2021 - 17:30)
<script type="text/javascript"> <script type="text/javascript">
(function(seconds) { (function(seconds) {
var refresh, var refresh,
......
{% load static %}
{% for observation in my_observations %}
{% if observation.my_status != "removed_invisible" %}
<div class="row">
<tr class="{{ observation.my_status }}">
<td>
<a href="{{ observation.get_absolute_url }}" target="_blank">{{ observation.taskID }} </a>
</td>
<td>
{{ observation.observing_mode }}
{% if not observation.science_observation %}
&nbsp;(sys)
{% endif %}
</td>
<td>{{ observation.name }}</td>
<td>
{% if observation.my_status != "valid_priority" %}
{{ observation.my_status }}
{% endif %}
{% if observation.my_status == "valid_priority" %}
valid (priority)
{% endif %}
{% if observation.my_status == "ingesting" %}
{% if observation.control_parameters != "unknown" %}
{{ observation.control_parameters }}
{% endif %}
{% endif %}
</td>
<td>{{ observation.creationTime|date:"Y-m-d H:i:s" }} </td>
<td>{{ observation.endtime|date:"Y-m-d H:i:s" }} </td>
<td>{{ observation.size|filesizeformat }} </td>
<td>
{% if "removed" in observation.my_status and observation.quality != "unknown" %}
{{ observation.quality }}
{% endif %}
</td>
<td>
{% if observation.my_status == "ingesting" %}
<a href="https://alta.astron.nl/science/monitor" class="btn btn-primary btn-sm" target="_blank" role="button">Monitor</a>
{% endif %}
{% if not "bad" in observation.quality %}
{% if observation.my_status == "archived" or observation.my_status == "removed" %}
<a href="https://alta.astron.nl/science/details/{{ observation.taskID }}" class="btn btn-primary btn-sm" target="_blank" role="button">Monitor</a>
{% endif %}
{% endif %}
</td>
<td>
{% if observation.my_status == "defined" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'scheduled' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Schedule</a>
{% endif %}
{% if observation.my_status == "scheduled" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'defined' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Unschedule</a>
{% if not observation.skip_auto_ingest %}
<a href="{% url 'observation-skipautoingest-view' observation.pk 'true' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Skip Ingest</a>
{% endif %}
{% endif %}
{% if observation.my_status == "completed" or observation.my_status == "incomplete" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'valid' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Ready to Ingest</a>
{% endif %}
{% if observation.my_status == "error (no completing)" or observation.my_status == "defined" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'completing' my_observations.number %}" class="btn btn-success btn-sm" role="button">Completing</a>
{% endif %}
{% if observation.my_status == "valid" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'valid_priority' my_observations.number %}" class="btn btn-warning btn-sm" role="button">Priority Ingest</a>
<a href="{% url 'observation-setstatus-view' observation.pk 'completed' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Cancel Ingest</a>
{% endif %}
{% if observation.my_status == "valid_priority" %}
<a href="{% url 'observation-setstatus-view' observation.pk 'valid' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Cancel Priority</a>
<a href="{% url 'observation-setstatus-view' observation.pk 'completed' my_observations.number %}" class="btn btn-primary btn-sm" role="button">Cancel Ingest</a>
{% endif %}
{% if observation.my_status == "archived" %}
<a href="{% url 'observation-setquality-view' observation.pk 'data_is_good' my_observations.number %}" class="btn btn-success btn-sm" role="button">Data is Good</a>
<a href="{% url 'observation-setquality-view' observation.pk 'data_is_bad' my_observations.number %}" class="btn btn-danger btn-sm" role="button">Data is Bad</a>
{% endif %}
{% if observation.my_status == "removed" and observation.quality == "unknown" %}
<a href="{% url 'observation-setquality-view' observation.pk 'data_is_good' my_observations.number %}" class="btn btn-success btn-sm" role="button">Data is Good</a>
<a href="{% url 'observation-setquality-view' observation.pk 'data_is_bad' my_observations.number %}" class="btn btn-danger btn-sm" role="button">Data is Bad</a>
{% endif %}
</td>
</tr>
</div>
{% endif %}
{% endfor %}
{% load bootstrap_pagination %} {% load bootstrap_pagination %}
<div> <div>
{% bootstrap_paginate my_observations range=20 show_prev_next="true" show_first_last="true" previous_label="Previous" first_label="First" next_label="Next" last_label="Last" %} {% bootstrap_paginate my_tasks range=20 show_prev_next="true" show_first_last="true" previous_label="Previous" first_label="First" next_label="Next" last_label="Last" %}
</div> </div>
\ No newline at end of file
{% load static %}
{% for task in my_tasks %}
{% if task.my_status != "removed_invisible" %}
<div class="row">
<tr class="{{ task.my_status }}">
<td>
<a href="{{ task.get_absolute_url }}" target="_blank">{{ task.taskID }} </a>
</td>
<td>
{{ task.task_type }}
</td>
<td>{{ task.project }}</td>
<td>
{% if task.my_status != "valid_priority" %}
{{ task.my_status }}
{% endif %}
{% if task.my_status == "valid_priority" %}
valid (priority)
{% endif %}
{% if task.my_status == "ingesting" %}
{% if task.control_parameters != "unknown" %}
{{ task.control_parameters }}
{% endif %}
{% endif %}
</td>
<td>{{ task.creationTime|date:"Y-m-d H:i:s" }} </td>
<td>{{ task.endtime|date:"Y-m-d H:i:s" }} </td>
<td>{{ task.size|filesizeformat }} </td>
<td>
{% if "removed" in task.my_status and task.quality != "unknown" %}
{{ task.quality }}
{% endif %}
</td>
<td>
{% if task.my_status == "defined" %}
<a href="{% url 'task-setstatus-view' task.pk 'scheduled' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Schedule</a>
{% endif %}
{% if task.my_status == "scheduled" %}
<a href="{% url 'task-setstatus-view' task.pk 'defined' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Unschedule</a>
{% if not task.skip_auto_ingest %}
<a href="{% url 'task-skipautoingest-view' task.pk 'true' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Skip Ingest</a>
{% endif %}
{% endif %}
{% if task.my_status == "completed" or task.my_status == "incomplete" %}
<a href="{% url 'task-setstatus-view' task.pk 'valid' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Ready to Ingest</a>
{% endif %}
{% if task.my_status == "error (no completing)" or task.my_status == "defined" %}
<a href="{% url 'task-setstatus-view' task.pk 'completing' my_tasks.number %}" class="btn btn-success btn-sm" role="button">Completing</a>
{% endif %}
{% if task.my_status == "valid" %}
<a href="{% url 'task-setstatus-view' task.pk 'valid_priority' my_tasks.number %}" class="btn btn-warning btn-sm" role="button">Priority Ingest</a>
<a href="{% url 'task-setstatus-view' task.pk 'completed' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Cancel Ingest</a>
{% endif %}
{% if task.my_status == "valid_priority" %}
<a href="{% url 'task-setstatus-view' task.pk 'valid' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Cancel Priority</a>
<a href="{% url 'task-setstatus-view' task.pk 'completed' my_tasks.number %}" class="btn btn-primary btn-sm" role="button">Cancel Ingest</a>
{% endif %}
{% if task.my_status == "archived" %}
<a href="{% url 'task-setquality-view' task.pk 'data_is_good' my_tasks.number %}" class="btn btn-success btn-sm" role="button">Data is Good</a>
<a href="{% url 'task-setquality-view' task.pk 'data_is_bad' my_tasks.number %}" class="btn btn-danger btn-sm" role="button">Data is Bad</a>
{% endif %}
{% if task.my_status == "removed" and task.quality == "unknown" %}
<a href="{% url 'task-setquality-view' task.pk 'data_is_good' my_tasks.number %}" class="btn btn-success btn-sm" role="button">Data is Good</a>
<a href="{% url 'task-setquality-view' task.pk 'data_is_bad' my_tasks.number %}" class="btn btn-danger btn-sm" role="button">Data is Bad</a>
{% endif %}
</td>
</tr>
</div>
{% endif %}
{% endfor %}
...@@ -4,19 +4,18 @@ from . import views ...@@ -4,19 +4,18 @@ from . import views
urlpatterns = [ urlpatterns = [
# --- GUI --- # --- GUI ---
# ex: /atdb/
#path('', views.index, name='index'),
path('', views.IndexView.as_view(), name='index'), path('', views.IndexView.as_view(), name='index'),
# --- REST API --- # --- REST API ---
# ex: /atdb/observations/ path('tasks/', views.TaskListViewAPI.as_view()),
path('observations/', views.ObservationListViewAPI.as_view()), path('tasks/<int:pk>/', views.TaskDetailsViewAPI.as_view(), name='task-detail-view-api'),
# ex: /atdb/observations_unpaginated/ path('workflows/', views.WorkflowListViewAPI.as_view()),
path('observations_unpaginated/', views.ObservationListUnpaginatedViewAPI.as_view()), path('workflows/<int:pk>/', views.WorkflowDetailsViewAPI.as_view(), name='workflow-detail-view-api'),
# ex: /atdb/observations/5/ path('logentries/', views.LogEntryListViewAPI.as_view()),
path('observations/<int:pk>/', views.ObservationDetailsViewAPI.as_view(),name='observation-detail-view-api'), path('logentries/<int:pk>/', views.LogEntryDetailsViewAPI.as_view(), name='logentry-detail-view-api'),
# --- custom requests --- # --- custom requests ---
# ex: /atdb/get_next_taskid?timestamp=2019-04-05 # ex: /atdb/get_next_taskid?timestamp=2019-04-05
...@@ -24,24 +23,12 @@ urlpatterns = [ ...@@ -24,24 +23,12 @@ urlpatterns = [
views.GetNextTaskIDView.as_view(), views.GetNextTaskIDView.as_view(),
name='get-next-taskid-view'), name='get-next-taskid-view'),
# ex: /atdb/get_next_observation?my_status=scheduled&observing_mode=imaging
path('get_next_observation',
views.GetNextObservationView.as_view(),
name='get-next-observation-view'),
# --- controller resources --- # --- controller resources ---
path('observations/<int:pk>/setstatus/<new_status>/<page>', path('tasks/<int:pk>/setstatus/<new_status>/<page>',
views.ObservationSetStatus, views.TaskSetStatus,
name='observation-setstatus-view'), name='task-setstatus-view'),
# set the quality field to 'good' or 'bad' (and transmit it to ALTA) path('tasks/<int:pk>/skip/<skip_it>/<page>',
path('observations/<int:pk>/setquality/<quality>/<page>', views.Skip,
views.ObservationSetQuality, name='task-skip-view'),
name='observation-setquality-view'),
path('observations/<int:pk>/setdatawriter/<datawriter>/<page>',
views.SetDatawriter,
name='observation-setdatawriter-view'),
path('observations/<int:pk>/skipautoingest/<skip_it>/<page>',
views.SkipAutoIngest,
name='observation-skipautoingest-view'),
] ]
\ No newline at end of file
...@@ -9,274 +9,209 @@ from django.views.generic import ListView ...@@ -9,274 +9,209 @@ from django.views.generic import ListView
from rest_framework import generics, pagination from rest_framework import generics, pagination
from rest_framework.response import Response from rest_framework.response import Response
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from django.template import loader
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Observation, Status from .models import Task, Status, Workflow, LogEntry
from django.db.models import Q from django.db.models import Q
from .serializers import ObservationSerializer, StatusSerializer from .serializers import TaskSerializer, WorkflowSerializer, LogEntrySerializer
from .forms import FilterForm from .forms import FilterForm
from .services import algorithms from .services import algorithms
from .services.common import timeit
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# ---------- filters (in the REST API) --------- # ---------- filters (in the REST API) ---------
# example: /atdb/observations/?observing_mode__icontains=arts class TaskFilter(filters.FilterSet):
class ObservationFilter(filters.FilterSet):
# A direct filter on a @property field is not possible, this simulates that behaviour
class Meta: class Meta:
model = Observation model = Task
fields = { fields = {
'observing_mode': ['exact', 'in', 'icontains', 'startswith'], # /atdb/observations/?observing_mode__icontains=arts 'project': ['exact', 'icontains'],
'field_ra': ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'], 'sas_id': ['exact', 'icontains'],
'field_dec': ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'], 'my_status': ['exact', 'icontains', 'in', 'startswith'],
'name': ['exact', 'icontains'],
'my_status': ['exact', 'icontains', 'in', 'startswith'], #/atdb/observations?&my_status__in=archived,removing
'taskID': ['gt', 'lt', 'gte', 'lte','exact', 'icontains', 'startswith','in'], 'taskID': ['gt', 'lt', 'gte', 'lte','exact', 'icontains', 'startswith','in'],
'creationTime' : ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'], 'purge_policy': ['exact'],
'starttime' : ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'], 'priority': ['exact'],
'endtime': ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'],
'data_location': ['exact', 'icontains'],
'node': ['exact', 'in'],
# 'metadata': ['icontains'],
'skip_auto_ingest': ['exact'],
'quality': ['exact', 'icontains'],
} }
class WorkflowFilter(filters.FilterSet):
# example: has 1811130001 been on 'running?'
# http://localhost:8000/atdb/status/?&taskID=181130001&name=running
class StatusFilter(filters.FilterSet):
# A direct filter on a @property field is not possible, this simulates that behaviour
taskID = filters.Filter(field_name="taskObject__taskID",lookup_expr='exact')
class Meta: class Meta:
model = Status model = Workflow
# https://django-filter.readthedocs.io/en/master/ref/filters.html?highlight=exclude
fields = { fields = {
#'taskid': ['exact', 'in'], 'repository': ['exact', 'icontains'],
'name': ['exact', 'in'], 'commit_id': ['exact', 'icontains'],
'timestamp': ['gt', 'lt', 'gte', 'lte', 'contains', 'exact'], 'path': ['exact', 'icontains'],
'taskObject__taskID': ['exact', 'in'],
# 'taskID': ['exact', 'in'],
} }
# this uses a form
def do_filter(request):
if request.method == 'GET': class LogEntryFilter(filters.FilterSet):
# create a form instance and populate it with data from the request:
form = FilterForm(request.POST)
if form.is_valid():
status = form.cleaned_data.get('status')
#observations = get_filtered_observations()
return HttpResponseRedirect('/atdb/')
else:
form = FilterForm
return render(request, 'taskdatabase/index.html', {'my_form': form}) class Meta:
model = LogEntry
fields = {
'step_name': ['exact', 'icontains'],
}
# ---------- GUI Views ----------- # ---------- GUI Views -----------
# http://localhost:8000/atdb/
# calling this view renders the index.html template in the GUI (the observation list)
# http://localhost:8000/atdb/query?my_status=removed
# http://localhost:8000/atdb/query?not_my_status=removed
class IndexView(ListView): class IndexView(ListView):
""" """
This is the main view of ATDB. It shows a pagination list of observations, sorted by creationTime. This is the main view of ATDB. It shows a pagination list of tasks, sorted by creationTime.
""" """
template_name = 'taskdatabase/index.html' template_name = 'taskdatabase/index.html'
# by default this returns the list in an object called object_list, so use 'object_list' in the html page. # by default this returns the list in an object called object_list, so use 'object_list' in the html page.
# but if 'context_object_name' is defined, then this returned list is named and can be accessed that way in html. # but if 'context_object_name' is defined, then this returned list is named and can be accessed that way in html.
context_object_name = 'my_observations' context_object_name = 'my_tasks'
def get_queryset(self): def get_queryset(self):
#observations = Observation.objects.order_by('-creationTime')
#observations = get_filtered_observations()
my_status = self.request.GET.get('my_status') my_status = self.request.GET.get('my_status')
not_my_status = self.request.GET.get('not_my_status') not_my_status = self.request.GET.get('not_my_status')
search_box = self.request.GET.get('search_box', None) search_box = self.request.GET.get('search_box', None)
if (search_box is not None): if (search_box is not None):
observations = get_searched_observations(search_box) tasks = get_searched_tasks(search_box)
else: else:
#observations = Observation.objects.order_by('-taskID') tasks = Task.objects.order_by('-creationTime')
observations = Observation.objects.order_by('-creationTime')
if (my_status is not None): if (my_status is not None):
observations = get_filtered_observations(my_status) tasks = get_filtered_tasks(my_status)
if (not_my_status is not None): if (not_my_status is not None):
observations = get_unfiltered_observations(not_my_status) tasks = get_unfiltered_tasks(not_my_status)
paginator = Paginator(observations, config.OBSERVATIONS_PER_PAGE) # Show 50 observations per page paginator = Paginator(tasks, config.TASKS_PER_PAGE) # Show 50 tasks per page
page = self.request.GET.get('page') page = self.request.GET.get('page')
try: try:
observations = paginator.page(page) tasks = paginator.page(page)
except PageNotAnInteger: except PageNotAnInteger:
# If page is not an integer, deliver first page. # If page is not an integer, deliver first page.
observations = paginator.page(1) tasks = paginator.page(1)
except EmptyPage: except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results. # If page is out of range (e.g. 9999), deliver last page of results.
observations = paginator.page(paginator.num_pages) tasks = paginator.page(paginator.num_pages)
return observations return tasks
# an attempt to get a filtering mechanism in the GUI # an attempt to get a filtering mechanism in the GUI
# filter on a single status # filter on a single status
# http://localhost:8000/atdb/query?my_status=scheduled # http://localhost:8000/atdb/query?my_status=scheduled
def get_filtered_observations(my_status): def get_filtered_tasks(my_status):
q = Observation.objects.order_by('-creationTime') q = Task.objects.order_by('-creationTime')
q = q.filter(my_status=my_status) q = q.filter(my_status=my_status)
#q = q.exclude(my_status__icontains='removed') #q = q.exclude(my_status__icontains='removed')
return q return q
# http://localhost:8000/atdb/query?not_my_status=removed # http://localhost:8000/atdb/query?not_my_status=removed
def get_unfiltered_observations(my_status): def get_unfiltered_tasks(my_status):
q = Observation.objects.order_by('-creationTime') q = Task.objects.order_by('-creationTime')
q = q.exclude(my_status=my_status) q = q.exclude(my_status=my_status)
return q return q
def get_searched_observations(search): def get_searched_tasks(search):
observations = Observation.objects.filter( tasks = Task.objects.filter(
Q(taskID__contains=search) | Q(taskID__contains=search) |
Q(observing_mode__icontains=search) | Q(sas_id__contains=search) |
Q(task_type__icontains=search) |
Q(my_status__icontains=search) | Q(my_status__icontains=search) |
Q(field_name__icontains=search)).order_by('-creationTime') Q(project__icontains=search)).order_by('-creationTime')
return observations return tasks
# ---------- REST API views ----------- # ---------- REST API views -----------
# example: /atdb/tasks/
# example: /atdb/observations/ class TaskListViewAPI(generics.ListCreateAPIView):
# calling this view serializes the observations list in a REST API
class ObservationListViewAPI(generics.ListCreateAPIView):
""" """
A pagination list of observations, unsorted. A pagination list of tasks, unsorted.
""" """
model = Observation model = Task
queryset = Observation.objects.all() queryset = Task.objects.all()
serializer_class = ObservationSerializer serializer_class = TaskSerializer
# using the Django Filter Backend - https://django-filter.readthedocs.io/en/latest/index.html # using the Django Filter Backend - https://django-filter.readthedocs.io/en/latest/index.html
filter_backends = (filters.DjangoFilterBackend,) filter_backends = (filters.DjangoFilterBackend,)
filter_class = ObservationFilter filter_class = TaskFilter
class ObservationListUnpaginated(pagination.PageNumberPagination):
page_size = 10000
# example: /atdb/observations_unpaginated/ # example: /atdb/tasks/5/
# calling this view serializes the observations list in a REST API # calling this view serializes a task in the REST API
class ObservationListUnpaginatedViewAPI(generics.ListCreateAPIView): class TaskDetailsViewAPI(generics.RetrieveUpdateDestroyAPIView):
""" """
A pagination list of observations, unsorted. Detailed view of a task.
""" """
model = Observation model = Task
queryset = Observation.objects.all() queryset = Task.objects.all()
serializer_class = ObservationSerializer serializer_class = TaskSerializer
pagination_class = ObservationListUnpaginated
# using the Django Filter Backend - https://django-filter.readthedocs.io/en/latest/index.html
filter_backends = (filters.DjangoFilterBackend,)
filter_class = ObservationFilter
# example: /atdb/workflows/
class WorkflowListViewAPI(generics.ListCreateAPIView):
model = Workflow
queryset = Workflow.objects.all()
serializer_class = WorkflowSerializer
# example: /atdb/observations/5/ filter_backends = (filters.DjangoFilterBackend,)
# calling this view serializes an observation in the REST API filter_class = WorkflowFilter
class ObservationDetailsViewAPI(generics.RetrieveUpdateDestroyAPIView):
"""
Detailed view of an observation.
"""
model = Observation
queryset = Observation.objects.all()
serializer_class = ObservationSerializer
# --- controller resources, triggered by a button in the GUI or directoy with a URL --- # example: /atdb/workflows/5/
# set observation status to 'new_status' - called from the GUI class WorkflowDetailsViewAPI(generics.RetrieveUpdateDestroyAPIView):
# example: 'Schedule', 'Unschedule', 'Ready to Ingest', 'Remove Data' model = Workflow
queryset = Workflow.objects.all()
serializer_class = WorkflowSerializer
def ObservationSetQuality(request,pk,quality,page):
model = Observation
observation = Observation.objects.get(pk=pk)
observation.quality = quality
# send quality to ALTA # example: /atdb/logentries/
#taskid = observation.taskID class LogEntryListViewAPI(generics.ListCreateAPIView):
#result = algorithms.send_quality_to_alta(taskid,quality) model = LogEntry
queryset = LogEntry.objects.all()
serializer_class = LogEntrySerializer
# nv:24jan2020, no longer communicate with ALTA directly, but leave it to a atdb service to pick up. filter_backends = (filters.DjangoFilterBackend,)
observation.quality = quality filter_class = WorkflowFilter
observation.save()
result = "OK"
if result == "OK":
# only save the observation when the update to ALTA has succeeded # example: /atdb/workflows/5/
# removed => removed (bad) or removed (good) # calling this view serializes a task in the REST API
# observation.new_status = observation.my_status+' ('+observation.quality+')' class LogEntryDetailsViewAPI(generics.RetrieveUpdateDestroyAPIView):
"""
Detailed view of a LogEntry.
"""
model = LogEntry
queryset = LogEntry.objects.all()
serializer_class = LogEntrySerializer
observation.save()
return redirect('/atdb/?page='+page)
else:
# todo: some better HTML error handling
return redirect('/atdb/ooops/&error='+result)
# --- controller resources, triggered by a button in the GUI or directoy with a URL ---
# set task status to 'new_status' - called from the GUI
def SkipAutoIngest(request,pk,skip_it,page):
model = Observation
observation = Observation.objects.get(pk=pk)
observation.skip_auto_ingest = (skip_it == 'true')
observation.save() def Skip(request,pk,skip_it,page):
model = Task
task = Task.objects.get(pk=pk)
task.skip = (skip_it == 'true')
task.save()
return redirect('/atdb/?page='+page) return redirect('/atdb/?page='+page)
def ObservationSetStatus(request,pk,new_status,page): def TaskSetStatus(request,pk,new_status,page):
model = Observation model = Task
observation = Observation.objects.get(pk=pk) task = Task.objects.get(pk=pk)
observation.new_status = new_status task.new_status = new_status
observation.save() task.save()
return redirect('/atdb/?page='+page)
# set the datawriter to which the observation will write to.
# /atdb/observations/<int:pk>/setdatawriter/<nr>/<page>
def SetDatawriter(request,pk,datawriter,page):
model = Observation
observation = Observation.objects.get(pk=pk)
# retrieve current data_location
data_location = observation.data_location
params = data_location.split(':')
host = params[0]
path = params[1]
# regardless of the current datawriter, apply the new one
new_data_location = datawriter + ":" + path
observation.data_location = new_data_location
observation.save()
taskid = observation.taskID
return redirect('/atdb/?page='+page) return redirect('/atdb/?page='+page)
# get the next taskid based on starttime and what is currently in the database # get the next taskid based on starttime and what is currently in the database
#/atdb/get_next_taskid?timestamp=2019-04-05 #/atdb/get_next_taskid?timestamp=2019-04-05
class GetNextTaskIDView(generics.ListAPIView): class GetNextTaskIDView(generics.ListAPIView):
queryset = Observation.objects.all() queryset = Task.objects.all()
# override list and generate a custom response # override list and generate a custom response
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
...@@ -302,37 +237,3 @@ class GetNextTaskIDView(generics.ListAPIView): ...@@ -302,37 +237,3 @@ class GetNextTaskIDView(generics.ListAPIView):
}) })
# get the observation with a starttime closest to now.
# /atdb/get_next_observation?my_status=scheduled&observing_mode=imaging
class GetNextObservationView(generics.ListAPIView):
queryset = Observation.objects.all()
# override list and generate a custom response
def list(self, request, *args, **kwargs):
# read the arguments from the query
try:
my_status = self.request.query_params['my_status']
except:
my_status = None
try:
observing_mode = self.request.query_params['observing_mode']
except:
observing_mode = None
try:
datawriter = self.request.query_params['datawriter']
except:
datawriter = None
# call the business logic
taskID, minutes_left = algorithms.get_next_observation(my_status, observing_mode, datawriter)
# return a response
return Response({
'taskID': taskID,
'minutes_left': minutes_left
})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment