Skip to content

Commit

Permalink
Merge pull request #55 from kartoza/fix-scenario-detail-api
Browse files Browse the repository at this point in the history
Fix scenario detail api
  • Loading branch information
danangmassandy committed Aug 6, 2024
2 parents c233550 + 0a669a9 commit e863d06
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Run Coverage test
working-directory: deployment
run: |
cat << EOF | docker-compose exec -T dev bash
cat << EOF | docker compose exec -T dev bash
python manage.py makemigrations
python manage.py migrate
python manage.py collectstatic --noinput --verbosity 0
Expand Down
22 changes: 11 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ build:
@echo "------------------------------------------------------------------"
@echo "Building in production mode"
@echo "------------------------------------------------------------------"
@docker-compose build
@docker compose build

build-dev:
@echo
@echo "------------------------------------------------------------------"
@echo "Building in dev mode"
@echo "------------------------------------------------------------------"
@docker-compose build dev
@docker compose build dev

wait-db:
@docker-compose ${ARGS} exec -T db su - postgres -c "until pg_isready; do sleep 5; done"
@docker compose ${ARGS} exec -T db su - postgres -c "until pg_isready; do sleep 5; done"

sleep:
@echo
Expand All @@ -31,53 +31,53 @@ up:
@echo "------------------------------------------------------------------"
@echo "Running in production mode"
@echo "------------------------------------------------------------------"
@docker-compose ${ARGS} up -d nginx django worker celery_beat
@docker compose ${ARGS} up -d nginx django worker celery_beat

dev:
@echo
@echo "------------------------------------------------------------------"
@echo "Running in dev mode"
@echo "------------------------------------------------------------------"
@docker-compose ${ARGS} up -d dev worker
@docker compose ${ARGS} up -d dev worker

down:
@echo
@echo "------------------------------------------------------------------"
@echo "Running in dev mode"
@echo "------------------------------------------------------------------"
@docker-compose ${ARGS} down
@docker compose ${ARGS} down

migrate:
@echo
@echo "------------------------------------------------------------------"
@echo "Running migration"
@echo "------------------------------------------------------------------"
@docker-compose ${ARGS} exec -T dev python manage.py migrate
@docker compose ${ARGS} exec -T dev python manage.py migrate

dev-runserver:
@echo
@echo "------------------------------------------------------------------"
@echo "Start django runserver in dev container"
@echo "------------------------------------------------------------------"
@docker-compose $(ARGS) exec -T dev bash -c "nohup python manage.py runserver 0.0.0.0:8080 &"
@docker compose $(ARGS) exec -T dev bash -c "nohup python manage.py runserver 0.0.0.0:8080 &"

dev-shell:
@echo
@echo "------------------------------------------------------------------"
@echo "Start django runserver in dev container"
@echo "------------------------------------------------------------------"
@docker-compose $(ARGS) exec dev bash
@docker compose $(ARGS) exec dev bash

scale-worker:
@echo
@echo "------------------------------------------------------------------"
@echo "scale-worker"
@echo "------------------------------------------------------------------"
@docker-compose up -d worker --no-deps --no-recreate --scale worker=$(COUNT)
@docker compose up -d worker --no-deps --no-recreate --scale worker=$(COUNT)

init-bucket:
@echo
@echo "------------------------------------------------------------------"
@echo "Init createbuckets Minio"
@echo "------------------------------------------------------------------"
@docker-compose ${ARGS} up -d createbuckets
@docker compose ${ARGS} up -d createbuckets
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ coverage:
changes: false
patch:
default:
threshold: "5%"
threshold: "10%"
ignore:
- "**/migrations/*.py"
3 changes: 2 additions & 1 deletion django_project/core/settings/contrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
'DEFAULT_VERSIONING_CLASS': (
'rest_framework.versioning.NamespaceVersioning'
),
'EXCEPTION_HANDLER': 'core.tools.custom_exception_handler'
'EXCEPTION_HANDLER': 'core.tools.custom_exception_handler',
'DATETIME_FORMAT': '%Y-%m-%dT%H:%M:%SZ'
}

