Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
add workflows app
Browse files Browse the repository at this point in the history
  • Loading branch information
MJedr committed Oct 18, 2023
1 parent a4a9fb5 commit d4ad5e4
Show file tree
Hide file tree
Showing 24 changed files with 336 additions and 3 deletions.
Empty file.
3 changes: 3 additions & 0 deletions backoffice/management/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions backoffice/management/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ManagementConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "management"
5 changes: 5 additions & 0 deletions backoffice/management/groups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib.auth.models import Group


admin_group, created = Group.objects.get_or_create(name='admin')
curator_group, created = Group.objects.get_or_create(name='curator')
Empty file.
3 changes: 3 additions & 0 deletions backoffice/management/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
27 changes: 27 additions & 0 deletions backoffice/management/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.contrib.auth.models import Group
from rest_framework import permissions


def _is_in_group(user, group_name):
try:
return Group.objects.get(name=group_name).user_set.filter(id=user.id).exists()
except Group.DoesNotExist:
return None


def _has_group_permission(user, required_groups):
return any([_is_in_group(user, group_name) for group_name in required_groups])


class PermissionCheckBase(permissions.BasePermission):
def has_permission(self, request, view):
has_group_permission = _has_group_permission(request.user, self.required_groups)
return request.user and has_group_permission

def has_object_permission(self, request, view, obj):
has_group_permission = _has_group_permission(request.user, self.required_groups)
return request.user and has_group_permission


class IsAdminOrCuratorUser(PermissionCheckBase):
required_groups = ['admin', 'curator']
45 changes: 45 additions & 0 deletions backoffice/management/tests/test_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.contrib.auth.models import Group
from rest_framework.test import APIRequestFactory, force_authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from django.test import TestCase
from management.permissions import IsAdminOrCuratorUser
from django.contrib.auth import get_user_model


User = get_user_model()


class MockView(APIView):
permission_classes = [IsAdminOrCuratorUser]

def get(self, request):
return Response("Test Response")


class PermissionCheckTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(email='[email protected]', password='testpassword')

self.admin_group, _ = Group.objects.get_or_create(name='admin')
self.curator_group, _ = Group.objects.get_or_create(name='curator')

def test_user_in_required_group(self):
self.user.groups.add(self.admin_group)

factory = APIRequestFactory()
request = factory.get('/mock/')
force_authenticate(request, user=self.user)

view = MockView.as_view()
response = view(request)
self.assertEqual(response.status_code, 200)

def test_user_not_in_required_group(self):
factory = APIRequestFactory()
request = factory.get('/mock/')
force_authenticate(request, user=self.user)

view = MockView.as_view()
response = view(request)
self.assertEqual(response.status_code, 403)
3 changes: 3 additions & 0 deletions backoffice/management/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.
Empty file.
3 changes: 3 additions & 0 deletions backoffice/workflows/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
8 changes: 8 additions & 0 deletions backoffice/workflows/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from rest_framework import serializers
from workflows.models import Workflow


class WorkflowSerializer(serializers.ModelSerializer):
class Meta:
model = Workflow
fields = '__all__'
17 changes: 17 additions & 0 deletions backoffice/workflows/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from workflows.models import Workflow


from rest_framework import viewsets
from .serializers import WorkflowSerializer


class WorkflowViewSet(viewsets.ModelViewSet):
queryset = Workflow.objects.all()
serializer_class = WorkflowSerializer

def get_queryset(self):
status = self.request.query_params.get('status')
if status:
return self.queryset.filter(status__status=status)
return self.queryset

