From 06993aeb9b253d250f193d7fef4ea55e76da6e9c Mon Sep 17 00:00:00 2001
From: Mattia Mancini <mancini@astron.nl>
Date: Fri, 6 Jul 2018 08:33:11 +0000
Subject: [PATCH] Story LSMR-13: Implementing directory structure to support
 additional models/serializers/viewsets

---
 .gitattributes                                |   9 +
 SAS/LSMR/src/lsmr/lsmrapp/CMakeLists.txt      |   8 +-
 .../src/lsmr/lsmrapp/models/CMakeLists.txt    |  10 ++
 SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py  |   1 +
 .../src/lsmr/lsmrapp/models/specification.py  | 161 ++++++++++++++++++
 .../lsmr/lsmrapp/serializers/CMakeLists.txt   |  11 ++
 .../src/lsmr/lsmrapp/serializers/__init__.py  |   1 +
 .../lsmr/lsmrapp/serializers/specification.py |  43 +++++
 .../src/lsmr/lsmrapp/viewsets/CMakeLists.txt  |  10 ++
 .../src/lsmr/lsmrapp/viewsets/__init__.py     |   1 +
 .../lsmr/lsmrapp/viewsets/specification.py    |  36 ++++
 11 files changed, 287 insertions(+), 4 deletions(-)
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/models/CMakeLists.txt
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/models/specification.py
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/serializers/CMakeLists.txt
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/serializers/__init__.py
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/serializers/specification.py
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/viewsets/CMakeLists.txt
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/viewsets/__init__.py
 create mode 100644 SAS/LSMR/src/lsmr/lsmrapp/viewsets/specification.py

diff --git a/.gitattributes b/.gitattributes
index 0149603c934..eb1a1acda27 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4249,8 +4249,17 @@ SAS/LSMR/src/lsmr/lsmrapp/migrations/0002_auto_20180705_0736.py -text
 SAS/LSMR/src/lsmr/lsmrapp/migrations/CMakeLists.txt -text
 SAS/LSMR/src/lsmr/lsmrapp/migrations/__init__.py -text
 SAS/LSMR/src/lsmr/lsmrapp/models.py -text
+SAS/LSMR/src/lsmr/lsmrapp/models/CMakeLists.txt -text
+SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py -text
+SAS/LSMR/src/lsmr/lsmrapp/models/specification.py -text
 SAS/LSMR/src/lsmr/lsmrapp/serializers.py -text
+SAS/LSMR/src/lsmr/lsmrapp/serializers/CMakeLists.txt -text
+SAS/LSMR/src/lsmr/lsmrapp/serializers/__init__.py -text
+SAS/LSMR/src/lsmr/lsmrapp/serializers/specification.py -text
 SAS/LSMR/src/lsmr/lsmrapp/viewsets.py -text
+SAS/LSMR/src/lsmr/lsmrapp/viewsets/CMakeLists.txt -text
+SAS/LSMR/src/lsmr/lsmrapp/viewsets/__init__.py -text
+SAS/LSMR/src/lsmr/lsmrapp/viewsets/specification.py -text
 SAS/LSMR/src/lsmr/settings.py -text
 SAS/LSMR/src/lsmr/urls.py -text
 SAS/LSMR/src/lsmr/wsgi.py -text
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/CMakeLists.txt b/SAS/LSMR/src/lsmr/lsmrapp/CMakeLists.txt
index eb5a82448aa..06facde1f65 100644
--- a/SAS/LSMR/src/lsmr/lsmrapp/CMakeLists.txt
+++ b/SAS/LSMR/src/lsmr/lsmrapp/CMakeLists.txt
@@ -5,13 +5,13 @@ set(_py_files
     __init__.py
     admin.py
     apps.py
-    models.py
-    viewsets.py
-    serializers.py
     )
 
 python_install(${_py_files}
     DESTINATION lofar/sas/lsmr/lsmr/lsmrapp)
 
 
