Skip to content

Commit

Permalink
Accept settings JSON on analysis or model creation (#1067)
Browse files Browse the repository at this point in the history
* wip

* Only read settings JSON on create / partial update

* POST settings on model creation working

* PATCH model settings working

* Expand analyses with Settings on POST/PATCH

* PEP8

* Fix model list serializer

* Fix missing field

* Fix missing run_mode
  • Loading branch information
sambles authored Jun 27, 2024
1 parent 30583a0 commit 51ade55
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 6 deletions.
52 changes: 50 additions & 2 deletions src/server/oasisapi/analyses/v2_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@
TaskErrorSerializer,
)

from ...schemas.serializers import AnalysisSettingsSerializer
from django.core.files import File
from tempfile import TemporaryFile
from ...files.models import RelatedFile


def create_settings_file(data, user):
json_serializer = AnalysisSettingsSerializer()
with TemporaryFile() as tmp_file:
tmp_file.write(data.encode('utf-8'))
tmp_file.seek(0)

return RelatedFile.objects.create(
file=File(tmp_file, name=json_serializer.filename),
filename=json_serializer.filename,
content_type='application/json',
creator=user,
)


class AnalysisTaskStatusSerializer(serializers.ModelSerializer):
output_log = serializers.SerializerMethodField()
Expand Down Expand Up @@ -65,6 +84,7 @@ class AnalysisListSerializer(serializers.Serializer):
task_started = serializers.DateTimeField(read_only=True)
task_finished = serializers.DateTimeField(read_only=True)
complex_model_data_files = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
priority = serializers.IntegerField(read_only=True)

# Groups - inherited from portfolio
groups = serializers.SerializerMethodField(read_only=True)
Expand Down Expand Up @@ -266,7 +286,7 @@ def get_settings_file(self, instance):
request = self.context.get('request')
return instance.get_absolute_settings_file_url(request=request, namespace=self.ns) if instance.settings_file_id else None

@swagger_serializer_method(serializer_or_field=serializers.URLField)
@swagger_serializer_method(serializer_or_field=AnalysisSettingsSerializer)
def get_settings(self, instance):
request = self.context.get('request')
return instance.get_absolute_settings_url(request=request, namespace=self.ns) if instance.settings_file_id else None
Expand Down Expand Up @@ -378,7 +398,6 @@ def validate(self, attrs):

# Check that portfolio has a location file and user is allowed to use the portfolio
if attrs.get('portfolio'):

try:
verify_and_get_groups(user, attrs['portfolio'].groups.all())
except ValidationError:
Expand All @@ -398,8 +417,37 @@ def validate(self, attrs):
'model': ["Model pk \"{}\" - 'run_mode' must not be null".format(attrs['model'].id)]
})

# Validate analyses settings if given at create/update
if attrs.get('settings'):
attrs['settings'] = AnalysisSettingsSerializer().validate(attrs.get('settings'))
return attrs

def to_internal_value(self, data):
settings = data.get('settings', {})
data = super(AnalysisSerializer, self).to_internal_value(data)
data['settings'] = AnalysisSettingsSerializer().to_internal_value(settings)
return data

def update(self, instance, validated_data):
data = validated_data.copy()
settings = data.pop('settings', {})
if settings:
instance.settings_file = create_settings_file(settings, instance.creator)
return super(AnalysisSerializer, self).update(instance, validated_data)

def create(self, validated_data):
data = validated_data.copy()
settings = data.pop('settings', {})
if 'request' in self.context:
data['creator'] = self.context.get('request').user

instance = super(AnalysisSerializer, self).create(data)
if settings:
instance.settings_file = create_settings_file(settings, data['creator'])

instance.save()
return instance


