Browse Source

added models and added first authorized getting of model data

Dimitri Korsch 3 years ago
parent
commit
8035b2dbcd

+ 2 - 1
backend/pycs_api/admin.py

@@ -1,3 +1,4 @@
 from django.contrib import admin
+from pycs_api.models import Model
 
-# Register your models here.
+admin.site.register(Model)

+ 118 - 0
backend/pycs_api/migrations/0001_initial.py

@@ -0,0 +1,118 @@
+# Generated by Django 3.2.9 on 2021-11-25 15:56
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import pycs_api.models.file
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='File',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('uuid', models.UUIDField(default=uuid.uuid4)),
+                ('extension', models.CharField(choices=[(pycs_api.models.file.Extensions['JPG'], 'JPG Image'), (pycs_api.models.file.Extensions['JPEG'], 'JPEG Image'), (pycs_api.models.file.Extensions['PNG'], 'PNG Image')], max_length=16)),
+                ('size', models.PositiveIntegerField()),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('path', models.ImageField(upload_to=pycs_api.models.file.project_directory)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Label',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('reference', models.CharField(max_length=255)),
+                ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pycs_api.label')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='LabelProvider',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('description', models.TextField()),
+                ('root_folder', models.CharField(max_length=255, unique=True)),
+                ('configuration_file', models.CharField(max_length=255, unique=True)),
+            ],
+            options={
+                'unique_together': {('root_folder', 'configuration_file')},
+            },
+        ),
+        migrations.CreateModel(
+            name='Model',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('description', models.TextField()),
+                ('root_folder', models.CharField(max_length=255, unique=True)),
+                ('supports_encoded', models.TextField()),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Result',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('type', models.CharField(choices=[('labeled-image', 'Image Label'), ('bounding-box', 'Bounding Box')], max_length=32)),
+                ('origin', models.CharField(choices=[('manual', 'Manual result'), ('computed', 'Automatically generated result')], max_length=32)),
+                ('data_encoded', models.TextField()),
+                ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pycs_api.file')),
+                ('label', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pycs_api.label')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Project',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255)),
+                ('description', models.TextField()),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('root_folder', models.CharField(max_length=255, unique=True)),
+                ('data_folder', models.CharField(max_length=255)),
+                ('external_data', models.BooleanField()),
+                ('label_provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pycs_api.labelprovider')),
+                ('model', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pycs_api.model')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', related_query_name='project', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.AddField(
+            model_name='label',
+            name='project',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pycs_api.project'),
+        ),
+        migrations.AddField(
+            model_name='file',
+            name='project',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pycs_api.project'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='label',
+            unique_together={('project', 'reference')},
+        ),
+        migrations.AlterUniqueTogether(
+            name='file',
+            unique_together={('project', 'path')},
+        ),
+    ]

+ 6 - 3
backend/pycs_api/models/__init__.py

@@ -1,3 +1,6 @@
-from django.db import models
-
-# Create your models here.
+from pycs_api.models.file import File
+from pycs_api.models.label import Label
+from pycs_api.models.label_provider import LabelProvider
+from pycs_api.models.model import Model
+from pycs_api.models.project import Project
+from pycs_api.models.result import Result

+ 15 - 0
backend/pycs_api/models/base.py

@@ -0,0 +1,15 @@
+
+from django.db import models
+
+class BaseModel(models.Model):
+
+    name = models.CharField(max_length=255)
+
+    class Meta:
+        abstract = True
+
+    serializer_fields = [
+        "id",
+        "name",
+    ]
+

+ 50 - 0
backend/pycs_api/models/file.py

@@ -0,0 +1,50 @@
+import uuid
+import enum
+
+from django.db import models
+from pycs_api.models import base
+from pycs_api.models.project import Project
+
+
+class Extensions(enum.Enum):
+    JPG = ".jpg"
+    JPEG = ".jpeg"
+    PNG = ".png"
+
+
+def project_directory(instance: "File", filename: str):
+    return f"uploads/{instance.project.id}/{filename}"
+
+class File(base.BaseModel):
+
+    EXTENSIONS = [
+        (Extensions.JPG, "JPG Image"),
+        (Extensions.JPEG, "JPEG Image"),
+        (Extensions.PNG, "PNG Image"),
+    ]
+
+    uuid = models.UUIDField(
+        default=uuid.uuid4
+    )
+
+    project = models.ForeignKey(
+        Project,
+        on_delete=models.CASCADE,
+    )
+
+    extension = models.CharField(
+        max_length=16,
+        choices=EXTENSIONS
+    )
+
+    size = models.PositiveIntegerField()
+
+    created = models.DateTimeField(auto_now_add=True)
+
+    path = models.ImageField(upload_to=project_directory)
+
+    class Meta:
+        unique_together = [
+            "project",
+            "path"
+        ]

