This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git


The following commit(s) were added to refs/heads/main by this push:
     new 1a08f2acc Add resource type selection and AWS/SLURM preference support
1a08f2acc is described below

commit 1a08f2acc70c90c5f063f87963d34ff1c6a81d78
Author: lahiruj <[email protected]>
AuthorDate: Tue Nov 18 16:05:11 2025 -0500

    Add resource type selection and AWS/SLURM preference support
    
    - Add ResourceType enum model for frontend resource type handling
    - Implement AwsComputeResourcePreference and SlurmComputeResourcePreference 
models
    - Add GroupAccountSSHProvisionerConfig model for SSH account provisioning
    - Update GroupComputeResourcePreference to handle resource type and 
specific preferences
    - ComputePreference.vue UI to support AWS and SLURM resource types
    - Update serializers to properly handle resourceType and 
specificPreferences types
---
 .../ComputePreference.vue                          | 179 ++++-
 .../django_airavata/apps/api/serializers.py        | 853 ++++++++++++++++++++-
 .../api/static/django_airavata_api/js/index.js     |   8 +
 .../js/models/AwsComputeResourcePreference.js      |  21 +
 .../js/models/GroupAccountSSHProvisionerConfig.js  |  18 +
 .../js/models/GroupComputeResourcePreference.js    | 235 +++++-
 .../django_airavata_api/js/models/ResourceType.js  |   5 +
 .../js/models/SlurmComputeResourcePreference.js    |  49 ++
 .../django_airavata/apps/api/views.py              | 156 ++++
 9 files changed, 1451 insertions(+), 73 deletions(-)

diff --git 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
index 4c4b41d7f..7bbd2a2e2 100644
--- 
a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
+++ 
b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputePreference.vue
@@ -71,17 +71,78 @@
               </ssh-credential-selector>
             </b-form-group>
             <b-form-group
-              label="Allocation Project Number"
-              label-for="allocation-number"
+              label="Resource Type"
+              label-for="resource-type"
+              
:invalid-feedback="validationFeedback.resourceType.invalidFeedback"
+              :state="validationFeedback.resourceType.state"
             >
-              <b-form-input
-                id="allocation-number"
-                type="text"
-                v-model="data.allocationProjectNumber"
+              <b-form-select
+                id="resource-type"
+                v-model="data.resourceType"
+                :options="resourceTypeOptions"
                 :disabled="!userHasWriteAccess"
+                :state="validationFeedback.resourceType.state"
+                @change="onResourceTypeChange"
               >
-              </b-form-input>
+                <template slot="first">
+                  <option :value="null">Select a resource type</option>
+                </template>
+              </b-form-select>
             </b-form-group>
+            <!-- SLURM-specific fields -->
+            <template v-if="isResourceType('SLURM')">
+              <b-form-group
+                label="Allocation Project Number"
+                label-for="allocation-number"
+              >
+                <b-form-input
+                  id="allocation-number"
+                  type="text"
+                  v-model="data.allocationProjectNumber"
+                  :disabled="!userHasWriteAccess"
+                >
+                </b-form-input>
+              </b-form-group>
+            </template>
+            <!-- AWS-specific fields -->
+            <template v-if="isResourceType('AWS')">
+              <b-form-group
+                label="Region"
+                label-for="aws-region"
+              >
+                <b-form-input
+                  id="aws-region"
+                  type="text"
+                  v-model="data.specificPreferences.region"
+                  :disabled="!userHasWriteAccess"
+                >
+                </b-form-input>
+              </b-form-group>
+              <b-form-group
+                label="Preferred AMI ID"
+                label-for="preferred-ami-id"
+              >
+                <b-form-input
+                  id="preferred-ami-id"
+                  type="text"
+                  v-model="data.specificPreferences.preferredAmiId"
+                  :disabled="!userHasWriteAccess"
+                >
+                </b-form-input>
+              </b-form-group>
+              <b-form-group
+                label="Preferred Instance Type"
+                label-for="preferred-instance-type"
+              >
+                <b-form-input
+                  id="preferred-instance-type"
+                  type="text"
+                  v-model="data.specificPreferences.preferredInstanceType"
+                  :disabled="!userHasWriteAccess"
+                >
+                </b-form-input>
+              </b-form-group>
+            </template>
             <b-form-group
               label="Scratch Location"
               label-for="scratch-location"
@@ -133,6 +194,7 @@
         <div class="card">
           <div class="card-body">
             <compute-resource-reservation-list
+              v-if="isResourceType('SLURM')"
               :reservations="data.reservations"
               :queues="queueNames"
               :readonly="!userHasWriteAccess"
@@ -147,40 +209,35 @@
       </div>
     </div>
     <div class="fixed-footer">
-      <b-button 
-      variant="primary" 
-      @click="save" 
-      :disabled="!valid || !userHasWriteAccess"
-        >Save</b-button
+      <b-button
+        variant="primary"
+        @click="save"
+        :disabled="!valid || !userHasWriteAccess"
+      >Save
+      </b-button
       >
-      <delete-button 
-      class="ml-2" 
-      :disabled="!userHasWriteAccess"
-      @delete="remove">
+      <delete-button
+        class="ml-2"
+        :disabled="!userHasWriteAccess"
+        @delete="remove">
         Are you sure you want to remove the preferences for compute resource
         <strong>{{ computeResource.hostName }}</strong
         >?
       </delete-button>
       <b-button class="ml-2" variant="secondary" @click="cancel"
-        >Cancel</b-button
+      >Cancel
+      </b-button
       >
     </div>
   </div>
 </template>
 
 <script>
-import DjangoAiravataAPI from "django-airavata-api";
+import DjangoAiravataAPI, {errors, models, services} from 
"django-airavata-api";
 import SSHCredentialSelector from 
"../../credentials/SSHCredentialSelector.vue";
 import ComputeResourceReservationList from "./ComputeResourceReservationList";
 import ComputeResourcePolicyEditor from "./ComputeResourcePolicyEditor";
