You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/04/08 07:02:18 UTC
(airavata-portals) 08/21: refactor: remove obsolete thrift_utils.py (replaced by proto_utils.py)
This is an automated email from the ASF dual-hosted git repository.
yasith pushed a commit to branch feat/sdk-facade-migration
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
commit 51caed7873393615a0836d4c5cb35ed2982c1d77
Author: yasithdev <ya...@icloud.com>
AuthorDate: Wed Apr 8 00:33:02 2026 -0500
refactor: remove obsolete thrift_utils.py (replaced by proto_utils.py)
---
.../django_airavata/apps/api/thrift_utils.py | 258 ---------------------
airavata-django-portal/django_airavata/settings.py | 2 +-
2 files changed, 1 insertion(+), 259 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/api/thrift_utils.py b/airavata-django-portal/django_airavata/apps/api/thrift_utils.py
deleted file mode 100644
index d4e6884f4..000000000
--- a/airavata-django-portal/django_airavata/apps/api/thrift_utils.py
+++ /dev/null
@@ -1,258 +0,0 @@
-"""
-Used to create Django Rest Framework serializers for Apache Thrift Data Types
-"""
-import copy
-import datetime
-import logging
-import enum
-
-from rest_framework.serializers import (
- BooleanField,
- CharField,
- DateTimeField,
- DecimalField,
- DictField,
- Field,
- IntegerField,
- ListField,
- ListSerializer,
- Serializer,
- SerializerMetaclass,
- ValidationError
-)
-from thrift.Thrift import TType
-from airavata.model.experiment.ttypes import ExperimentType
-from airavata.model.status.ttypes import ExperimentState
-from airavata.model.application.io.ttypes import DataType
-from airavata.model.appcatalog.parallelism.ttypes import ApplicationParallelismType
-
-logger = logging.getLogger(__name__)
-
-# used to map apache thrift data types to django serializer fields
-mapping = {
- TType.STRING: CharField,
- TType.I08: IntegerField,
- TType.I16: IntegerField,
- TType.I32: IntegerField,
- TType.I64: IntegerField,
- TType.DOUBLE: DecimalField,
- TType.BOOL: BooleanField,
- TType.MAP: DictField
-}
-
-
-class UTCPosixTimestampDateTimeField(DateTimeField):
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.default = self.current_time_ms
- self.initial = self.initial_value
- self.required = False
-
- def to_representation(self, obj):
- # Create datetime instance from milliseconds that is aware of timezon
- dt = datetime.datetime.fromtimestamp(obj / 1000, datetime.timezone.utc)
- return super().to_representation(dt)
-
- def to_internal_value(self, data):
- dt = super().to_internal_value(data)
- return int(dt.timestamp() * 1000)
-
- def initial_value(self):
- return self.to_representation(self.current_time_ms())
-
- def current_time_ms(self):
- return int(datetime.datetime.utcnow().timestamp() * 1000)
-
-
-class ThriftEnumField(Field):
-
- def __init__(self, enumClass, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.enumClass = enumClass
-
- def to_representation(self, obj):
- # For IntEnum, obj is the enum member, its `.name` is the string
- if obj is None:
- return None
- return obj.name
-
- def to_internal_value(self, data):
- # Convert string name back into an IntEnum member
- if self.allow_null and data is None:
- return None
- try:
- return self.enumClass[data]
- except KeyError:
- raise ValidationError(f"'{data}' is not a valid name for enum {self.enumClass.__name__}")
-
-
-def create_serializer(thrift_data_type, enable_date_time_conversion=False, **kwargs):
- """
- Create django rest framework serializer based on the thrift data type
- :param thrift_data_type: Thrift data type
- :param kwargs: Other Django Framework Serializer initialization parameters
- :param enable_date_time_conversion: enable conversion of field with name ending with time to
- UTCPosixTimestampDateTimeField instead of IntegerField
- :return: instance of custom serializer for the given thrift data type
- """
- return create_serializer_class(thrift_data_type, enable_date_time_conversion)(**kwargs)
-
-
-def create_serializer_class(thrift_data_type, enable_date_time_conversion=False):
- class CustomSerializerMeta(SerializerMetaclass):
-
- def __new__(cls, name, bases, attrs):
- meta = attrs.get('Meta', None)
- thrift_spec = thrift_data_type.thrift_spec
- for field in thrift_spec:
- # Don't replace existing attrs to allow subclasses to override
- if field and field[2] not in attrs:
- required = (field[2] in meta.required
- if meta and hasattr(meta, 'required')
- else False)
- read_only = (field[2] in meta.read_only
- if meta and hasattr(meta, 'read_only')
- else False)
- allow_null = not required
- field_serializer = process_field(
- field, enable_date_time_conversion, required=required, read_only=read_only,
- allow_null=allow_null)
- attrs[field[2]] = field_serializer
- return super().__new__(cls, name, bases, attrs)
-
- class CustomSerializer(Serializer, metaclass=CustomSerializerMeta):
- """
- Custom Serializer which handle the list fields which holds custom class objects
- """
-
- def process_nested_fields(self, validated_data):
- fields = self.fields
- params = copy.deepcopy(validated_data)
- for field_name, serializer in fields.items():
- if (isinstance(serializer, ListField) or
- isinstance(serializer, ListSerializer)):
- if (params.get(field_name, None) is not None or
- not serializer.allow_null):
- if isinstance(serializer.child, Serializer):
- # If this is a list of experiment inputs, need to manually
- # convert the 'type' field from an integer to a DataType enum
- if field_name == 'experimentInputs' and 'type' in serializer.child.fields:
- for item in params[field_name]:
- if 'type' in item and isinstance(item['type'], int):
- item['type'] = DataType(item['type'])
- elif field_name == 'experimentOutputs' and 'type' in serializer.child.fields:
- for item in params[field_name]:
- if 'type' in item and isinstance(item['type'], int):
- item['type'] = DataType(item['type'])
- elif field_name == 'experimentStatus' and 'state' in serializer.child.fields:
- for item in params[field_name]:
- if 'state' in item and isinstance(item['state'], int):
- item['state'] = ExperimentState(item['state'])
- params[field_name] = [serializer.child.create(
- item) for item in params[field_name]]
- else:
- params[field_name] = serializer.to_representation(
- params[field_name])
- elif isinstance(serializer, Serializer):
- if field_name in params and params[field_name] is not None:
- params[field_name] = serializer.create(
- params[field_name])
- return params
-
- def create(self, validated_data):
- params = self.process_nested_fields(validated_data)
-
- # The Thrift models expect a mandatory ID but provide a default value for creation.
- # The latest library upgrade is tight and fails if an ID is explicitly passed as `None`.
- # This logic removes such fields allowing the default to be used.
- thrift_spec = thrift_data_type.thrift_spec
- for field_spec in thrift_spec:
- if field_spec:
- field_name = field_spec[2]
- default_value = field_spec[4]
- if default_value is not None:
- if field_name in params and params[field_name] is None:
- del params[field_name]
-
- if (thrift_data_type.__name__ == 'ExperimentModel' and
- 'experimentType' in params and isinstance(params['experimentType'], int)):
- params['experimentType'] = ExperimentType(params['experimentType'])
-
- if (thrift_data_type.__name__ == 'ApplicationDeploymentDescription' and
- 'parallelism' in params and isinstance(params['parallelism'], int)):
- params['parallelism'] = ApplicationParallelismType(params['parallelism'])
-
- return thrift_data_type(**params)
-
- def update(self, instance, validated_data):
- return self.create(validated_data)
-
- return CustomSerializer
-
-
-def process_field(field, enable_date_time_conversion, required=False, read_only=False, allow_null=False):
- """
- Used to process a thrift data type field
- :param field:
- :param required:
- :param read_only:
- :param allow_null:
- :return:
- """
- if field[1] in mapping:
- # handling scenarios when the thrift field type is present in the
- # mapping
- field_class = mapping[field[1]]
- kwargs = dict(required=required, read_only=read_only)
- # allow_null isn't allowed for BooleanField
- if field_class not in (BooleanField,):
- kwargs['allow_null'] = allow_null
- # allow_null CharField are also allowed to be blank
- if field_class == CharField:
- kwargs['allow_blank'] = allow_null
- thrift_model_class = mapping[field[1]]
-
- if thrift_model_class == IntegerField and field[3] is not None and isinstance(field[3], type) and issubclass(field[3], enum.IntEnum):
- return ThriftEnumField(field[3], required=required, read_only=read_only, allow_null=allow_null)
-
- if enable_date_time_conversion and thrift_model_class == IntegerField and field[2].lower().endswith("time"):
- thrift_model_class = UTCPosixTimestampDateTimeField
- return thrift_model_class(**kwargs)
- elif field[1] == TType.LIST:
- # handling scenario when the thrift field type is list
- list_field_serializer = process_list_field(field)
- return ListField(child=list_field_serializer,
- required=required,
- read_only=read_only,
- allow_null=allow_null)
- elif field[1] == TType.STRUCT:
- # handling scenario when the thrift field type is struct
- return create_serializer(field[3][0],
- required=required,
- read_only=read_only,
- allow_null=allow_null)
-
-
-def process_list_field(field):
- """
- Used to process thrift list type field
- :param field:
- :return:
- """
- list_details = field[3]
- item_ttype = list_details[0]
- # For enums, extra type info is the enum Class (e.g. ExperimentState)
- # For structs, it's a tuple of (StructClass, StructClass.thrift_spec)
- item_type_info = list_details[1]
-
- if (item_ttype == TType.I32 and
- item_type_info is not None and
- isinstance(item_type_info, type) and
- issubclass(item_type_info, enum.IntEnum)):
- return ThriftEnumField(item_type_info)
-
- if item_ttype in mapping:
- return mapping[item_ttype]()
- elif item_ttype == TType.STRUCT:
- return create_serializer(item_type_info[0])
diff --git a/airavata-django-portal/django_airavata/settings.py b/airavata-django-portal/django_airavata/settings.py
index b4caa1635..47939a8ae 100644
--- a/airavata-django-portal/django_airavata/settings.py
+++ b/airavata-django-portal/django_airavata/settings.py
@@ -62,7 +62,7 @@ INSTALLED_APPS = [
'wagtail.images',
'wagtail.search',
'wagtail.admin',
- 'wagtail.core',
+ 'wagtail',
'wagtail.contrib.styleguide',
# wagtail third party dependencies