+ 29 - 0
backend/pycs_api/models/label.py

@@ -0,0 +1,29 @@
+from django.db import models
+from pycs_api.models import base
+from pycs_api.models.project import Project
+
+
+class Label(base.BaseModel):
+
+    project = models.ForeignKey(
+        Project,
+        on_delete=models.CASCADE
+    )
+
+    # recursive relation
+    parent = models.ForeignKey(
+        'self',
+        on_delete=models.CASCADE
+    )
+
+    created = models.DateTimeField(auto_now_add=True)
+
+    reference = models.CharField(max_length=255)
+
+
+    class Meta:
+        unique_together = [
+            "project",
+            "reference"
+        ]
+

+ 17 - 0
backend/pycs_api/models/label_provider.py

@@ -0,0 +1,17 @@
+from django.db import models
+from pycs_api.models import base
+
+
+class LabelProvider(base.BaseModel):
+
+    description = models.TextField()
+
+    root_folder = models.CharField(max_length=255, unique=True)
+
+    configuration_file = models.CharField(max_length=255, unique=True)
+
+    class Meta:
+        unique_together = [
+            "root_folder",
+            "configuration_file"
+        ]

+ 17 - 0
backend/pycs_api/models/model.py

@@ -0,0 +1,17 @@
+from django.db import models
+from pycs_api.models import base
+
+
+class Model(base.BaseModel):
+
+    description = models.TextField()
+
+    root_folder = models.CharField(max_length=255, unique=True)
+
+    supports_encoded = models.TextField()
+
+    serializer_fields = base.BaseModel.serializer_fields + [
+        "description",
+        "root_folder",
+        "supports_encoded",
+    ]

+ 38 - 0
backend/pycs_api/models/project.py

@@ -0,0 +1,38 @@
+from django.contrib.auth.models import User
+from django.db import models
+from pycs_api.models import base
+from pycs_api.models.model import Model
+from pycs_api.models.label_provider import LabelProvider
+
+class Project(base.BaseModel):
+
+    user = models.ForeignKey(
+        User,
+        on_delete=models.CASCADE,
+        related_name="projects",
+        related_query_name="project",
+    )
+
+    description = models.TextField()
+
+    created = models.DateTimeField(auto_now_add=True)
+
+    model = models.ForeignKey(
+        Model,
+        null=True,
+        blank=True,
+        on_delete=models.SET_NULL
+    )
+
+    label_provider = models.ForeignKey(
+        LabelProvider,
+        null=True,
+        blank=True,
+        on_delete=models.SET_NULL
+    )
+
+    root_folder = models.CharField(max_length=255, unique=True)
+
+    data_folder = models.CharField(max_length=255)
+
+    external_data = models.BooleanField()

+ 37 - 0
backend/pycs_api/models/result.py

@@ -0,0 +1,37 @@
+from django.db import models
+from pycs_api.models import base
+from pycs_api.models.file import File
+from pycs_api.models.label import Label
+
+
+class Result(base.BaseModel):
+
+    file = models.ForeignKey(
+        File,
+        on_delete=models.CASCADE
+    )
+
+    label = models.ForeignKey(
+        Label,
+        blank=True,
+        null=True,
+        on_delete=models.SET_NULL
+    )
+
+    type = models.CharField(
+        max_length=32,
+        choices=[
+            ("labeled-image", "Image Label"),
+            ("bounding-box", "Bounding Box"),
+        ]
+    )
+
+    origin = models.CharField(
+        max_length=32,
+        choices=[
+            ("manual", "Manual result"),
+            ("computed", "Automatically generated result"),
+        ]
+    )
+
+    data_encoded = models.TextField()

+ 10 - 0
backend/pycs_api/serializers.py

@@ -0,0 +1,10 @@
+
+from rest_framework import serializers
+from pycs_api.models import Model
+
+
+class ModelSerializer(serializers.HyperlinkedModelSerializer):
+
+    class Meta:
+        model = Model
+        fields = Model.serializer_fields

+ 6 - 0
backend/pycs_api/urls.py

@@ -16,10 +16,16 @@ Including another URLconf
 from django.urls import include
 from django.urls import path
 from pycs_api import views
+
+from rest_framework import routers
 from rest_framework_simplejwt.views import TokenObtainPairView
 from rest_framework_simplejwt.views import TokenRefreshView
 