AUTHENTICATION_BACKENDS = (
Expand Down
50 changes: 43 additions & 7 deletions django_project/cplus_api/api_views/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
ScenarioTaskStatusSerializer,
ScenarioTaskLogListSerializer,
ScenarioTaskLogSerializer,
PaginatedScenarioTaskStatusSerializer,
ScenarioDetailSerializer
PaginatedScenarioTaskItemSerializer,
ScenarioDetailSerializer,
ScenarioTaskItemSerializer
)
from cplus_api.serializers.common import (
APIErrorSerializer
APIErrorSerializer,
NoContentSerializer
)
from cplus_api.utils.api_helper import (
SCENARIO_API_TAG,
Expand Down Expand Up @@ -266,9 +268,18 @@ class ScenarioAnalysisHistory(APIView):
@swagger_auto_schema(
operation_id='scenario-analysis-history',
tags=[SCENARIO_API_TAG],
manual_parameters=PARAMS_PAGINATION,
manual_parameters=[
openapi.Parameter(
'status', openapi.IN_QUERY,
description=(
'Scenario status filter'
),
type=openapi.TYPE_STRING,
required=False
)
] + PARAMS_PAGINATION,
responses={
200: PaginatedScenarioTaskStatusSerializer,
200: PaginatedScenarioTaskItemSerializer,
400: APIErrorSerializer,
404: APIErrorSerializer
}
Expand All @@ -278,7 +289,12 @@ def get(self, request, *args, **kwargs):
page_size = get_page_size(request)
scenarios = ScenarioTask.objects.filter(
submitted_by=request.user
).order_by('submitted_on')
).order_by('-submitted_on')
status = request.GET.get('status', '')
if status:
scenarios = scenarios.filter(
status__in=status.split(',')
)
# set pagination
paginator = Paginator(scenarios, page_size)
total_page = math.ceil(paginator.count / page_size)
Expand All @@ -287,7 +303,7 @@ def get(self, request, *args, **kwargs):
else:
paginated_entities = paginator.get_page(page)
output = (
ScenarioTaskStatusSerializer(
ScenarioTaskItemSerializer(
paginated_entities,
many=True
).data
Expand Down Expand Up @@ -322,3 +338,23 @@ def get(self, request, *args, **kwargs):
self.validate_user_access(request.user, scenario_task)
return Response(
status=200, data=ScenarioDetailSerializer(scenario_task).data)

@swagger_auto_schema(
operation_id='scenario-analysis-remove',
operation_description='API to remove scenario analysis.',
tags=[SCENARIO_API_TAG],
manual_parameters=[PARAM_SCENARIO_UUID_IN_PATH],
responses={
204: NoContentSerializer,
400: APIErrorSerializer,
403: APIErrorSerializer,
404: APIErrorSerializer
}
)
def delete(self, request, *args, **kwargs):
scenario_uuid = kwargs.get('scenario_uuid')
scenario_task = get_object_or_404(
ScenarioTask, uuid=scenario_uuid)
self.validate_user_access(request.user, scenario_task, 'delete')
scenario_task.delete()
return Response(status=204)
48 changes: 45 additions & 3 deletions django_project/cplus_api/serializers/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,11 +561,53 @@ class ScenarioTaskLogListSerializer(serializers.ListSerializer):
child = ScenarioTaskLogSerializer()


class PaginatedScenarioTaskStatusSerializer(serializers.Serializer):
class ScenarioTaskItemSerializer(ScenarioTaskStatusSerializer):
class Meta:
props = (
ScenarioTaskStatusSerializer.Meta.
swagger_schema_fields['properties']
)
del props['logs']
swagger_schema_fields = {
'type': openapi.TYPE_OBJECT,
'title': 'Scenario Task Item',
'properties': {
**props,
'detail': {
**ScenarioInputSerializer.Meta.swagger_schema_fields
}
},
'example': {
'uuid': '8c4582ab-15b1-4ed0-b8e4-00640ec10a65',
'task_id': '3e0c7dff-51f2-48c5-a316-15d9ca2407cb',
'plugin_version': '1.0.0',
'scenario_name': 'Scenario A',
'status': 'Queued',
'submitted_on': '2022-08-15T08:09:15.049806Z',
'created_by': '[email protected]',
'started_at': '2022-08-15T08:09:15.049806Z',
'finished_at': '2022-08-15T09:09:15.049806Z',
'errors': None,
'progress': 70,
'progress_text': 'Processing ABC',
'detail': {}
}
}
model = ScenarioTask
fields = [
'uuid', 'task_id', 'plugin_version',
'scenario_name', 'status', 'submitted_on',
'created_by', 'started_at', 'finished_at',
'errors', 'progress', 'progress_text',
'detail'
]


class PaginatedScenarioTaskItemSerializer(serializers.Serializer):
page = serializers.IntegerField()
total_page = serializers.IntegerField()
page_size = serializers.IntegerField()
results = ScenarioTaskStatusSerializer(many=True)
results = ScenarioTaskItemSerializer(many=True)

class Meta:
swagger_schema_fields = {
Expand All @@ -588,7 +630,7 @@ class Meta:
title='Results',
type=openapi.TYPE_ARRAY,
items=openapi.Items(
**ScenarioTaskStatusSerializer.
**ScenarioTaskItemSerializer.
Meta.swagger_schema_fields
),
)
Expand Down
20 changes: 20 additions & 0 deletions django_project/cplus_api/tests/test_scenario_api_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,15 @@ def test_scenario_history(self):
self.assertEqual(len(response.data['results']), 1)
scenario = response.data['results'][0]
self.assertEqual(str(scenario_task.uuid), scenario['uuid'])
# filter completed status only
request = self.factory.get(
reverse('v1:scenario-history') + f'?status={TaskStatus.COMPLETED}'
)
request.resolver_match = FakeResolverMatchV1
request.user = self.user_1
response = view(request)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['results']), 0)

def test_scenario_detail(self):
view = ScenarioAnalysisTaskDetail.as_view()
Expand All @@ -348,3 +357,14 @@ def test_scenario_detail(self):
response.data['scenario_name'],
scenario_task.detail['scenario_name']
)
# delete scenario
request = self.factory.delete(
reverse('v1:scenario-detail', kwargs=kwargs)
)
request.resolver_match = FakeResolverMatchV1
request.user = self.superuser
response = view(request, **kwargs)
self.assertEqual(response.status_code, 204)
self.assertFalse(ScenarioTask.objects.filter(
id=scenario_task.id
).exists())
7 changes: 5 additions & 2 deletions django_project/cplus_api/utils/api_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import traceback
from datetime import datetime
from uuid import UUID
from enum import Enum

import boto3
import math
Expand Down Expand Up @@ -232,15 +233,17 @@ class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, UUID):
# if the obj is uuid, we simply return the value of uuid
return obj.hex
return str(obj)
if isinstance(obj, datetime):
# if the obj is uuid, we simply return the value of uuid
return obj.isoformat()
return json.JSONEncoder.default(self, obj)


def todict(obj, classkey=None):
if isinstance(obj, dict):
if isinstance(obj, Enum):
return obj.value
elif isinstance(obj, dict):
data = {}
for (k, v) in obj.items():
data[k] = todict(v, classkey)
Expand Down

0 comments on commit e863d06

Please sign in to comment.