class AnalysisSerializerWebSocket(serializers.Serializer):
""" Minimal Analysis Infomation needed to send via WebSocket
Expand Down
4 changes: 2 additions & 2 deletions src/server/oasisapi/analyses/v2_api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,9 @@ def get_queryset(self):
return super().get_queryset().select_related(*self.file_action_types).prefetch_related('complex_model_data_files')

def get_serializer_class(self):
if self.action in ['create', 'options', 'update', 'partial_update', 'retrieve']:
if self.action in ['create', 'options', 'update', 'partial_update']:
return super().get_serializer_class()
elif self.action in ['list']:
elif self.action in ['list', 'retrieve']:
return AnalysisListSerializer
elif self.action == 'copy':
return AnalysisCopySerializer
Expand Down
88 changes: 86 additions & 2 deletions src/server/oasisapi/analysis_models/v2_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,73 @@
from ..models import AnalysisModel, ModelScalingOptions, ModelChunkingOptions
from ...permissions.group_auth import validate_and_update_groups, validate_data_files

from ...schemas.serializers import ModelParametersSerializer
from django.core.files import File
from tempfile import TemporaryFile
from ...files.models import RelatedFile


def create_settings_file(data, user):
json_serializer = ModelParametersSerializer()
with TemporaryFile() as tmp_file:
tmp_file.write(data.encode('utf-8'))
tmp_file.seek(0)

return RelatedFile.objects.create(
file=File(tmp_file, name=json_serializer.filename),
filename=json_serializer.filename,
content_type='application/json',
creator=user,
)


class AnalysisModelListSerializer(serializers.Serializer):
""" Read Only Model Deserializer for efficiently returning a list of all
entries in DB
"""
id = serializers.IntegerField(read_only=True)
supplier_id = serializers.CharField(read_only=True)
model_id = serializers.CharField(read_only=True)
version_id = serializers.CharField(read_only=True)
created = serializers.DateTimeField(read_only=True)
modified = serializers.DateTimeField(read_only=True)
settings = serializers.SerializerMethodField()
versions = serializers.SerializerMethodField()
scaling_configuration = serializers.SerializerMethodField()
chunking_configuration = serializers.SerializerMethodField()
groups = serializers.SlugRelatedField(many=True, read_only=False, slug_field='name', required=False, queryset=Group.objects.all())
settings = serializers.SerializerMethodField()
run_mode = serializers.CharField(read_only=True)
namespace = 'v2-models'

@swagger_serializer_method(serializer_or_field=serializers.URLField)
def get_settings(self, instance):
request = self.context.get('request')
return instance.get_absolute_settings_url(request=request, namespace=self.namespace)

@swagger_serializer_method(serializer_or_field=serializers.URLField)
def get_versions(self, instance):
request = self.context.get('request')
return instance.get_absolute_versions_url(request=request, namespace=self.namespace)

@swagger_serializer_method(serializer_or_field=serializers.URLField)
def get_scaling_configuration(self, instance):
request = self.context.get('request')
return instance.get_absolute_scaling_configuration_url(request=request, namespace=self.namespace)

@swagger_serializer_method(serializer_or_field=serializers.URLField)
def get_chunking_configuration(self, instance):
request = self.context.get('request')
return instance.get_absolute_chunking_configuration_url(request=request, namespace=self.namespace)


class AnalysisModelSerializer(serializers.ModelSerializer):
settings = serializers.SerializerMethodField()
versions = serializers.SerializerMethodField()
scaling_configuration = serializers.SerializerMethodField()
chunking_configuration = serializers.SerializerMethodField()
groups = serializers.SlugRelatedField(many=True, read_only=False, slug_field='name', required=False, queryset=Group.objects.all())
settings = serializers.SerializerMethodField()
namespace = 'v2-models'

class Meta:
Expand Down Expand Up @@ -45,15 +105,39 @@ def validate(self, attrs):
validate_and_update_groups(self.partial, user, attrs)
validate_data_files(user, attrs.get('data_files'))

if attrs.get('settings'):
attrs['settings'] = ModelParametersSerializer().validate(attrs.get('settings'))

return attrs

def to_internal_value(self, data):
settings = data.get('settings', {})
data = super(AnalysisModelSerializer, self).to_internal_value(data)
data['settings'] = ModelParametersSerializer().to_internal_value(settings)
return data

def update(self, instance, validated_data):
data = validated_data.copy()
settings = data.pop('settings', {})
if settings:
instance.resource_file = create_settings_file(settings, instance.creator)

return super(AnalysisModelSerializer, self).update(instance, validated_data)

def create(self, validated_data):
data = validated_data.copy()
settings = data.pop('settings', {})
if 'request' in self.context:
data['creator'] = self.context.get('request').user
return super(AnalysisModelSerializer, self).create(data)

@swagger_serializer_method(serializer_or_field=serializers.URLField)
instance = super(AnalysisModelSerializer, self).create(data)
if settings:
instance.resource_file = create_settings_file(settings, data['creator'])

instance.save()
return instance

@swagger_serializer_method(serializer_or_field=ModelParametersSerializer)
def get_settings(self, instance):
request = self.context.get('request')
return instance.get_absolute_settings_url(request=request, namespace=self.namespace)
Expand Down
3 changes: 3 additions & 0 deletions src/server/oasisapi/analysis_models/v2_api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..models import AnalysisModel, SettingsTemplate
from .serializers import (
AnalysisModelSerializer,
AnalysisModelListSerializer,
ModelVersionsSerializer,
CreateTemplateSerializer,
TemplateSerializer,
Expand Down Expand Up @@ -194,6 +195,8 @@ class AnalysisModelViewSet(VerifyGroupAccessModelViewSet):
def get_serializer_class(self):
if self.action in ['resource_file', 'set_resource_file']:
return RelatedFileSerializer
elif self.action in ['list', 'retrieve']:
return AnalysisModelListSerializer
elif self.action in ['data_files']:
return DataFileSerializer
elif self.action in ['versions']:
Expand Down

0 comments on commit 51ade55

Please sign in to comment.