+router = routers.DefaultRouter()
+router.register(r'models', views.ModelViewSet)
+
 urlpatterns = [
+    path('', include(router.urls)),
 
     path('api-token/',
         TokenObtainPairView.as_view(), name="obtain-token"),

+ 13 - 0
backend/pycs_api/views/__init__.py

@@ -1 +1,14 @@
 from pycs_api.views.project import ProjectView
+
+from pycs_api import models
+from pycs_api import serializers
+
+from rest_framework import viewsets
+from rest_framework import permissions
+
+class ModelViewSet(viewsets.ModelViewSet):
+
+    queryset = models.Model.objects.all().order_by("id")
+    serializer_class = serializers.ModelSerializer
+    permission_classes = [permissions.IsAuthenticated]
+

+ 1 - 1
backend/pycs_api/views/base.py

@@ -5,4 +5,4 @@ class JsonResponseView(views.View):
 
     @classmethod
     def respond(cls, obj = {}):
-        return http.JsonResponse(obj)
+        return http.JsonResponse(obj, safe=False)

+ 7 - 3
backend/pycs_api/views/project.py

@@ -1,6 +1,7 @@
-
-from pycs_api.views.base import JsonResponseView
 from django.views.decorators.http import require_POST
+from pycs_api.views.base import JsonResponseView
+from rest_framework.decorators import permission_classes
+
 
 class ProjectView(JsonResponseView):
 
@@ -18,7 +19,10 @@ class ProjectView(JsonResponseView):
 
     def get(self, request, *args, **kwargs):
         """ lists projects """
-        return self.respond()
+        return self.respond([
+            dict(name="Project 1", description="Desc1", ),
+            dict(name="Project 2", description="Desc2"),
+        ])
 
     def post(self, request, *args, **kwargs):
         """ creates a project """

+ 1 - 0
backend/pycs_backend/settings/__init__.py

@@ -27,6 +27,7 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
 
     'corsheaders',
+    'rest_framework',
     'rest_framework_simplejwt.token_blacklist',
 
 ]

+ 2 - 1
backend/pycs_backend/settings/database.py

@@ -7,7 +7,8 @@ DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.mysql',
         'OPTIONS': {
-            'read_default_file': str(BASE_DIR / 'mysql.cnf')
+            'read_default_file': str(BASE_DIR / 'mysql.cnf'),
+            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
         }
     }
 }

+ 1 - 0
backend/requirements.txt

@@ -4,6 +4,7 @@ django-cors-headers==3.3.0
 djangorestframework==3.11.0
 djangorestframework_simplejwt==5.0.0
 
+pillow==8.4.0
 dnspython==1.16.0
 enum-compat==0.0.3
 eventlet==0.25.2

+ 1 - 0
frontend/src/store.js

@@ -10,6 +10,7 @@ export default new Vuex.Store({
      refreshToken: null,
 
      username: null,
+     models: null,
   },
 
   mutations: {

+ 50 - 4
frontend/src/views/Projects.vue

@@ -1,15 +1,61 @@
 <template>
-  <div>
-    <h1>Projects</h1>
+  <v-container fluid>
+    <v-row>
+      <v-col :cols=10>
+        <h1>Projects</h1>
+      </v-col>
+      <v-col :cols=2 >
+        <v-btn block color="primary" @click="addProject">
+          Add Project <v-icon>mdi-plus</v-icon>
+      </v-btn>
+      </v-col>
+    </v-row>
+    <v-row dense>
+      <v-col
+        v-for="model in models"
+        :key="model.id"
+        :cols=4
+      >
+        <v-card
+          outlined elevation="2"
+          max-width=450px class="mx-auto"
+          >
+          <v-card-title>Name: {{model.name}}</v-card-title>
+          <v-card-text>
+              <p>Desc: {{model.description}}</p>
+              <p>Root: {{model.root_folder}}</p>
+              <p>Supports: {{model.supports_encoded}}</p>
+          </v-card-text>
+        </v-card>
+      </v-col>
+    </v-row>
+  </v-container>
 
-  </div>
 </template>
 
 <script>
   import { mapState } from 'vuex'
+  import { getAPI } from '@/axios-api'
+
   export default {
     name: 'Projects',
-    computed: mapState(["accessToken"])
+    computed: mapState(["models"]),
+
+    created () {
+      getAPI.get('/models/',
+        { headers: { Authorization: `Bearer ${this.$store.state.accessToken}`}})
+        .then(response => {
+          this.$store.state.models = response.data
+        })
+        .catch(err => {
+          console.log(err)
+        })
+    },
+    methods: {
+      addProject() {
+        console.log("Adding Project")
+      }
+    },
   }
 </script>