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 diff --git a/django_project/cplus_api/api_views/layer.py b/django_project/cplus_api/api_views/layer.py index bdc155c..535f1ba 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,7 +114,32 @@ 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): + """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/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 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/tests/factories.py b/django_project/cplus_api/tests/factories.py index d253462..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,23 +10,10 @@ ) -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(BaseFactory[User], - metaclass=BaseMetaFactory[User]): +class UserF(factory.django.DjangoModelFactory): class Meta: model = User @@ -38,8 +24,7 @@ class Meta: last_name = 'Doe' -class ScenarioTaskF(BaseFactory[ScenarioTask], - metaclass=BaseMetaFactory[ScenarioTask]): +class ScenarioTaskF(factory.django.DjangoModelFactory): class Meta: model = ScenarioTask @@ -266,8 +251,7 @@ class Meta: } -class InputLayerF(BaseFactory[InputLayer], - metaclass=BaseMetaFactory[InputLayer]): +class InputLayerF(factory.django.DjangoModelFactory): class Meta: model = InputLayer @@ -281,8 +265,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 +278,7 @@ class Meta: scenario = factory.SubFactory(ScenarioTaskF) -class MultipartUploadF(BaseFactory[MultipartUpload], - metaclass=BaseMetaFactory[MultipartUpload]): +class MultipartUploadF(factory.django.DjangoModelFactory): class Meta: model = MultipartUpload 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 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(),