From 296275c1a1c498b39cb4893237544ff6063eb42d Mon Sep 17 00:00:00 2001 From: Danang Date: Mon, 19 Aug 2024 07:13:58 +0000 Subject: [PATCH 1/6] add metadata to input layer model --- .../migrations/0010_inputlayer_metadata.py | 18 ++++++++++++++++++ django_project/cplus_api/models/layer.py | 6 ++++++ 2 files changed, 24 insertions(+) create mode 100644 django_project/cplus_api/migrations/0010_inputlayer_metadata.py diff --git a/django_project/cplus_api/migrations/0010_inputlayer_metadata.py b/django_project/cplus_api/migrations/0010_inputlayer_metadata.py new file mode 100644 index 0000000..4aff35f --- /dev/null +++ b/django_project/cplus_api/migrations/0010_inputlayer_metadata.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-08-19 06:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cplus_api', '0009_scenariotask_stack_trace_errors'), + ] + + operations = [ + migrations.AddField( + model_name='inputlayer', + name='metadata', + field=models.JSONField(blank=True, default=dict, help_text='Layer Metadata.'), + ), + ] diff --git a/django_project/cplus_api/models/layer.py b/django_project/cplus_api/models/layer.py index 676f81e..c8e8b7f 100644 --- a/django_project/cplus_api/models/layer.py +++ b/django_project/cplus_api/models/layer.py @@ -119,6 +119,12 @@ class PrivacyTypes(models.TextChoices): blank=True ) + metadata = models.JSONField( + default=dict, + blank=True, + help_text='Layer Metadata.' + ) + def download_to_working_directory(self, base_dir: str): if not self.is_available(): return None From 9553d042eae74a39793fe57365676cfa67f95035 Mon Sep 17 00:00:00 2001 From: Danang Date: Mon, 19 Aug 2024 07:14:32 +0000 Subject: [PATCH 2/6] add API fetch default layers --- django_project/cplus_api/api_views/layer.py | 26 ++++++++++++++++++- django_project/cplus_api/serializers/layer.py | 6 ++++- django_project/cplus_api/urls_v1.py | 8 +++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/django_project/cplus_api/api_views/layer.py b/django_project/cplus_api/api_views/layer.py index bdc155c..d4f42fa 100644 --- a/django_project/cplus_api/api_views/layer.py +++ b/django_project/cplus_api/api_views/layer.py @@ -20,7 +20,8 @@ PaginatedInputLayerSerializer, UploadLayerSerializer, FinishUploadLayerSerializer, - LAYER_SCHEMA_FIELDS + LAYER_SCHEMA_FIELDS, + InputLayerListSerializer ) from cplus_api.serializers.common import ( APIErrorSerializer, @@ -113,6 +114,29 @@ def get(self, request, *args, **kwargs): }) +class DefaultLayerList(APIView): + """API to return default layers.""" + permission_classes = [IsAuthenticated] + + @swagger_auto_schema( + operation_id='layer-default-list', + tags=[LAYER_API_TAG], + responses={ + 200: InputLayerListSerializer, + 400: APIErrorSerializer, + 404: APIErrorSerializer + } + ) + def get(self, request, *args, **kwargs): + layers = InputLayer.objects.filter( + privacy_type=InputLayer.PrivacyTypes.COMMON + ).order_by('name') + return Response(status=200, data=( + InputLayerSerializer( + layers, many=True + ).data + )) + class BaseLayerUpload(APIView): def validate_upload_access(self, privacy_type, user, diff --git a/django_project/cplus_api/serializers/layer.py b/django_project/cplus_api/serializers/layer.py index 09c143e..d53dc33 100644 --- a/django_project/cplus_api/serializers/layer.py +++ b/django_project/cplus_api/serializers/layer.py @@ -179,7 +179,7 @@ class Meta: 'uuid', 'filename', 'created_on', 'created_by', 'layer_type', 'size', 'url', 'component_type', 'privacy_type', - 'client_id' + 'client_id', 'metadata' ] @@ -215,6 +215,10 @@ class Meta: } +class InputLayerListSerializer(serializers.ListSerializer): + child = InputLayerSerializer() + + class UploadLayerSerializer(serializers.Serializer): layer_type = serializers.IntegerField() component_type = serializers.CharField() diff --git a/django_project/cplus_api/urls_v1.py b/django_project/cplus_api/urls_v1.py index 5026996..87da465 100644 --- a/django_project/cplus_api/urls_v1.py +++ b/django_project/cplus_api/urls_v1.py @@ -4,7 +4,8 @@ LayerList, LayerDetail, LayerUpload, LayerUploadStart, LayerUploadFinish, CheckLayer, LayerUploadAbort, - FetchLayerByClientId + FetchLayerByClientId, + DefaultLayerList ) from cplus_api.api_views.scenario import ( ScenarioAnalysisSubmit, @@ -32,6 +33,11 @@ # LAYER API layer_urls = [ + path( + 'layer/default/', + DefaultLayerList.as_view(), + name='layer-default-list' + ), path( 'layer/list/', LayerList.as_view(), From 95c2c18565c8d989cb7125d445a2e861eec94c36 Mon Sep 17 00:00:00 2001 From: Danang Date: Tue, 20 Aug 2024 03:45:08 +0000 Subject: [PATCH 3/6] fix lint and add test for default layer list API --- django_project/cplus_api/api_views/layer.py | 2 ++ .../cplus_api/tests/test_layer_api_view.py | 25 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/django_project/cplus_api/api_views/layer.py b/django_project/cplus_api/api_views/layer.py index d4f42fa..535f1ba 100644 --- a/django_project/cplus_api/api_views/layer.py +++ b/django_project/cplus_api/api_views/layer.py @@ -137,7 +137,9 @@ def get(self, request, *args, **kwargs): ).data )) + class BaseLayerUpload(APIView): + """Base class for layer upload.""" def validate_upload_access(self, privacy_type, user, is_update=False, existing_layer=None): diff --git a/django_project/cplus_api/tests/test_layer_api_view.py b/django_project/cplus_api/tests/test_layer_api_view.py index 4187ae6..2389cb3 100644 --- a/django_project/cplus_api/tests/test_layer_api_view.py +++ b/django_project/cplus_api/tests/test_layer_api_view.py @@ -22,7 +22,8 @@ is_internal_user, validate_layer_access, LayerUploadAbort, - FetchLayerByClientId + FetchLayerByClientId, + DefaultLayerList ) from cplus_api.models.profile import UserProfile from cplus_api.utils.api_helper import convert_size @@ -102,6 +103,28 @@ def test_layer_list(self): self.assertTrue(find_layer) self.assertTrue(find_layer['url']) + def test_default_layer_list(self): + request = self.factory.get( + reverse('v1:layer-default-list') + ) + request.resolver_match = FakeResolverMatchV1 + request.user = self.superuser + view = DefaultLayerList.as_view() + response = view(request) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, []) + input_layer = InputLayerF.create( + privacy_type=InputLayer.PrivacyTypes.COMMON + ) + response = view(request) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data), 1) + find_layer = self.find_layer_from_response( + response.data, input_layer.uuid) + self.assertTrue(find_layer) + self.assertFalse(find_layer['url']) + self.assertFalse(input_layer.file) + def test_layer_access(self): input_layer_1 = InputLayerF.create( privacy_type=InputLayer.PrivacyTypes.COMMON From 6efdded3f7e18f3c49ce5053a0d0ea667421174d Mon Sep 17 00:00:00 2001 From: Danang Date: Tue, 20 Aug 2024 04:27:21 +0000 Subject: [PATCH 4/6] change python 3.11.8 in django_test workflow --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7492e97..21f9468 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,7 +32,7 @@ jobs: strategy: matrix: python-version: - - 3.10.4 + - 3.11.8 steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v3 From 25aef94321ac1ac1c16472b6073954b301f9111a Mon Sep 17 00:00:00 2001 From: Danang Date: Tue, 20 Aug 2024 05:05:02 +0000 Subject: [PATCH 5/6] remove generic base factory usage --- django_project/cplus_api/tests/factories.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/django_project/cplus_api/tests/factories.py b/django_project/cplus_api/tests/factories.py index d253462..f5e8b0d 100644 --- a/django_project/cplus_api/tests/factories.py +++ b/django_project/cplus_api/tests/factories.py @@ -26,8 +26,7 @@ def create(cls, **kwargs) -> T: return super().create(**kwargs) -class UserF(BaseFactory[User], - metaclass=BaseMetaFactory[User]): +class UserF(factory.django.DjangoModelFactory): class Meta: model = User @@ -38,8 +37,7 @@ class Meta: last_name = 'Doe' -class ScenarioTaskF(BaseFactory[ScenarioTask], - metaclass=BaseMetaFactory[ScenarioTask]): +class ScenarioTaskF(factory.django.DjangoModelFactory): class Meta: model = ScenarioTask @@ -266,8 +264,7 @@ class Meta: } -class InputLayerF(BaseFactory[InputLayer], - metaclass=BaseMetaFactory[InputLayer]): +class InputLayerF(factory.django.DjangoModelFactory): class Meta: model = InputLayer @@ -281,8 +278,7 @@ class Meta: privacy_type = InputLayer.PrivacyTypes.PRIVATE -class OutputLayerF(BaseFactory[OutputLayer], - metaclass=BaseMetaFactory[OutputLayer]): +class OutputLayerF(factory.django.DjangoModelFactory): class Meta: model = OutputLayer @@ -295,8 +291,7 @@ class Meta: scenario = factory.SubFactory(ScenarioTaskF) -class MultipartUploadF(BaseFactory[MultipartUpload], - metaclass=BaseMetaFactory[MultipartUpload]): +class MultipartUploadF(factory.django.DjangoModelFactory): class Meta: model = MultipartUpload From 8c7d0bc7d98b6f4a8f913ac8c2413f7459cc252c Mon Sep 17 00:00:00 2001 From: Danang Date: Tue, 20 Aug 2024 05:30:18 +0000 Subject: [PATCH 6/6] remove base factory class --- django_project/cplus_api/tests/factories.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/django_project/cplus_api/tests/factories.py b/django_project/cplus_api/tests/factories.py index f5e8b0d..aa3b515 100644 --- a/django_project/cplus_api/tests/factories.py +++ b/django_project/cplus_api/tests/factories.py @@ -1,7 +1,6 @@ """Model factories.""" import factory import uuid -from typing import Generic, TypeVar from django.contrib.auth import get_user_model from django.utils import timezone from cplus_api.models.scenario import ScenarioTask @@ -11,21 +10,9 @@ ) -T = TypeVar('T') User = get_user_model() -class BaseMetaFactory(Generic[T], factory.base.FactoryMetaClass): - def __call__(cls, *args, **kwargs) -> T: - return super().__call__(*args, **kwargs) - - -class BaseFactory(Generic[T], factory.django.DjangoModelFactory): - @classmethod - def create(cls, **kwargs) -> T: - return super().create(**kwargs) - - class UserF(factory.django.DjangoModelFactory): class Meta: model = User