-add_subdirectory(migrations)
\ No newline at end of file
+add_subdirectory(migrations)
+add_subdirectory(models)
+add_subdirectory(serializers)
+add_subdirectory(viewsets)
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/models/CMakeLists.txt b/SAS/LSMR/src/lsmr/lsmrapp/models/CMakeLists.txt
new file mode 100644
index 00000000000..ddb94230c05
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/models/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+include(PythonInstall)
+
+set(_py_files
+    __init__.py
+    specification.py
+    )
+
+python_install(${_py_files}
+    DESTINATION lofar/sas/lsmr/lsmr/lsmrapp/models)
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py b/SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py
new file mode 100644
index 00000000000..9e8eba30f25
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/models/__init__.py
@@ -0,0 +1 @@
+from .specification import *
\ No newline at end of file
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/models/specification.py b/SAS/LSMR/src/lsmr/lsmrapp/models/specification.py
new file mode 100644
index 00000000000..a7c28a13ef2
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/models/specification.py
@@ -0,0 +1,161 @@
+"""
+This file contains the database models
+"""
+
+from django.db.models import Model, CharField, DateTimeField, BooleanField, ForeignKey, CASCADE, Field
+from django.contrib.postgres.fields import ArrayField, JSONField
+from django.contrib.postgres.indexes import GinIndex
+from enum import Enum
+
+
+#
+# Common
+#
+
+# abstract models
+
+class BasicCommon(Model):
+    # todo: we cannot use foreign keys in the array here, so we have to keep the Tags table up to date by trigger or so.
+    # todo: we could switch to a manytomany field instead?
+    tags = ArrayField(CharField(max_length=30), size=8, blank=True)
+    creation = DateTimeField(auto_now_add=True)
+    update = DateTimeField(auto_now=True)
+
+    class Meta:
+        abstract = True
+        indexes = [GinIndex(fields=['tags'])]
+
+
+class UserDefinedCommon(BasicCommon):
+    name = CharField(max_length=30)
+
+    class Meta:
+        abstract = True
+
+
+class SystemDefinedCommon(BasicCommon):
+
+    class Meta:
+        abstract = True
+
+
+# concrete models
+
+class Tags(Model):
+    # todo: figure out how to keep this in sync with tags columns (->BasicCommon)
+    # todo: Or remove this altogether without keeping track of tags?
+    title = CharField(max_length=30)
+    description = CharField(max_length=255)
+
+
+#
+# I/O
+#
+
+# enums
+
+class RoleChoice(Enum):
+    CORRELATOR = "correlator"
+    BEAMFORMER = "beamformer"
+    INSPECTION_PLOTS = "inspection plots"
+    CALIBRATOR = "calibrator"
+    TARGET = "target"
+    INPUT_OUTPUT = "input, output"
+
+
+class DatatypeChoice(Enum):
+    VISIBILITIES = "visibilities"
+    TIME_SERIES = "time series"
+    INSTRUMENT_MODEL = "instrument model"
+    IMAGE = "image"
+    QUALITY = "quality"
+
+
+class DataformatChoice(Enum):
+    MEASUREMENTSET = "MeasurementSet"
+    HDF5 = "HDF5"
+
+
+# todo: fix this!
+#class EnumField(Field):  # todo: Test if CharField works better e.g. for forms
+#    """
+#    Django does not support creating db enums, so we got to do that ourselves here
+#    """
+#    def __init__(self, *args, **kwargs):
+#        super(EnumField, self).__init__(*args, **kwargs)
+#
+#    def db_type(self, connection):
+#        return "enum(%s)" % ','.join("'%s'" % key for (key, _) in self.choices)
+# # todo: unfortunately this won't work for Postgres since a CREATE TYPE is required first...
+
+
+# concrete models
+
+class WorkIORoles(Model):
+    # todo: Choices do not translate to a Postgres ENUM type currently, but are only enforced on the Django level.
+    # todo: If we are not happy with this as is, I see two options:
+    # todo: 1. Wait to see if they implement that EnumField in the meantime (https://code.djangoproject.com/ticket/24342)
+    # todo: 2. Build this ourselves. I tried to achieve that with the EnumField above, but it won't be that easy, I guess
+    role = CharField(
+        max_length=30,
+        choices=[(item.name, item.value) for item in RoleChoice]
+    )
+    datatype = CharField(
+        max_length=30,
+        choices=[(item.name, item.value) for item in DatatypeChoice]
+    )
+    dataformat = ArrayField(CharField(
+        max_length=30,
+        choices=[(item.name, item.value) for item in DataformatChoice]
+    ), size=8, blank=True)
+    outputs = ForeignKey("WorkRequestTemplate", related_name='role_output', on_delete=CASCADE, null=True)
+    inputs = ForeignKey("WorkRequestTemplate", related_name='role_input', on_delete=CASCADE, null=True)
+
+
+#
+# Templates
+#
+
+# abstract models
+
+class Template(SystemDefinedCommon):
+    name = CharField(max_length=30)         # todo: check if this should go in SystemDefinedCommon or BasicCommon
+    description = CharField(max_length=255) # todo: check if this should go in SystemDefinedCommon or BasicCommon
+    default_version = BooleanField()
+    version = CharField(max_length=30)
+    schema = JSONField()
+
+    class Meta:
+        abstract = True
+        #unique_together = ("name", "default_version") # todo: We only want this for default_version=True, find a way to do so
+
+
+# concrete models
+
+class GeneratorTemplate(Template):
+    create_func = CharField(max_length=30)
+
+
+class RunTemplate(Template):
+    pass
+
+
+class WorkRequestTemplate(Template):
+    validation_code_js = CharField(max_length=30)
+    # outputs = ForeignKey("WorkIORoles", related_name='role_output', on_delete=CASCADE)     # todo: removed in latest draft, delete?
+    # inputs = ForeignKey("WorkIORoles", related_name='role_input', on_delete=CASCADE)       # todo: removed in latest draft, delete?
+
+
+class WorkRelationSelectionTemplate(Template):
+    pass
+
+
+
+
+#
+# Instance Objects
+#
+
+# todo: the blue stuff
+# run / work request / work request relation -> SystemDefinedCommon
+# others -> UserDefinedCommon
\ No newline at end of file
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/serializers/CMakeLists.txt b/SAS/LSMR/src/lsmr/lsmrapp/serializers/CMakeLists.txt
new file mode 100644
index 00000000000..c1cdf544c20
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/serializers/CMakeLists.txt
@@ -0,0 +1,11 @@
+
+include(PythonInstall)
+
+set(_py_files
+    __init__.py
+    specification.py
+
+    )
+
+python_install(${_py_files}
+    DESTINATION lofar/sas/lsmr/lsmr/lsmrapp/serializers)
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/serializers/__init__.py b/SAS/LSMR/src/lsmr/lsmrapp/serializers/__init__.py
new file mode 100644
index 00000000000..9e8eba30f25
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/serializers/__init__.py
@@ -0,0 +1 @@
+from .specification import *
\ No newline at end of file
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/serializers/specification.py b/SAS/LSMR/src/lsmr/lsmrapp/serializers/specification.py
new file mode 100644
index 00000000000..9b70a05d1b0
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/serializers/specification.py
@@ -0,0 +1,43 @@
+"""
+This file contains the serializers (for the elsewhere defined data models)
+"""
+
+from rest_framework import serializers
+from lsmr.lsmrapp import models
+
+
+class TagsSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.Tags
+        fields = '__all__'
+
+
+class GeneratorTemplateSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.GeneratorTemplate
+        fields = '__all__'
+
+
+class RunTemplateSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.RunTemplate
+        fields = '__all__'
+
+
+class WorkRequestTemplateSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.WorkRequestTemplate
+        fields = '__all__'
+
+
+class WorkRelationSelectionTemplateSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.WorkRelationSelectionTemplate
+        fields = '__all__'
+
+
+class WorkIORolesSerializer(serializers.HyperlinkedModelSerializer):
+    class Meta:
+        model = models.WorkIORoles
+        fields = '__all__'
+
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/viewsets/CMakeLists.txt b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/CMakeLists.txt
new file mode 100644
index 00000000000..65ce74a51a2
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+include(PythonInstall)
+
+set(_py_files
+    __init__.py
+    specification.py
+    )
+
+python_install(${_py_files}
+    DESTINATION lofar/sas/lsmr/lsmr/lsmrapp/viewsets)
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/viewsets/__init__.py b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/__init__.py
new file mode 100644
index 00000000000..9e8eba30f25
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/__init__.py
@@ -0,0 +1 @@
+from .specification import *
\ No newline at end of file
diff --git a/SAS/LSMR/src/lsmr/lsmrapp/viewsets/specification.py b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/specification.py
new file mode 100644
index 00000000000..ce4aff6ec8c
--- /dev/null
+++ b/SAS/LSMR/src/lsmr/lsmrapp/viewsets/specification.py
@@ -0,0 +1,36 @@
+"""
+This file contains the viewsets (based on the elsewhere defined data models and serializers)
+"""
+
+from rest_framework import viewsets
+from lsmr.lsmrapp import models, serializers
+
+
+class TagsViewSet(viewsets.ModelViewSet):
+    queryset = models.Tags.objects.all()
+    serializer_class = serializers.TagsSerializer
+
+
+class GeneratorTemplateViewSet(viewsets.ModelViewSet):
+    queryset = models.GeneratorTemplate.objects.all()
+    serializer_class = serializers.GeneratorTemplateSerializer
+
+
+class RunTemplateViewSet(viewsets.ModelViewSet):
+    queryset = models.RunTemplate.objects.all()
+    serializer_class = serializers.RunTemplateSerializer
+
+
+class WorkRequestTemplateViewSet(viewsets.ModelViewSet):
+    queryset = models.WorkRequestTemplate.objects.all()
+    serializer_class = serializers.WorkRequestTemplateSerializer
+
+
+class WorkRelationSelectionTemplateViewSet(viewsets.ModelViewSet):
+    queryset = models.WorkRelationSelectionTemplate.objects.all()
+    serializer_class = serializers.WorkRelationSelectionTemplateSerializer
+
+
+class WorkIORolesViewSet(viewsets.ModelViewSet):
+    queryset = models.WorkIORoles.objects.all()
+    serializer_class = serializers.WorkIORolesSerializer
-- 
GitLab