6 changes: 6 additions & 0 deletions backoffice/workflows/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class WorkflowsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "workflows"
63 changes: 63 additions & 0 deletions backoffice/workflows/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Generated by Django 4.2.6 on 2023-10-16 09:06

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):
initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="WorkflowData",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("data", models.JSONField()),
],
),
migrations.CreateModel(
name="WorkflowMeta",
fields=[
(
"id",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="workflows.workflowdata",
),
),
("core", models.BooleanField()),
("is_update", models.BooleanField()),
],
),
migrations.CreateModel(
name="WorkflowStatus",
fields=[
(
"id",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="workflows.workflowdata",
),
),
(
"status",
models.CharField(
choices=[
("PREPROCESSING", "Preprocessing"),
("APPROVAL", "Approval"),
("POSTPROCESSING", "Postprocessing"),
],
default="PREPROCESSING",
max_length=30,
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 4.2.6 on 2023-10-17 07:16

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):
dependencies = [
("workflows", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Workflow",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("data", models.JSONField()),
(
"status",
models.CharField(
choices=[
("PREPROCESSING", "Preprocessing"),
("APPROVAL", "Approval"),
("POSTPROCESSING", "Postprocessing"),
],
default="PREPROCESSING",
max_length=30,
),
),
("core", models.BooleanField()),
("is_update", models.BooleanField()),
],
),
migrations.RemoveField(
model_name="workflowmeta",
name="id",
),
migrations.RemoveField(
model_name="workflowstatus",
name="id",
),
migrations.DeleteModel(
name="WorkflowData",
),
migrations.DeleteModel(
name="WorkflowMeta",
),
migrations.DeleteModel(
name="WorkflowStatus",
),
]
Empty file.
28 changes: 28 additions & 0 deletions backoffice/workflows/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.db import models
import uuid


class Workflow(models.Model):
PREPROCESSING = 'PREPROCESSING'
APPROVAL = 'APPROVAL'
POSTPROCESSING = 'POSTPROCESSING'
STATUS_CHOICES = (
(PREPROCESSING, 'Preprocessing'),
(APPROVAL, 'Approval'),
(POSTPROCESSING, 'Postprocessing'),
)
id = models.UUIDField(
primary_key = True,
default = uuid.uuid4,
editable = False
)

data = models.JSONField()
status = models.CharField(
max_length=30,
choices=STATUS_CHOICES,
default=PREPROCESSING,
)
core = models.BooleanField()
is_update = models.BooleanField()

3 changes: 3 additions & 0 deletions backoffice/workflows/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
45 changes: 45 additions & 0 deletions backoffice/workflows/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.test import TestCase
from django.apps import apps

User = get_user_model()
Workflow = apps.get_model(app_label='workflows', model_name='Workflow')

class TestWorkflowViewSet(TestCase):
endpoint = '/api/workflows/'

def setUp(self):
self.curator_group = Group.objects.create(name="curator")
self.admin_group = Group.objects.create(name="admin")

self.curator = User.objects.create_user(email='[email protected]', password='12345')
self.admin = User.objects.create_user(email='[email protected]', password='12345')
self.user = User.objects.create_user(email='[email protected]', password='12345')

self.curator.groups.add(self.curator_group)
self.admin.groups.add(self.admin_group)

self.api_client = APIClient()
self.workflow = Workflow.objects.create(data={}, status='APPROVAL', core=True, is_update=False)

def test_list_curator(self):
self.api_client.force_authenticate(user=self.curator)
response = self.api_client.get(self.endpoint, format="json")

self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 1)

def test_list_admin(self):
self.api_client.force_authenticate(user=self.admin)
response = self.api_client.get(self.endpoint, format="json")

self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.json()), 1)

def test_list_anonymous(self):
self.api_client.force_authenticate(user=self.user)
response = self.api_client.get(self.endpoint, format="json")

self.assertEqual(response.status_code, 403)
8 changes: 8 additions & 0 deletions backoffice/workflows/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib import admin
from django.urls import include, path


urlpatterns = [
path("workflows/", include("workflows.urls")),
path("admin/", admin.site.urls),
]
4 changes: 4 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from rest_framework.routers import DefaultRouter, SimpleRouter

from backoffice.users.api.views import UserViewSet
from backoffice.workflows.api.views import WorkflowViewSet


if settings.DEBUG:
router = DefaultRouter()
Expand All @@ -10,6 +12,8 @@

router.register("users", UserViewSet)

# Workflows
router.register("workflows", WorkflowViewSet, basename='workflows')

app_name = "api"
urlpatterns = router.urls
4 changes: 3 additions & 1 deletion config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@

LOCAL_APPS = [
"backoffice.users",
"backoffice.workflows",
"backoffice.management"
# Your stuff: custom apps go here
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
Expand Down Expand Up @@ -339,7 +341,7 @@
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_PERMISSION_CLASSES": ('backoffice.management.permissions.IsAdminOrCuratorUser', ),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

Expand Down
7 changes: 5 additions & 2 deletions config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@

# EMAIL
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = env("DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend")
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)

# Disable the email sending for password reset
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# django-debug-toolbar
# ------------------------------------------------------------------------------
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
Expand Down

0 comments on commit d4ad5e4

Please sign in to comment.