-
-import { models, services, errors } from "django-airavata-api";
-import {
-  mixins,
-  notifications,
-  errors as uiErrors,
-  components,
-} from "django-airavata-common-ui";
+import {components, errors as uiErrors, mixins, notifications,} from 
"django-airavata-common-ui";
 
 export default {
   name: "compute-preference",
@@ -210,11 +267,11 @@ export default {
   },
   mounted: function () {
     const computeResourcePromise = this.fetchComputeResource(this.host_id);
-    if (this.localGroupResourceProfile){
-        this.userHasWriteAccess = 
this.localGroupResourceProfile.userHasWriteAccess;
+    if (this.localGroupResourceProfile) {
+      this.userHasWriteAccess = 
this.localGroupResourceProfile.userHasWriteAccess;
     }
     if (!this.value && this.id && this.host_id) {
-      services.GroupResourceProfileService.retrieve({ lookup: this.id }).then(
+      services.GroupResourceProfileService.retrieve({lookup: this.id}).then(
         (groupResourceProfile) => {
           this.localGroupResourceProfile = groupResourceProfile;
           this.userHasWriteAccess = 
this.localGroupResourceProfile.userHasWriteAccess;
@@ -240,19 +297,19 @@ export default {
     } else if (!this.computeResourcePolicy) {
       this.createDefaultComputeResourcePolicy(computeResourcePromise);
     }
-    if (!this.id){
-      this.userHasWriteAccess=true;
+    if (!this.id) {
+      this.userHasWriteAccess = true;
     }
     this.$on("input", this.validate);
-    
+
   },
   data: function () {
     return {
       data: this.value
         ? this.value.clone()
         : new models.GroupComputeResourcePreference({
-            computeResourceId: this.host_id,
-          }),
+          computeResourceId: this.host_id,
+        }),
       localGroupResourceProfile: this.groupResourceProfile
         ? this.groupResourceProfile.clone()
         : null,
@@ -270,6 +327,10 @@ export default {
       reservationsInvalid: false,
       computeResourcePolicyInvalid: false,
       userHasWriteAccess: false,
+      resourceTypeOptions: models.ResourceType.values.map(rt => ({
+        value: rt,
+        text: rt.name,
+      })),
     };
   },
   computed: {
@@ -331,7 +392,7 @@ export default {
             this.validationErrors =
               error.details.response.computePreferences[
                 computePreferencesIndex
-              ];
+                ];
           } else {
             this.validationErrors = null;
             notifications.NotificationList.addError(error);
@@ -341,13 +402,13 @@ export default {
     saveOrUpdate(groupResourceProfile) {
       if (this.id) {
         return DjangoAiravataAPI.services.GroupResourceProfileService.update(
-          { data: groupResourceProfile, lookup: this.id },
-          { ignoreErrors: true }
+          {data: groupResourceProfile, lookup: this.id},
+          {ignoreErrors: true}
         );
       } else {
         return DjangoAiravataAPI.services.GroupResourceProfileService.create(
-          { data: groupResourceProfile },
-          { ignoreErrors: true }
+          {data: groupResourceProfile},
+          {ignoreErrors: true}
         );
       }
     },
@@ -379,12 +440,12 @@ export default {
       if (this.id) {
         this.$router.push({
           name: "group_resource_preference",
-          params: { id: this.id },
+          params: {id: this.id},
         });
       } else {
         this.$router.push({
           name: "new_group_resource_preference",
-          params: { value: this.localGroupResourceProfile },
+          params: {value: this.localGroupResourceProfile},
         });
       }
     },
@@ -424,13 +485,47 @@ export default {
       );
       this.data.reservations.splice(reservationIndex, 1, reservation);
     },
+    onResourceTypeChange() {
+      if (!this.data.resourceType) {
+        this.data.specificPreferences = null;
+        this.validate();
+        return;
+      }
+      if (this.data.resetSpecificPreferences) {
+        this.data.resetSpecificPreferences();
+      } else {
+        const resourceTypeName = this.data.resourceType.name;
+        const modelClassName = 
this._getPreferenceModelClassName(resourceTypeName);
+        if (modelClassName && models[modelClassName]) {
+          const PreferenceModel = models[modelClassName];
+          this.data.specificPreferences = new PreferenceModel();
+        } else {
+          this.data.specificPreferences = null;
+        }
+      }
+      this.validate();
+    },
+    _getPreferenceModelClassName(resourceTypeName) {
+      // Convert resource type name to model class name
+      // 'SLURM' -> 'SlurmComputeResourcePreference'
+      // 'AWS' -> 'AwsComputeResourcePreference'
+      if (!resourceTypeName) return null;
+      const capitalized = resourceTypeName.charAt(0) + 
resourceTypeName.slice(1).toLowerCase();
+      return capitalized + 'ComputeResourcePreference';
+    },
+    isResourceType(resourceTypeName) {
+      if (this.data.isResourceType) {
+        return this.data.isResourceType(resourceTypeName);
+      }
+      return this.data.resourceType && this.data.resourceType.name === 
resourceTypeName;
+    },
   },
   beforeRouteEnter: function (to, from, next) {
     // If we don't have the Group Resource Profile id or instance, then the
     // Group Resource Profile wasn't created and we need to just go back to
     // the dashboard
     if (!to.params.id && !to.params.groupResourceProfile) {
-      next({ name: "group_resource_preference_dashboard" });
+      next({name: "group_resource_preference_dashboard"});
     } else {
       next();
     }
diff --git a/airavata-django-portal/django_airavata/apps/api/serializers.py 
b/airavata-django-portal/django_airavata/apps/api/serializers.py
index 70c6af6f7..128d5b20b 100644
--- a/airavata-django-portal/django_airavata/apps/api/serializers.py
+++ b/airavata-django-portal/django_airavata/apps/api/serializers.py
@@ -26,7 +26,10 @@ from airavata.model.appcatalog.gatewayprofile.ttypes import (
 from airavata.model.appcatalog.groupresourceprofile.ttypes import (
     ComputeResourceReservation,
     GroupComputeResourcePreference,
-    GroupResourceProfile
+    GroupResourceProfile,
+    ResourceType,
+    SlurmComputeResourcePreference,
+    AwsComputeResourcePreference
 )
 from airavata.model.appcatalog.parser.ttypes import Parser
 from airavata.model.appcatalog.storageresource.ttypes import (
@@ -751,9 +754,513 @@ class GroupComputeResourcePreferenceSerializer(
 
         return []
 
+    @staticmethod
+    def _convert_nested_list_fields_to_thrift(slurm_pref):
+        from collections import OrderedDict
+        from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+            ComputeResourceReservation,
+            GroupAccountSSHProvisionerConfig
+        )
+
+        if hasattr(slurm_pref, 'reservations') and slurm_pref.reservations:
+            if isinstance(slurm_pref.reservations, list):
+                converted_reservations = []
+                for res in slurm_pref.reservations:
+                    if isinstance(res, (dict, OrderedDict)):
+                        
converted_reservations.append(ComputeResourceReservation(**res))
+                    else:
+                        converted_reservations.append(res)
+                slurm_pref.reservations = converted_reservations
+
+        if hasattr(slurm_pref, 'groupSSHAccountProvisionerConfigs') and 
slurm_pref.groupSSHAccountProvisionerConfigs:
+            if isinstance(slurm_pref.groupSSHAccountProvisionerConfigs, list):
+                converted_configs = []
+                for cfg in slurm_pref.groupSSHAccountProvisionerConfigs:
+                    if isinstance(cfg, (dict, OrderedDict)):
+                        
converted_configs.append(GroupAccountSSHProvisionerConfig(**cfg))
+                    else:
+                        converted_configs.append(cfg)
+                slurm_pref.groupSSHAccountProvisionerConfigs = 
converted_configs
+
+    @staticmethod
+    def _convert_specific_preferences_dict_to_thrift(pref_instance, 
resource_type):
+        from collections import OrderedDict
+
+        if not hasattr(pref_instance, 'specificPreferences'):
+            return
+
+        if isinstance(pref_instance.specificPreferences, (dict, OrderedDict)):
+            specific_prefs_dict = pref_instance.specificPreferences
+
+            union_type_class = None
+            try:
+                from airavata.model.appcatalog.groupresourceprofile.ttypes 
import (
+                    EnvironmentSpecificPreferences
+                )
+                union_type_class = EnvironmentSpecificPreferences
+                log.debug(
+                    "GCPreference: Got union type class from import: %s",
+                    union_type_class.__name__,
+                )
+            except ImportError as e:
+                log.error(
+                    "GCPreference: Failed to import 
EnvironmentSpecificPreferences: %s",
+                    str(e),
+                    exc_info=True,
+                )
+
+            if union_type_class:
+                pref_instance.specificPreferences = union_type_class()
+
+                if resource_type == ResourceType.SLURM:
+                    if 'slurm' in specific_prefs_dict:
+                        slurm_data = specific_prefs_dict['slurm']
+                    else:
+                        slurm_data = specific_prefs_dict
+
+                    if slurm_data and isinstance(slurm_data, dict) and 
len(slurm_data) > 0:
+                        try:
+                            from collections import OrderedDict
+                            from 
airavata.model.appcatalog.groupresourceprofile.ttypes import (
+                                ComputeResourceReservation,
+                                GroupAccountSSHProvisionerConfig
+                            )
+
+                            if 'reservations' in slurm_data and 
slurm_data['reservations']:
+                                reservations_list = slurm_data['reservations']
+                                if isinstance(reservations_list, list):
+                                    converted_reservations = []
+                                    for res in reservations_list:
+                                        if isinstance(res, (dict, 
OrderedDict)):
+                                            
converted_reservations.append(ComputeResourceReservation(**res))
+                                        else:
+                                            converted_reservations.append(res)
+                                    slurm_data['reservations'] = 
converted_reservations
+
+                            if 'groupSSHAccountProvisionerConfigs' in 
slurm_data and slurm_data['groupSSHAccountProvisionerConfigs']:
+                                configs_list = 
slurm_data['groupSSHAccountProvisionerConfigs']
+                                if isinstance(configs_list, list):
+                                    converted_configs = []
+                                    for cfg in configs_list:
+                                        if isinstance(cfg, (dict, 
OrderedDict)):
+                                            
converted_configs.append(GroupAccountSSHProvisionerConfig(**cfg))
+                                        else:
+                                            converted_configs.append(cfg)
+                                    
slurm_data['groupSSHAccountProvisionerConfigs'] = converted_configs
+
+                            slurm_pref = 
SlurmComputeResourcePreference(**slurm_data)
+                            pref_instance.specificPreferences.slurm = 
slurm_pref
+                            
GroupComputeResourcePreferenceSerializer._convert_nested_list_fields_to_thrift(slurm_pref)
+                            log.info(
+                                "GCPreference: Converted specificPreferences 
dict to SLURM Thrift union type, computeResourceId=%s",
+                                pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                            )
+                        except Exception as e:
+                            log.error(
+                                "GCPreference: Failed to create 
SlurmComputeResourcePreference from dict: %s, computeResourceId=%s",
+                                str(e),
+                                pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                                exc_info=True,
+                            )
+                    else:
+                        log.info(
+                            "GCPreference: specificPreferences dict is empty, 
created empty union type, computeResourceId=%s",
+                            pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                        )
+                elif resource_type == ResourceType.AWS:
+                    if 'aws' in specific_prefs_dict:
+                        aws_data = specific_prefs_dict['aws']
+                    else:
+                        aws_data = specific_prefs_dict
+
+                    if aws_data and isinstance(aws_data, dict) and 
len(aws_data) > 0:
+                        try:
+                            aws_pref = AwsComputeResourcePreference(**aws_data)
+                            pref_instance.specificPreferences.aws = aws_pref
+                            log.info(
+                                "GCPreference: Converted specificPreferences 
dict to AWS Thrift union type, computeResourceId=%s",
+                                pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                            )
+                        except Exception as e:
+                            log.error(
+                                "GCPreference: Failed to create 
AwsComputeResourcePreference from dict: %s, computeResourceId=%s",
+                                str(e),
+                                pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                                exc_info=True,
+                            )
+                    else:
+                        log.info(
+                            "GCPreference: specificPreferences dict is empty, 
created empty union type, computeResourceId=%s",
+                            pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                        )
+            else:
+                log.error(
+                    "GCPreference: Could not get union type class to convert 
specificPreferences dict, computeResourceId=%s",
+                    pref_instance.computeResourceId if hasattr(pref_instance, 
'computeResourceId') else 'unknown',
+                )
+                union_type_created = False
+                try:
+                    test_instance = 
GroupComputeResourcePreference(resourceType=resource_type)
+                    if test_instance.specificPreferences is not None:
+                        pref_instance.specificPreferences = 
type(test_instance.specificPreferences)()
+                        union_type_created = True
+                        log.info(
+                            "GCPreference: Created empty union type from test 
instance, computeResourceId=%s",
+                            pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                        )
+                except Exception as e:
+                    log.warning(
+                        "GCPreference: Failed to create union type from test 
instance: %s, trying direct construction",
+                        str(e),
+                    )
+
+                if not union_type_created:
+                    if hasattr(pref_instance, 'resourceType') and 
pref_instance.resourceType:
+                        try:
+                            temp = 
GroupComputeResourcePreference(resourceType=pref_instance.resourceType)
+                            if temp.specificPreferences is not None:
+                                pref_instance.specificPreferences = 
type(temp.specificPreferences)()
+                                union_type_created = True
+                                log.info(
+                                    "GCPreference: Created empty union type 
using pref_instance.resourceType, computeResourceId=%s",
+                                    pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                                )
+                        except Exception as e2:
+                            log.error(
+                                "GCPreference: All attempts to create union 
type failed: %s, computeResourceId=%s",
+                                str(e2),
+                                pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                                exc_info=True,
+                            )
+
+                if not union_type_created:
+                    log.error(
+                        "GCPreference: Could not create union type at all! 
specificPreferences will remain as dict, computeResourceId=%s",
+                        pref_instance.computeResourceId if 
hasattr(pref_instance, 'computeResourceId') else 'unknown',
+                    )
+        else:
+            if hasattr(pref_instance.specificPreferences, 'slurm') and 
pref_instance.specificPreferences.slurm:
+                
GroupComputeResourcePreferenceSerializer._convert_nested_list_fields_to_thrift(
+                    pref_instance.specificPreferences.slurm
+                )
+
+    def to_representation(self, instance):
+        """
+        Override to extract fields from specificPreferences union type.
+        """
+        ret = super().to_representation(instance)
+
+        if hasattr(instance, 'specificPreferences') and 
instance.specificPreferences:
+            if hasattr(instance.specificPreferences, 'slurm') and 
instance.specificPreferences.slurm:
+                slurm_pref = instance.specificPreferences.slurm
+                if hasattr(slurm_pref, 'allocationProjectNumber'):
+                    ret['allocationProjectNumber'] = 
slurm_pref.allocationProjectNumber
+                if 'specificPreferences' in ret and 
isinstance(ret['specificPreferences'], dict):
+                    if 'slurm' not in ret['specificPreferences']:
+                        ret['specificPreferences'] = {'slurm': 
ret['specificPreferences']}
+            elif hasattr(instance.specificPreferences, 'aws') and 
instance.specificPreferences.aws:
+                aws_pref = instance.specificPreferences.aws
+                aws_fields = {
+                    'region': getattr(aws_pref, 'region', None),
+                    'preferredAmiId': getattr(aws_pref, 'preferredAmiId', 
None),
+                    'preferredInstanceType': getattr(aws_pref, 
'preferredInstanceType', None),
+                }
+                ret['specificPreferences'] = aws_fields
+
+        return ret
+
+    def create(self, validated_data):
+        """
+        Override create() to properly handle resourceType and 
specificPreferences union type.
+        """
+        if isinstance(validated_data, GroupComputeResourcePreference):
+            resource_type = None
+            if not hasattr(validated_data, 'resourceType') or 
validated_data.resourceType is None:
+                from collections import OrderedDict
+                if hasattr(validated_data, 'specificPreferences') and 
validated_data.specificPreferences:
+                    if isinstance(validated_data.specificPreferences, (dict, 
OrderedDict)):
+                        specific_prefs_dict = 
validated_data.specificPreferences
+                        if 'slurm' in specific_prefs_dict or 
'allocationProjectNumber' in specific_prefs_dict:
+                            resource_type = ResourceType.SLURM
+                        elif 'aws' in specific_prefs_dict or 'region' in 
specific_prefs_dict:
+                            resource_type = ResourceType.AWS
+                        else:
+                            resource_type = ResourceType.SLURM
+                    elif hasattr(validated_data.specificPreferences, 'slurm') 
and validated_data.specificPreferences.slurm:
+                        resource_type = ResourceType.SLURM
+                    elif hasattr(validated_data.specificPreferences, 'aws') 
and validated_data.specificPreferences.aws:
+                        resource_type = ResourceType.AWS
+                    else:
+                        resource_type = ResourceType.SLURM
+                else:
+                    resource_type = ResourceType.SLURM
+
+                if resource_type:
+                    validated_data.resourceType = resource_type
+            else:
+                resource_type = validated_data.resourceType
+
+            if resource_type:
+                
self._convert_specific_preferences_dict_to_thrift(validated_data, resource_type)
+
+            return validated_data
+
+        data = copy.deepcopy(validated_data)
+
+        resource_type = data.get('resourceType')
+        if resource_type is None:
+            if 'allocationProjectNumber' in data or ('specificPreferences' in 
data and isinstance(data.get('specificPreferences'), dict) and 'slurm' in 
data.get('specificPreferences', {})):
+                resource_type = ResourceType.SLURM
+            elif 'specificPreferences' in data and 
isinstance(data.get('specificPreferences'), dict) and 'aws' in 
data.get('specificPreferences', {}):
+                resource_type = ResourceType.AWS
+            elif 'region' in data or 'preferredAmiId' in data or 
'preferredInstanceType' in data:
+                resource_type = ResourceType.AWS
+            else:
+                resource_type = ResourceType.SLURM
+
+        if isinstance(resource_type, str):
+            try:
+                resource_type = ResourceType[resource_type]
+            except (KeyError, AttributeError):
+                resource_type = ResourceType.SLURM
+        elif isinstance(resource_type, int):
+            try:
+                resource_type = ResourceType(resource_type)
+            except (ValueError, AttributeError):
+                resource_type = ResourceType.SLURM
+
+        data['resourceType'] = resource_type
+
+        specific_prefs = data.pop('specificPreferences', None)
+
+        slurm_data = {}
+        aws_data = {}
+
+        slurm_fields = ['allocationProjectNumber', 'preferredBatchQueue', 
'qualityOfService',
+                       'usageReportingGatewayId', 'sshAccountProvisioner',
+                       'groupSSHAccountProvisionerConfigs', 
'sshAccountProvisionerAdditionalInfo',
+                       'reservations']
+        aws_fields = ['region', 'preferredAmiId', 'preferredInstanceType']
+
+        for field in slurm_fields:
+            if field in data:
+                slurm_data[field] = data.pop(field)
+        for field in aws_fields:
+            if field in data:
+                aws_data[field] = data.pop(field)
+
+        thrift_spec = GroupComputeResourcePreference.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 data and data[field_name] is None:
+                        del data[field_name]
+
+        instance = GroupComputeResourcePreference(**data)
+
+        instance.resourceType = resource_type
+
+        union_type_class = None
+        try:
+            from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+                EnvironmentSpecificPreferences
+            )
+            union_type_class = EnvironmentSpecificPreferences
+        except ImportError as e:
+            log.error(
+                "GCPreference create: Failed to import 
EnvironmentSpecificPreferences: %s",
+                str(e),
+                exc_info=True,
+            )
+
+        if specific_prefs is None:
+            if resource_type == ResourceType.SLURM and slurm_data:
+                from collections import OrderedDict
+                from airavata.model.appcatalog.groupresourceprofile.ttypes 
import (
+                    GroupAccountSSHProvisionerConfig
+                )
+
+                if 'reservations' in slurm_data and slurm_data['reservations']:
+                    reservations_list = slurm_data['reservations']
+                    if isinstance(reservations_list, list):
+                        converted_reservations = []
+                        for res in reservations_list:
+                            if isinstance(res, (dict, OrderedDict)):
+                                
converted_reservations.append(ComputeResourceReservation(**res))
+                            else:
+                                converted_reservations.append(res)
+                        slurm_data['reservations'] = converted_reservations
+
+                if 'groupSSHAccountProvisionerConfigs' in slurm_data and 
slurm_data['groupSSHAccountProvisionerConfigs']:
+                    configs_list = 
slurm_data['groupSSHAccountProvisionerConfigs']
+                    if isinstance(configs_list, list):
+                        converted_configs = []
+                        for cfg in configs_list:
+                            if isinstance(cfg, (dict, OrderedDict)):
+                                
converted_configs.append(GroupAccountSSHProvisionerConfig(**cfg))
+                            else:
+                                converted_configs.append(cfg)
+                        slurm_data['groupSSHAccountProvisionerConfigs'] = 
converted_configs
+
+                slurm_pref = SlurmComputeResourcePreference(**slurm_data)
+                if union_type_class:
+                    instance.specificPreferences = union_type_class()
+                    instance.specificPreferences.slurm = slurm_pref
+                else:
+                    try:
+                        test_instance = 
GroupComputeResourcePreference(resourceType=resource_type)
+                        if test_instance.specificPreferences is not None:
+                            instance.specificPreferences = 
type(test_instance.specificPreferences)()
+                            instance.specificPreferences.slurm = slurm_pref
+                        else:
+                            log.warning(
+                                "GCPreference create: Could not create union 
type, instance may be invalid"
+                            )
+                    except Exception as e:
+                        log.error(
+                            "GCPreference create: Failed to set 
specificPreferences.slurm: %s",
+                            str(e),
+                            exc_info=True,
+                        )
+            elif resource_type == ResourceType.AWS and aws_data:
+                aws_pref = AwsComputeResourcePreference(**aws_data)
+                if union_type_class:
+                    instance.specificPreferences = union_type_class()
+                    instance.specificPreferences.aws = aws_pref
+                else:
+                    try:
+                        test_instance = 
GroupComputeResourcePreference(resourceType=resource_type)
+                        if test_instance.specificPreferences is not None:
+                            instance.specificPreferences = 
type(test_instance.specificPreferences)()
+                            instance.specificPreferences.aws = aws_pref
+                    except Exception as e:
+                        log.error(
+                            "GCPreference create: Failed to set 
specificPreferences.aws: %s",
+                            str(e),
+                            exc_info=True,
+                        )
+        elif isinstance(specific_prefs, dict):
+            if 'slurm' in specific_prefs:
+                slurm_dict = specific_prefs['slurm'].copy() if 
isinstance(specific_prefs['slurm'], dict) else {}
+                slurm_dict.update(slurm_data)
+                from collections import OrderedDict
+                from airavata.model.appcatalog.groupresourceprofile.ttypes 
import (
+                    GroupAccountSSHProvisionerConfig
+                )
+
+                if 'reservations' in slurm_dict and slurm_dict['reservations']:
+                    reservations_list = slurm_dict['reservations']
+                    if isinstance(reservations_list, list):
+                        converted_reservations = []
+                        for res in reservations_list:
+                            if isinstance(res, (dict, OrderedDict)):
+                                
converted_reservations.append(ComputeResourceReservation(**res))
+                            else:
+                                converted_reservations.append(res)
+                        slurm_dict['reservations'] = converted_reservations
+
+                if 'groupSSHAccountProvisionerConfigs' in slurm_dict and 
slurm_dict['groupSSHAccountProvisionerConfigs']:
+                    configs_list = 
slurm_dict['groupSSHAccountProvisionerConfigs']
+                    if isinstance(configs_list, list):
+                        converted_configs = []
+                        for cfg in configs_list:
+                            if isinstance(cfg, (dict, OrderedDict)):
+                                
converted_configs.append(GroupAccountSSHProvisionerConfig(**cfg))
+                            else:
+                                converted_configs.append(cfg)
+                        slurm_dict['groupSSHAccountProvisionerConfigs'] = 
converted_configs
+
+                slurm_pref = SlurmComputeResourcePreference(**slurm_dict)
+                if union_type_class:
+                    instance.specificPreferences = union_type_class()
+                    instance.specificPreferences.slurm = slurm_pref
+            elif 'aws' in specific_prefs:
+                aws_pref = 
AwsComputeResourcePreference(**specific_prefs['aws'])
+                if union_type_class:
+                    instance.specificPreferences = union_type_class()
+                    instance.specificPreferences.aws = aws_pref
+            elif slurm_data:
+                from collections import OrderedDict
+                from airavata.model.appcatalog.groupresourceprofile.ttypes 
import (
+                    GroupAccountSSHProvisionerConfig
+                )
+
+                if 'reservations' in slurm_data and slurm_data['reservations']:
+                    reservations_list = slurm_data['reservations']
+                    if isinstance(reservations_list, list):
+                        converted_reservations = []
+                        for res in reservations_list:
+                            if isinstance(res, (dict, OrderedDict)):
+                                
converted_reservations.append(ComputeResourceReservation(**res))
+                            else:
+                                converted_reservations.append(res)
+                        slurm_data['reservations'] = converted_reservations
+
+                if 'groupSSHAccountProvisionerConfigs' in slurm_data and 
slurm_data['groupSSHAccountProvisionerConfigs']:
+                    configs_list = 
slurm_data['groupSSHAccountProvisionerConfigs']
+                    if isinstance(configs_list, list):
+                        converted_configs = []
+                        for cfg in configs_list:
+                            if isinstance(cfg, (dict, OrderedDict)):
+                                
converted_configs.append(GroupAccountSSHProvisionerConfig(**cfg))
+                            else:
+                                converted_configs.append(cfg)
+                        slurm_data['groupSSHAccountProvisionerConfigs'] = 
converted_configs
+
+                slurm_pref = SlurmComputeResourcePreference(**slurm_data)
+                if union_type_class:
+                    instance.specificPreferences = union_type_class()
+                    instance.specificPreferences.slurm = slurm_pref
+
+        if not hasattr(instance, 'resourceType') or instance.resourceType is 
None:
+            if hasattr(instance, 'specificPreferences') and 
instance.specificPreferences:
+                from collections import OrderedDict
+                if isinstance(instance.specificPreferences, (dict, 
OrderedDict)):
+                    if 'slurm' in instance.specificPreferences or 
'allocationProjectNumber' in instance.specificPreferences:
+                        instance.resourceType = ResourceType.SLURM
+                    elif 'aws' in instance.specificPreferences or 'region' in 
instance.specificPreferences:
+                        instance.resourceType = ResourceType.AWS
+                    else:
+                        instance.resourceType = ResourceType.SLURM
+                elif hasattr(instance.specificPreferences, 'slurm') and 
instance.specificPreferences.slurm:
+                    instance.resourceType = ResourceType.SLURM
+                elif hasattr(instance.specificPreferences, 'aws') and 
instance.specificPreferences.aws:
+                    instance.resourceType = ResourceType.AWS
+                else:
+                    instance.resourceType = ResourceType.SLURM
+            else:
+                instance.resourceType = ResourceType.SLURM
+            log.warning(
+                "GCPreference create: Had to set resourceType=%s at end of 
create(), computeResourceId=%s",
+                instance.resourceType.name if hasattr(instance.resourceType, 
'name') else instance.resourceType,
+                instance.computeResourceId if hasattr(instance, 
'computeResourceId') else 'unknown',
+            )
+
+        if hasattr(instance, 'resourceType') and instance.resourceType:
+            if instance.specificPreferences is None:
+                try:
+                    from airavata.model.appcatalog.groupresourceprofile.ttypes 
import (
+                        EnvironmentSpecificPreferences
+                    )
+                    instance.specificPreferences = 
EnvironmentSpecificPreferences()
+                    log.debug(
+                        "GCPreference create: Initialized empty 
specificPreferences union type, computeResourceId=%s",
+                        instance.computeResourceId if hasattr(instance, 
'computeResourceId') else 'unknown',
+                    )
+                except ImportError as e:
+                    log.warning(
+                        "GCPreference create: Could not initialize empty 
specificPreferences: %s",
+                        str(e),
+                    )
+            self._convert_specific_preferences_dict_to_thrift(instance, 
instance.resourceType)
+
+        return instance
+
 
 class GroupResourceProfileSerializer(
-        thrift_utils.create_serializer_class(GroupResourceProfile)):
+    thrift_utils.create_serializer_class(GroupResourceProfile)):
     url = FullyEncodedHyperlinkedIdentityField(
         view_name='django_airavata_api:group-resource-profile-detail',
         lookup_field='groupResourceProfileId',
@@ -766,38 +1273,348 @@ class GroupResourceProfileSerializer(
     class Meta:
         required = ('groupResourceProfileName',)
 
+    def create(self, validated_data):
+        """
+        Override create() to preserve Thrift instances in computePreferences.
+        """
+        compute_prefs = validated_data.get('computePreferences')
+        if compute_prefs:
+            all_thrift_instances = all(
+                isinstance(item, GroupComputeResourcePreference)
+                for item in compute_prefs
+            )
+            if all_thrift_instances:
+                validated_data_copy = copy.deepcopy(validated_data)
+                validated_data_copy['computePreferences'] = []
+
+                params = self.process_nested_fields(validated_data_copy)
+                params['computePreferences'] = [copy.deepcopy(pref) for pref 
in compute_prefs]
+
+                thrift_spec = GroupResourceProfile.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]
+
+                return GroupResourceProfile(**params)
+
+        params = self.process_nested_fields(validated_data)
+
+        if 'computePreferences' in params and params['computePreferences']:
+            compute_prefs = params['computePreferences']
+            processed_compute_prefs = []
+            for pref in compute_prefs:
+                if isinstance(pref, GroupComputeResourcePreference):
+                    processed_compute_prefs.append(pref)
+                elif isinstance(pref, dict):
+                    serializer = GroupComputeResourcePreferenceSerializer()
+                    try:
+                        log.debug(
+                            "GCPreference create: Converting dict to Thrift 
instance, computeResourceId=%s",
+                            pref.get('computeResourceId', 'unknown'),
+                        )
+                        thrift_pref = serializer.create(pref)
+                        if isinstance(thrift_pref, 
GroupComputeResourcePreference):
+                            log.debug(
+                                "GCPreference create: Successfully created 
Thrift instance, computeResourceId=%s resourceType=%s",
+                                thrift_pref.computeResourceId if 
hasattr(thrift_pref, 'computeResourceId') else 'unknown',
+                                getattr(thrift_pref, 'resourceType', None),
+                            )
+                            processed_compute_prefs.append(thrift_pref)
+                        else:
+                            log.warning(
+                                "GCPreference create: serializer.create() 
returned non-Thrift instance: %s, type=%s",
+                                pref.get('computeResourceId', 'unknown'),
+                                type(thrift_pref).__name__,
+                            )
+                            raise ValueError(f"serializer.create() returned 
{type(thrift_pref).__name__}, expected GroupComputeResourcePreference")
+                    except Exception as e:
+                        log.warning(
+                            "GCPreference create: Failed to convert dict to 
Thrift instance using serializer.create(): %s, trying direct construction",
+                            str(e),
+                            exc_info=True,
+                        )
+                        pref_copy = copy.deepcopy(pref)
+                        if 'resourceType' not in pref_copy or 
pref_copy.get('resourceType') is None:
+                            if 'allocationProjectNumber' in pref_copy:
+                                pref_copy['resourceType'] = ResourceType.SLURM
+                            elif 'region' in pref_copy or 'preferredAmiId' in 
pref_copy:
+                                pref_copy['resourceType'] = ResourceType.AWS
+                            else:
+                                pref_copy['resourceType'] = ResourceType.SLURM 
 # Default
+                        try:
+                            
processed_compute_prefs.append(GroupComputeResourcePreference(**pref_copy))
+                        except Exception as e2:
+                            log.error(
+                                "GCPreference create: Failed to create Thrift 
instance directly: %s",
+                                str(e2),
+                                exc_info=True,
+                            )
+                            processed_compute_prefs.append(pref)
+                else:
+                    processed_compute_prefs.append(pref)
+            params['computePreferences'] = processed_compute_prefs
+
+        thrift_spec = GroupResourceProfile.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]
+
+        return GroupResourceProfile(**params)
+
+    def process_nested_fields(self, validated_data):
+        compute_prefs = validated_data.get('computePreferences')
+        if compute_prefs is None or compute_prefs == []:
+            validated_data_copy = copy.deepcopy(validated_data)
+            validated_data_copy.pop('computePreferences', None)
+            params = self._process_nested_fields_base(validated_data_copy)
+            params['computePreferences'] = compute_prefs if compute_prefs is 
not None else []
+            return params
+
+        if compute_prefs and all(isinstance(item, 
GroupComputeResourcePreference) for item in compute_prefs):
+            validated_data_copy = copy.deepcopy(validated_data)
+            validated_data_copy.pop('computePreferences', None)
+            params = self._process_nested_fields_base(validated_data_copy)
+            params['computePreferences'] = compute_prefs
+            return params
+
+        return self._process_nested_fields_base(validated_data)
+
+    def _process_nested_fields_base(self, validated_data):
+        from rest_framework.serializers import ListField, ListSerializer, 
Serializer
+        if not isinstance(validated_data, dict):
+            return validated_data
+
+        params = copy.deepcopy(validated_data)
+        fields = self.fields
+
+        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):
+                        items = params[field_name]
+                        if items and all(not isinstance(item, dict) for item 
in items):
+                            continue
+
+                        if field_name == 'experimentInputs' and 'type' in 
serializer.child.fields:
+                            for item in params[field_name]:
+                                if isinstance(item, dict) and '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 isinstance(item, dict) and '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 isinstance(item, dict) and 'state' in item 
and isinstance(item['state'], int):
+                                    item['state'] = 
ExperimentState(item['state'])
+
+                        processed_items = []
+                        for item in params[field_name]:
+                            if isinstance(item, dict):
+                                if hasattr(serializer.child, 'create'):
+                                    try:
+                                        
processed_items.append(serializer.child.create(item))
+                                    except NotImplementedError:
+                                        processed_items.append(item)
+                                else:
+                                    processed_items.append(item)
+                            else:
+                                processed_items.append(item)
+                        params[field_name] = processed_items
+                    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:
+                    if not isinstance(params[field_name], dict):
+                        continue
+                    if hasattr(serializer, 'create'):
+                        try:
+                            params[field_name] = 
serializer.create(params[field_name])
+                        except NotImplementedError:
+                            pass
+
+        return params
+
     def update(self, instance, validated_data):
-        result = super().update(instance, validated_data)
+        # Merge existing computePreferences with incoming data to preserve 
fields that aren't being updated
+        if 'computePreferences' in validated_data and 
instance.computePreferences:
+            existing_prefs_by_id = {
+                pref.computeResourceId: pref
+                for pref in instance.computePreferences
+                if hasattr(pref, 'computeResourceId')
+            }
+
+            for incoming_pref in validated_data['computePreferences']:
+                if isinstance(incoming_pref, dict):
+                    compute_resource_id = 
incoming_pref.get('computeResourceId')
+                    if compute_resource_id and compute_resource_id in 
existing_prefs_by_id:
+                        existing_pref = 
existing_prefs_by_id[compute_resource_id]
+                        if 'specificPreferences' in incoming_pref and 
isinstance(incoming_pref['specificPreferences'], dict):
+                            if 'slurm' in incoming_pref['specificPreferences']:
+                                incoming_slurm = 
incoming_pref['specificPreferences']['slurm']
+                                if isinstance(incoming_slurm, dict):
+                                    existing_slurm = None
+                                    if (hasattr(existing_pref, 
'specificPreferences') and
+                                        existing_pref.specificPreferences and
+                                        
hasattr(existing_pref.specificPreferences, 'slurm') and
+                                        
existing_pref.specificPreferences.slurm):
+                                        existing_slurm = 
existing_pref.specificPreferences.slurm
+
+                                    simple_string_fields = [
+                                        'preferredBatchQueue',
+                                        'qualityOfService',
+                                        'usageReportingGatewayId',
+                                        'sshAccountProvisioner',
+                                        'sshAccountProvisionerAdditionalInfo',
+                                    ]
+                                    for field in simple_string_fields:
+                                        if incoming_slurm.get(field) is None 
and existing_slurm:
+                                            existing_value = 
getattr(existing_slurm, field, None)
+                                            if existing_value is not None:
+                                                incoming_slurm[field] = 
existing_value
+                                                log.debug(
+                                                    "GCPreference update: 
Preserved existing %s=%s for computeResourceId=%s",
+                                                    field,
+                                                    existing_value,
+                                                    compute_resource_id,
+                                                )
+
+                                    list_fields = [
+                                        'groupSSHAccountProvisionerConfigs',
+                                        'reservations',
+                                    ]
+                                    for field in list_fields:
+                                        if incoming_slurm.get(field) is None 
and existing_slurm:
+                                            existing_value = 
getattr(existing_slurm, field, None)
+                                            if existing_value is not None and 
isinstance(existing_value, list) and len(existing_value) > 0:
+                                                converted_list = []
+                                                for item in existing_value:
+                                                    if hasattr(item, 
'__dict__'):
+                                                        if field == 
'reservations':
+                                                            try:
+                                                                serializer = 
ComputeResourceReservationSerializer()
+                                                                
converted_list.append(serializer.to_representation(item))
+                                                            except Exception 
as e:
+                                                                log.warning(
+                                                                    
"GCPreference update: Could not serialize reservation item: %s",
+                                                                    str(e),
+                                                                )
+                                                                item_dict = 
{k: v for k, v in item.__dict__.items() if not k.startswith('_')}
+                                                                
converted_list.append(item_dict)
+                                                        else:
+                                                            item_dict = {k: v 
for k, v in item.__dict__.items() if not k.startswith('_')}
+                                                            
converted_list.append(item_dict)
+                                                    else:
+                                                        
converted_list.append(item)
+                                                incoming_slurm[field] = 
converted_list
+                                                log.debug(
+                                                    "GCPreference update: 
Preserved existing %s (list with %d items) for computeResourceId=%s",
+                                                    field,
+                                                    len(converted_list),
+                                                    compute_resource_id,
+                                                )
+
+        if 'computeResourcePolicies' in validated_data and 
instance.computeResourcePolicies:
+            existing_policies_by_resource_id = {
+                pol.computeResourceId: pol
+                for pol in instance.computeResourcePolicies
+                if hasattr(pol, 'computeResourceId') and hasattr(pol, 
'resourcePolicyId')
+            }
+
+            for incoming_policy in validated_data['computeResourcePolicies']:
+                if isinstance(incoming_policy, dict):
+                    compute_resource_id = 
incoming_policy.get('computeResourceId')
+                    if compute_resource_id and compute_resource_id in 
existing_policies_by_resource_id:
+                        existing_policy = 
existing_policies_by_resource_id[compute_resource_id]
+                        if 'resourcePolicyId' not in incoming_policy or 
incoming_policy.get('resourcePolicyId') is None:
+                            incoming_policy['resourcePolicyId'] = 
existing_policy.resourcePolicyId
+                            log.debug(
+                                "GCPreference update: Preserved existing 
resourcePolicyId=%s for computeResourceId=%s",
+                                existing_policy.resourcePolicyId,
+                                compute_resource_id,
+                            )
+
+        if 'batchQueueResourcePolicies' in validated_data and 
instance.batchQueueResourcePolicies:
+            existing_bq_policies_by_key = {}
+            for pol in instance.batchQueueResourcePolicies:
+                if hasattr(pol, 'computeResourceId') and hasattr(pol, 
'queuename') and hasattr(pol, 'resourcePolicyId'):
+                    key = (pol.computeResourceId, pol.queuename)
+                    existing_bq_policies_by_key[key] = pol
+
+            for incoming_bq_policy in 
validated_data['batchQueueResourcePolicies']:
+                if isinstance(incoming_bq_policy, dict):
+                    compute_resource_id = 
incoming_bq_policy.get('computeResourceId')
+                    queuename = incoming_bq_policy.get('queuename')
+                    if compute_resource_id and queuename:
+                        key = (compute_resource_id, queuename)
+                        if key in existing_bq_policies_by_key:
+                            existing_bq_policy = 
existing_bq_policies_by_key[key]
+                            if 'resourcePolicyId' not in incoming_bq_policy or 
incoming_bq_policy.get('resourcePolicyId') is None:
+                                incoming_bq_policy['resourcePolicyId'] = 
existing_bq_policy.resourcePolicyId
+                                log.debug(
+                                    "GCPreference update: Preserved existing 
resourcePolicyId=%s for batchQueueResourcePolicy computeResourceId=%s 
queuename=%s",
+                                    existing_bq_policy.resourcePolicyId,
+                                    compute_resource_id,
+                                    queuename,
+                                )
+
+        result = self.create(validated_data)
         result._removed_compute_resource_preferences = []
         result._removed_compute_resource_policies = []
         result._removed_batch_queue_resource_policies = []
         # Find all compute resource preferences that were removed
         for compute_resource_preference in instance.computePreferences:
-            existing_compute_resource_preference = next(
-                (pref for pref in result.computePreferences
-                 if pref.computeResourceId ==
-                 compute_resource_preference.computeResourceId),
-                None)
+            existing_compute_resource_preference = None
+            for pref in result.computePreferences:
+                if isinstance(pref, GroupComputeResourcePreference):
+                    pref_id = pref.computeResourceId
+                elif isinstance(pref, dict):
+                    pref_id = pref.get('computeResourceId')
+                else:
+                    continue
+                if pref_id == compute_resource_preference.computeResourceId:
+                    existing_compute_resource_preference = pref
+                    break
             if not existing_compute_resource_preference:
                 result._removed_compute_resource_preferences.append(
                     compute_resource_preference)
         # Find all compute resource policies that were removed
         for compute_resource_policy in instance.computeResourcePolicies:
-            existing_compute_resource_policy = next(
-                (pol for pol in result.computeResourcePolicies
-                 if pol.resourcePolicyId ==
-                 compute_resource_policy.resourcePolicyId),
-                None)
+            existing_compute_resource_policy = None
+            for pol in result.computeResourcePolicies:
+                if hasattr(pol, 'resourcePolicyId'):
+                    pol_id = pol.resourcePolicyId
+                elif isinstance(pol, dict):
+                    pol_id = pol.get('resourcePolicyId')
+                else:
+                    continue
+                if pol_id == compute_resource_policy.resourcePolicyId:
+                    existing_compute_resource_policy = pol
+                    break
             if not existing_compute_resource_policy:
                 result._removed_compute_resource_policies.append(
                     compute_resource_policy)
         # Find all batch queue resource policies that were removed
         for batch_queue_resource_policy in instance.batchQueueResourcePolicies:
-            existing_batch_queue_resource_policy_for_update = next(
-                (bq for bq in result.batchQueueResourcePolicies
-                 if bq.resourcePolicyId ==
-                 batch_queue_resource_policy.resourcePolicyId),
-                None)
+            existing_batch_queue_resource_policy_for_update = None
+            for bq in result.batchQueueResourcePolicies:
+                if hasattr(bq, 'resourcePolicyId'):
+                    bq_id = bq.resourcePolicyId
+                elif isinstance(bq, dict):
+                    bq_id = bq.get('resourcePolicyId')
+                else:
+                    continue
+                if bq_id == batch_queue_resource_policy.resourcePolicyId:
+                    existing_batch_queue_resource_policy_for_update = bq
+                    break
             if not existing_batch_queue_resource_policy_for_update:
                 result._removed_batch_queue_resource_policies.append(
                     batch_queue_resource_policy)
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/index.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 9cb8a6550..3d62ba6fc 100644
--- 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -23,9 +23,13 @@ import ExtendedUserProfileFieldChoice from 
"./models/ExtendedUserProfileFieldCho
 import ExtendedUserProfileFieldLink from 
"./models/ExtendedUserProfileFieldLink";
 import FullExperiment from "./models/FullExperiment";
 import Group from "./models/Group";
+import GroupAccountSSHProvisionerConfig from 
"./models/GroupAccountSSHProvisionerConfig";
 import GroupComputeResourcePreference from 
"./models/GroupComputeResourcePreference";
 import GroupPermission from "./models/GroupPermission";
 import GroupResourceProfile from "./models/GroupResourceProfile";
+import AwsComputeResourcePreference from 
"./models/AwsComputeResourcePreference";
+import ResourceType from "./models/ResourceType";
+import SlurmComputeResourcePreference from 
"./models/SlurmComputeResourcePreference";
 import IAMUserProfile from "./models/IAMUserProfile";
 import InputDataObjectType from "./models/InputDataObjectType";
 import JobState from "./models/JobState";
@@ -71,6 +75,7 @@ const models = {
   ApplicationDeploymentDescription,
   ApplicationInterfaceDefinition,
   ApplicationModule,
+  AwsComputeResourcePreference,
   BaseModel,
   BatchQueue,
   BatchQueueResourcePolicy,
@@ -88,6 +93,7 @@ const models = {
   ExtendedUserProfileFieldLink,
   FullExperiment,
   Group,
+  GroupAccountSSHProvisionerConfig,
   GroupComputeResourcePreference,
   GroupPermission,
   GroupResourceProfile,
@@ -100,8 +106,10 @@ const models = {
   ParallelismType,
   Project,
   ResourcePermissionType,
+  ResourceType,
   SetEnvPaths,
   SharedEntity,
+  SlurmComputeResourcePreference,
   StoragePreference,
   SummaryType,
   UserConfigurationData,
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/AwsComputeResourcePreference.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/AwsComputeResourcePreference.js
new file mode 100644
index 000000000..8ba728871
--- /dev/null
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/AwsComputeResourcePreference.js
@@ -0,0 +1,21 @@
+import BaseModel from "./BaseModel";
+
+const FIELDS = [
+  "region",
+  "preferredAmiId",
+  "preferredInstanceType",
+];
+
+export default class AwsComputeResourcePreference extends BaseModel {
+  constructor(data = {}) {
+    super(FIELDS, data);
+  }
+
+  toJSON() {
+    return { ...this };
+  }
+
+  validate() {
+    return {};
+  }
+}
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupAccountSSHProvisionerConfig.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupAccountSSHProvisionerConfig.js
new file mode 100644
index 000000000..d43346f7e
--- /dev/null
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupAccountSSHProvisionerConfig.js
@@ -0,0 +1,18 @@
+import BaseModel from "./BaseModel";
+
+const FIELDS = [
+  "resourceId",
+  "groupResourceProfileId",
+  "configName",
+  "configValue",
+];
+
+export default class GroupAccountSSHProvisionerConfig extends BaseModel {
+  constructor(data = {}) {
+    super(FIELDS, data);
+  }
+
+  toJSON() {
+    return { ...this };
+  }
+}
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
index eb1da485f..f13a317fe 100644
--- 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupComputeResourcePreference.js
@@ -1,5 +1,7 @@
 import BaseModel from "./BaseModel";
-import ComputeResourceReservation from "./ComputeResourceReservation";
+import ResourceType from "./ResourceType";
+import SlurmComputeResourcePreference from "./SlurmComputeResourcePreference";
+import AwsComputeResourcePreference from "./AwsComputeResourcePreference";
 
 const FIELDS = [
   "computeResourceId",
@@ -12,27 +14,225 @@ const FIELDS = [
   "loginUserName",
   "preferredJobSubmissionProtocol",
   "preferredDataMovementProtocol",
-  "preferredBatchQueue",
   "scratchLocation",
-  "allocationProjectNumber",
   "resourceSpecificCredentialStoreToken",
-  "usageReportingGatewayId",
-  "qualityOfService",
-  "sshAccountProvisioner",
-  "groupSSHAccountProvisionerConfigs",
-  "sshAccountProvisionerAdditionalInfo",
   {
-    name: "reservations",
-    type: ComputeResourceReservation,
-    list: true,
-    default: BaseModel.defaultNewInstance(Array),
+    name: "resourceType",
+    type: ResourceType,
+    required: true,
+  },
+  {
+    name: "specificPreferences",
+    type: null,
   },
-  
 ];
 
+const PREFERENCE_MODEL_MAP = {
+  SLURM: SlurmComputeResourcePreference,
+  AWS: AwsComputeResourcePreference,
+};
+
 export default class GroupComputeResourcePreference extends BaseModel {
   constructor(data = {}) {
+    const topLevelAllocationProjectNumber = data.allocationProjectNumber;
+    const rawSpecificPreferences = data.specificPreferences;
+
     super(FIELDS, data);
+
+    const specificPrefsToUse = rawSpecificPreferences !== undefined && 
rawSpecificPreferences !== null
+      ? rawSpecificPreferences
+      : (data.specificPreferences !== undefined && data.specificPreferences 
!== null ? data.specificPreferences : null);
+
+    if (specificPrefsToUse !== null) {
+      this.specificPreferences = specificPrefsToUse;
+    }
+
+    if (this.resourceType && typeof this.resourceType === 'number') {
+      this.resourceType = ResourceType.values.find(rt => rt.value === 
this.resourceType) || this.resourceType;
+    }
+
+    if (topLevelAllocationProjectNumber) {
+      if (this.resourceType && this.resourceType.name === 'SLURM') {
+        if (!this.specificPreferences) {
+          this.specificPreferences = {};
+        }
+        if (typeof this.specificPreferences === 'object' && 
!(this.specificPreferences instanceof BaseModel)) {
+          if (!this.specificPreferences.allocationProjectNumber) {
+            this.specificPreferences.allocationProjectNumber = 
topLevelAllocationProjectNumber;
+          }
+        }
+      } else if (!this.resourceType && topLevelAllocationProjectNumber) {
+        this.resourceType = ResourceType.SLURM;
+        if (!this.specificPreferences) {
+          this.specificPreferences = {};
+        }
+        if (typeof this.specificPreferences === 'object' && 
!(this.specificPreferences instanceof BaseModel)) {
+          if (!this.specificPreferences.allocationProjectNumber) {
+            this.specificPreferences.allocationProjectNumber = 
topLevelAllocationProjectNumber;
+          }
+        }
+      }
+    }
+
+    this._coerceSpecificPreferences();
+  }
+
+  toJSON() {
+    const json = {...this};
+    if (this.resourceType && this.resourceType.value !== undefined) {
+      json.resourceType = this.resourceType.value;
+    } else if (this.resourceType && this.resourceType.name) {
+      json.resourceType = this.resourceType.name;
+    }
+
+    let specificPrefsPayload = this.specificPreferences;
+    if (
+      this.specificPreferences &&
+      typeof this.specificPreferences.toJSON === "function"
+    ) {
+      specificPrefsPayload = this.specificPreferences.toJSON();
+    }
+
+    if (specificPrefsPayload && this.isResourceType("SLURM")) {
+      json.specificPreferences = {slurm: specificPrefsPayload};
+    } else if (specificPrefsPayload && this.isResourceType("AWS")) {
+      json.specificPreferences = {aws: specificPrefsPayload};
+    } else if (specificPrefsPayload) {
+      json.specificPreferences = specificPrefsPayload;
+    } else {
+      json.specificPreferences = null;
+    }
+
+    return json;
+  }
+
+  _coerceSpecificPreferences() {
+    // Ensure resourceType is properly set
+    if (this.resourceType && typeof this.resourceType === 'number') {
+      this.resourceType = ResourceType.byValue(this.resourceType) || 
ResourceType.values.find(rt => rt.value === this.resourceType) || 
this.resourceType;
+    }
+
+    if (!this.resourceType) {
+      this.specificPreferences = null;
+      return;
+    }
+
+    if (!this.resourceType.name) {
+      return;
+    }
+
+    if (
+      this.specificPreferences &&
+      this.specificPreferences instanceof BaseModel
+    ) {
+      return;
+    }
+    let rawData =
+      this.specificPreferences && typeof this.specificPreferences === "object"
+        ? this.specificPreferences
+        : null;
+
+    if (rawData && !(rawData instanceof BaseModel)) {
+      if (this.resourceType.name === 'SLURM' && 'slurm' in rawData) {
+        rawData = rawData.slurm;
+      } else if (this.resourceType.name === 'AWS') {
+        if ('aws' in rawData) {
+          rawData = rawData.aws;
+        }
+      }
+    }
+
+    const PreferenceModel = PREFERENCE_MODEL_MAP[this.resourceType.name];
+    if (PreferenceModel) {
+      const newPref = rawData
+        ? new PreferenceModel(rawData)
+        : new PreferenceModel();
+      this.specificPreferences = newPref;
+    } else {
+      this.specificPreferences = rawData;
+    }
+  }
+
+  resetSpecificPreferences(data = null) {
+    if (!this.resourceType) {
+      this.specificPreferences = null;
+      return;
+    }
+    if (data && typeof data === "object") {
+      this.specificPreferences = data;
+    } else {
+      this.specificPreferences = null;
+    }
+    this._coerceSpecificPreferences();
+  }
+
+  isResourceType(resourceTypeName) {
+    return (
+      !!this.resourceType && this.resourceType.name === resourceTypeName
+    );
+  }
+
+  _ensureSpecificPreferences() {
+    if (!this.specificPreferences) {
+      this._coerceSpecificPreferences();
+    }
+  }
+
+  _getSlurmField(fieldName, defaultValue) {
+    if (this.isResourceType("SLURM") && this.specificPreferences) {
+      return this.specificPreferences[fieldName];
+    }
+    return defaultValue;
+  }
+
+  _setSlurmField(fieldName, value) {
+    if (!this.isResourceType("SLURM")) {
+      return;
+    }
+    this._ensureSpecificPreferences();
+    if (this.specificPreferences) {
+      this.specificPreferences[fieldName] = value;
+    }
+  }
+
+  get allocationProjectNumber() {
+    return this._getSlurmField("allocationProjectNumber");
+  }
+
+  set allocationProjectNumber(value) {
+    this._setSlurmField("allocationProjectNumber", value);
+  }
+
+  get preferredBatchQueue() {
+    return this._getSlurmField("preferredBatchQueue");
+  }
+
+  set preferredBatchQueue(value) {
+    this._setSlurmField("preferredBatchQueue", value);
+  }
+
+  get qualityOfService() {
+    return this._getSlurmField("qualityOfService");
+  }
+
+  set qualityOfService(value) {
+    this._setSlurmField("qualityOfService", value);
+  }
+
+  get usageReportingGatewayId() {
+    return this._getSlurmField("usageReportingGatewayId");
+  }
+
+  set usageReportingGatewayId(value) {
+    this._setSlurmField("usageReportingGatewayId", value);
+  }
+
+  get reservations() {
+    return this._getSlurmField("reservations", []);
+  }
+
+  set reservations(value) {
+    this._setSlurmField("reservations", value);
   }
 
   validate() {
@@ -44,6 +244,15 @@ export default class GroupComputeResourcePreference extends 
BaseModel {
       validationResults["scratchLocation"] =
         "Please provide a scratch location.";
     }
+    if (!this.resourceType) {
+      validationResults["resourceType"] = "Please select a resource type.";
+    }
+    if (this.resourceType && this.specificPreferences) {
+      const specificValidation = this.specificPreferences.validate();
+      if (specificValidation && Object.keys(specificValidation).length > 0) {
+        Object.assign(validationResults, specificValidation);
+      }
+    }
     return validationResults;
   }
 }
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ResourceType.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ResourceType.js
new file mode 100644
index 000000000..f674c94ed
--- /dev/null
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ResourceType.js
@@ -0,0 +1,5 @@
+import BaseEnum from "./BaseEnum";
+
+export default class ResourceType extends BaseEnum {}
+ResourceType.init(["SLURM", "AWS"]);
+
diff --git 
a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/SlurmComputeResourcePreference.js
 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/SlurmComputeResourcePreference.js
new file mode 100644
index 000000000..11e350c17
--- /dev/null
+++ 
b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/SlurmComputeResourcePreference.js
@@ -0,0 +1,49 @@
+import BaseModel from "./BaseModel";
+import ComputeResourceReservation from "./ComputeResourceReservation";
+import GroupAccountSSHProvisionerConfig from 
"./GroupAccountSSHProvisionerConfig";
+
+const FIELDS = [
+  "allocationProjectNumber",
+  "preferredBatchQueue",
+  "qualityOfService",
+  "usageReportingGatewayId",
+  "sshAccountProvisioner",
+  {
+    name: "groupSSHAccountProvisionerConfigs",
+    type: GroupAccountSSHProvisionerConfig,
+    list: true,
+    default: BaseModel.defaultNewInstance(Array),
+  },
+  "sshAccountProvisionerAdditionalInfo",
+  {
+    name: "reservations",
+    type: ComputeResourceReservation,
+    list: true,
+    default: BaseModel.defaultNewInstance(Array),
+  },
+];
+
+export default class SlurmComputeResourcePreference extends BaseModel {
+  constructor(data = {}) {
+    super(FIELDS, data);
+  }
+
+  toJSON() {
+    const json = { ...this };
+    if (json.groupSSHAccountProvisionerConfigs) {
+      json.groupSSHAccountProvisionerConfigs = 
json.groupSSHAccountProvisionerConfigs.map((cfg) =>
+        typeof cfg.toJSON === "function" ? cfg.toJSON() : cfg
+      );
+    }
+    if (json.reservations) {
+      json.reservations = json.reservations.map((res) =>
+        typeof res.toJSON === "function" ? res.toJSON() : res
+      );
+    }
+    return json;
+  }
+
+  validate() {
+    return {};
+  }
+}
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py 
b/airavata-django-portal/django_airavata/apps/api/views.py
index 9130544c7..1cca33e26 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -24,6 +24,10 @@ from airavata.model.experiment.ttypes import (
     ExperimentModel,
     ExperimentSearchFields
 )
+from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+    GroupComputeResourcePreference,
+    ResourceType
+)
 from airavata.model.group.ttypes import ResourcePermissionType
 from airavata.model.user.ttypes import Status
 from airavata_django_portal_sdk import (
@@ -950,6 +954,8 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
         group_resource_profile.creationTime = 
new_group_resource_profile.creationTime
 
     def perform_update(self, serializer):
+        original_instance = serializer.instance
+
         grp = serializer.save()
         for removed_compute_resource_preference \
                 in grp._removed_compute_resource_preferences:
@@ -967,6 +973,156 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
             self.request.airavata_client.removeGroupBatchQueueResourcePolicy(
                 self.authz_token,
                 removed_batch_queue_resource_policy.resourcePolicyId)
+        if hasattr(grp, 'computePreferences') and grp.computePreferences:
+            from collections import OrderedDict
+            from django_airavata.apps.api.serializers import 
GroupComputeResourcePreferenceSerializer
+
+            for pref in grp.computePreferences:
+                if isinstance(pref, GroupComputeResourcePreference):
+                    if not hasattr(pref, 'resourceType') or pref.resourceType 
is None:
+                        resource_type = None
+                        if hasattr(pref, 'specificPreferences') and 
pref.specificPreferences:
+                            if isinstance(pref.specificPreferences, (dict, 
OrderedDict)):
+                                specific_prefs_dict = pref.specificPreferences
+                                if 'slurm' in specific_prefs_dict or 
'allocationProjectNumber' in specific_prefs_dict:
+                                    resource_type = ResourceType.SLURM
+                                elif 'aws' in specific_prefs_dict or 'region' 
in specific_prefs_dict:
+                                    resource_type = ResourceType.AWS
+                                else:
+                                    resource_type = ResourceType.SLURM
+                            elif hasattr(pref.specificPreferences, 'slurm') 
and pref.specificPreferences.slurm:
+                                resource_type = ResourceType.SLURM
+                            elif hasattr(pref.specificPreferences, 'aws') and 
pref.specificPreferences.aws:
+                                resource_type = ResourceType.AWS
+                            else:
+                                resource_type = ResourceType.SLURM
+                        else:
+                            resource_type = ResourceType.SLURM
+                        pref.resourceType = resource_type
+
+                    resource_type = pref.resourceType if hasattr(pref, 
'resourceType') and pref.resourceType else None
+                    if resource_type:
+                        if hasattr(pref, 'specificPreferences') and 
isinstance(pref.specificPreferences, (dict, OrderedDict)):
+                            
GroupComputeResourcePreferenceSerializer._convert_specific_preferences_dict_to_thrift(
+                                pref, resource_type
+                            )
+                        elif hasattr(pref, 'specificPreferences') and 
pref.specificPreferences:
+                            
GroupComputeResourcePreferenceSerializer._convert_specific_preferences_dict_to_thrift(
+                                pref, resource_type
+                            )
+
+        from collections import OrderedDict
+        from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+            ComputeResourcePolicy,
+            BatchQueueResourcePolicy
+        )
+
+        if hasattr(grp, 'computeResourcePolicies') and 
grp.computeResourcePolicies:
+            existing_policies_by_resource_id = {}
+            if original_instance and hasattr(original_instance, 
'computeResourcePolicies'):
+                for existing_policy in 
original_instance.computeResourcePolicies:
+                    if hasattr(existing_policy, 'computeResourceId') and 
hasattr(existing_policy, 'resourcePolicyId'):
+                        
existing_policies_by_resource_id[existing_policy.computeResourceId] = 
existing_policy
+
+            indices_to_remove = []
+            for idx, policy in enumerate(grp.computeResourcePolicies):
+                if isinstance(policy, (dict, OrderedDict)):
+                    try:
+                        if isinstance(policy, OrderedDict):
+                            policy = dict(policy)
+
+                        compute_resource_id = policy.get('computeResourceId')
+                        current_resource_policy_id = 
policy.get('resourcePolicyId')
+
+                        if not current_resource_policy_id:
+                            if compute_resource_id and compute_resource_id in 
existing_policies_by_resource_id:
+                                existing_policy = 
existing_policies_by_resource_id[compute_resource_id]
+                                policy['resourcePolicyId'] = 
existing_policy.resourcePolicyId
+                            elif original_instance and 
hasattr(original_instance, 'computeResourcePolicies') and idx < 
len(original_instance.computeResourcePolicies):
+                                existing_policy_by_idx = 
original_instance.computeResourcePolicies[idx]
+                                if hasattr(existing_policy_by_idx, 
'resourcePolicyId') and existing_policy_by_idx.resourcePolicyId:
+                                    policy['resourcePolicyId'] = 
existing_policy_by_idx.resourcePolicyId
+
+                        if not policy.get('resourcePolicyId'):
+                            indices_to_remove.append(idx)
+                            continue
+
+                        grp.computeResourcePolicies[idx] = 
ComputeResourcePolicy(**policy)
+                    except Exception as e:
+                        log.error(
+                            "GCPreference perform_update: Failed to convert 
computeResourcePolicies[%d] OrderedDict to Thrift: %s, policy keys: %s",
+                            idx,
+                            str(e),
+                            list(policy.keys()) if isinstance(policy, dict) 
else list(policy.keys()),
+                            exc_info=True,
+                        )
+                        raise
+
+            for idx in reversed(indices_to_remove):
+                grp.computeResourcePolicies.pop(idx)
+
+        if hasattr(grp, 'batchQueueResourcePolicies') and 
grp.batchQueueResourcePolicies:
+            existing_bq_policies_by_key = {}
+            if original_instance and hasattr(original_instance, 
'batchQueueResourcePolicies'):
+                for existing_bq_policy in 
original_instance.batchQueueResourcePolicies:
+                    if (hasattr(existing_bq_policy, 'computeResourceId') and
+                        hasattr(existing_bq_policy, 'queuename') and
+                        hasattr(existing_bq_policy, 'resourcePolicyId')):
+                        key = (existing_bq_policy.computeResourceId, 
existing_bq_policy.queuename)
+                        existing_bq_policies_by_key[key] = existing_bq_policy
+
+            for idx, policy in enumerate(grp.batchQueueResourcePolicies):
+                if isinstance(policy, (dict, OrderedDict)):
+                    try:
+                        compute_resource_id = policy.get('computeResourceId')
+                        queuename = policy.get('queuename')
+                        if compute_resource_id and queuename:
+                            key = (compute_resource_id, queuename)
+                            if key in existing_bq_policies_by_key:
+                                existing_bq_policy = 
existing_bq_policies_by_key[key]
+                                if 'resourcePolicyId' not in policy or 
policy.get('resourcePolicyId') is None:
+                                    policy['resourcePolicyId'] = 
existing_bq_policy.resourcePolicyId
+                        grp.batchQueueResourcePolicies[idx] = 
BatchQueueResourcePolicy(**policy)
+                    except Exception as e:
+                        log.error(
+                            "GCPreference perform_update: Failed to convert 
batchQueueResourcePolicies[%d] OrderedDict to Thrift: %s",
+                            idx,
+                            str(e),
+                            exc_info=True,
+                        )
+
+        if hasattr(grp, 'computePreferences') and grp.computePreferences:
+            for idx, pref in enumerate(grp.computePreferences):
+                if isinstance(pref, (dict, OrderedDict)):
+                    from django_airavata.apps.api.serializers import 
GroupComputeResourcePreferenceSerializer
+                    serializer = GroupComputeResourcePreferenceSerializer()
+                    try:
+                        pref = serializer.create(pref)
+                        grp.computePreferences[idx] = pref
+                    except Exception as e:
+                        log.error(
+                            "GCPreference perform_update: Failed to convert 
OrderedDict to Thrift: %s",
+                            str(e),
+                            exc_info=True,
+                        )
+
+                if isinstance(pref, GroupComputeResourcePreference):
+                    if hasattr(pref, 'specificPreferences') and 
pref.specificPreferences:
+                        if hasattr(pref.specificPreferences, 'slurm') and 
pref.specificPreferences.slurm:
+                            
GroupComputeResourcePreferenceSerializer._convert_nested_list_fields_to_thrift(
+                                pref.specificPreferences.slurm
+                            )
+                            if hasattr(pref.specificPreferences.slurm, 
'reservations') and pref.specificPreferences.slurm.reservations:
+                                for res_idx, res in 
enumerate(pref.specificPreferences.slurm.reservations):
+                                    if isinstance(res, (dict, OrderedDict)):
+                                        from 
airavata.model.appcatalog.groupresourceprofile.ttypes import 
ComputeResourceReservation
+                                        
pref.specificPreferences.slurm.reservations[res_idx] = 
ComputeResourceReservation(**res)
+                            if hasattr(pref.specificPreferences.slurm, 
'groupSSHAccountProvisionerConfigs') and 
pref.specificPreferences.slurm.groupSSHAccountProvisionerConfigs:
+                                for cfg_idx, cfg in 
enumerate(pref.specificPreferences.slurm.groupSSHAccountProvisionerConfigs):
+                                    if isinstance(cfg, (dict, OrderedDict)):
+                                        from 
airavata.model.appcatalog.groupresourceprofile.ttypes import 
GroupAccountSSHProvisionerConfig
+                                        
pref.specificPreferences.slurm.groupSSHAccountProvisionerConfigs[cfg_idx] = 
GroupAccountSSHProvisionerConfig(**cfg)
+
         self.request.airavata_client.updateGroupResourceProfile(
             self.authz_token, grp)
 


Reply via email to