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/06/11 01:43:26 UTC

(airavata-portals) branch main updated: Thin proto-direct Django portal: Keycloak-only auth, no DRF/Thrift/DB account layer (#211)

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

yasithdev 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 fa5e322a2 Thin proto-direct Django portal: Keycloak-only auth, no DRF/Thrift/DB account layer (#211)
fa5e322a2 is described below

commit fa5e322a24d380fd0106fba241586231fd6edf98
Author: Yasith Jayawardana <ya...@gatech.edu>
AuthorDate: Wed Jun 10 21:43:20 2026 -0400

    Thin proto-direct Django portal: Keycloak-only auth, no DRF/Thrift/DB account layer (#211)
    
    Reduces the Airavata Django portal to a thin client over the proto-direct airavata-python-sdk (its sole Airavata dependency). Removes Django REST Framework entirely in favor of a small in-repo request/response/router/auth layer that renders protobuf messages to snake_case JSON, drops the retired Thrift SDKs, and feeds the frontend lean snake_case proto shapes. Makes auth Keycloak-only — the portal keeps only login-initiation and a post-logout landing while registration, password reset [...]
---
 .../compose/dbinit/01-init-db.sql                  |    8 -
 airavata-django-portal/compose/docker-compose.yaml |   52 -
 airavata-django-portal/django_airavata/__init__.py |   10 -
 .../BatchQueueResourcePolicy.vue                   |   44 +-
 .../ComputePreference.vue                          |   92 +-
 .../ComputeResourcePolicyEditor.vue                |   42 +-
 .../ComputeResourcePolicySummary.vue               |   16 +-
 .../ComputeResourceReservationEditor.vue           |   32 +-
 .../ComputeResourceReservationList.vue             |   18 +-
 .../ComputeResourceReservationsSummary.vue         |    2 +-
 .../GroupComputeResourcePreference.vue             |   64 +-
 .../applications/ApplicationDeploymentEditor.vue   |   84 +-
 .../applications/ApplicationDeploymentsList.vue    |   28 +-
 .../applications/ApplicationEditorContainer.vue    |  112 +-
 .../applications/ApplicationInputFieldEditor.vue   |   28 +-
 .../applications/ApplicationInterfaceEditor.vue    |   38 +-
 .../applications/ApplicationModuleEditor.vue       |   12 +-
 .../applications/ApplicationOutputFieldEditor.vue  |   27 +-
 .../credentials/SSHCredentialSelector.vue          |    4 +-
 .../dashboards/ApplicationsDashboard.vue           |    6 +-
 .../ComputeResourcePreferenceDashboard.vue         |   20 +-
 .../dashboards/CredentialStoreDashboard.vue        |   14 +-
 .../GatewayResourceProfileEditor.vue               |    6 +-
 .../GatewayResourceProfileEditorContainer.vue      |   22 +-
 .../gatewayprofile/StoragePreferenceEditor.vue     |    6 +-
 .../gatewayprofile/StoragePreferenceList.vue       |   28 +-
 .../src/components/notices/NoticeEditor.vue        |   22 +-
 .../notices/NoticesManagementContainer.vue         |   26 +-
 .../statistics/ExperimentDetailsView.vue           |  177 +-
 .../statistics/ExperimentStatisticsContainer.vue   |   74 +-
 .../components/users/ExtendedUserProfilePanel.vue  |    2 +-
 .../components/users/GroupMembershipDisplay.vue    |   12 +-
 .../IdentityServiceUserManagementContainer.vue     |   24 +-
 .../UnverifiedEmailUserManagementContainer.vue     |   26 +-
 .../src/components/users/UserDetailsContainer.vue  |   32 +-
 .../components/users/UserGroupMembershipEditor.vue |   14 +-
 .../src/components/users/UserProfilePanel.vue      |    8 +-
 .../ExperimentStatisticsContainer.spec.js          |   78 +-
 .../django_airavata/apps/api/admin.py              |   26 -
 .../django_airavata/apps/api/authentication.py     |   33 -
 .../django_airavata/apps/api/downloads.py          |   33 +-
 .../django_airavata/apps/api/exceptions.py         |   79 +-
 .../django_airavata/apps/api/grpc_adapters.py      |   53 -
 .../django_airavata/apps/api/grpc_requests.py      |   63 -
 .../django_airavata/apps/api/helpers.py            |  177 +-
 .../django_airavata/apps/api/output_views.py       |   20 +-
 .../django_airavata/apps/api/proto_render.py       |   98 +
 .../django_airavata/apps/api/serializers.py        | 3299 +-------------------
 .../js/models/ApplicationDeploymentDescription.js  |   35 +-
 .../js/models/ApplicationInterfaceDefinition.js    |   46 +-
 .../js/models/ApplicationModule.js                 |   11 +-
 .../js/models/AwsComputeResourcePreference.js      |    4 +-
 .../django_airavata_api/js/models/BaseEnum.js      |   32 +-
 .../django_airavata_api/js/models/BaseModel.js     |    9 +-
 .../django_airavata_api/js/models/BatchQueue.js    |   26 +-
 .../js/models/BatchQueueResourcePolicy.js          |   42 +-
 .../django_airavata_api/js/models/CommandObject.js |    2 +-
 .../models/ComputationalResourceSchedulingModel.js |  101 +-
 .../js/models/ComputeResourceDescription.js        |   34 +-
 .../js/models/ComputeResourcePolicy.js             |   16 +-
 .../js/models/ComputeResourceReservation.js        |   32 +-
 .../js/models/CredentialSummary.js                 |   11 +-
 .../django_airavata_api/js/models/DataProduct.js   |   47 +-
 .../js/models/DataReplicaLocation.js               |   27 +-
 .../django_airavata_api/js/models/DataType.js      |    1 +
 .../django_airavata_api/js/models/ErrorModel.js    |   12 +-
 .../django_airavata_api/js/models/Experiment.js    |   73 +-
 .../js/models/ExperimentSearchFields.js            |    1 +
 .../js/models/ExperimentState.js                   |   19 +-
 .../js/models/ExperimentStatistics.js              |   24 +-
 .../js/models/ExperimentStatus.js                  |    4 +-
 .../js/models/ExperimentSummary.js                 |   35 +-
 .../js/models/FullExperiment.js                    |   20 +-
 .../js/models/GatewayResourceProfile.js            |   15 +-
 .../static/django_airavata_api/js/models/Group.js  |   20 +-
 .../js/models/GroupAccountSSHProvisionerConfig.js  |    8 +-
 .../js/models/GroupComputeResourcePreference.js    |  193 +-
 .../js/models/GroupPermission.js                   |    3 +-
 .../js/models/GroupResourceProfile.js              |   80 +-
 .../js/models/IAMUserProfile.js                    |   26 +-
 .../static/django_airavata_api/js/models/IOType.js |    2 +-
 .../js/models/InputDataObjectType.js               |   46 +-
 .../js/models/IntermediateOutput.js                |    6 +-
 .../static/django_airavata_api/js/models/Job.js    |   34 +-
 .../django_airavata_api/js/models/JobState.js      |    2 +-
 .../django_airavata_api/js/models/JobStatus.js     |    6 +-
 .../django_airavata_api/js/models/Notification.js  |   39 +-
 .../js/models/NotificationPriority.js              |    7 +-
 .../js/models/OutputDataObjectType.js              |   44 +-
 .../js/models/ParallelismType.js                   |    3 +
 .../static/django_airavata_api/js/models/Parser.js |   14 +-
 .../django_airavata_api/js/models/ParserInput.js   |    4 +-
 .../django_airavata_api/js/models/ParserOutput.js  |    4 +-
 .../django_airavata_api/js/models/ProcessModel.js  |   62 +-
 .../django_airavata_api/js/models/ProcessState.js  |   32 +-
 .../django_airavata_api/js/models/ProcessStatus.js |    7 +-
 .../js/models/ProcessWorkflow.js                   |    8 +-
 .../django_airavata_api/js/models/Project.js       |   27 +-
 .../js/models/ResourcePermissionType.js            |    8 +-
 .../django_airavata_api/js/models/ResourceType.js  |    4 +-
 .../django_airavata_api/js/models/SetEnvPaths.js   |    2 +-
 .../django_airavata_api/js/models/SharedEntity.js  |   45 +-
 .../js/models/SlurmComputeResourcePreference.js    |   18 +-
 .../js/models/StoragePreference.js                 |    8 +-
 .../js/models/StorageResourceDescription.js        |   18 +-
 .../django_airavata_api/js/models/SummaryType.js   |    4 +-
 .../static/django_airavata_api/js/models/Task.js   |   30 +-
 .../django_airavata_api/js/models/TaskState.js     |    9 +-
 .../django_airavata_api/js/models/TaskStatus.js    |    6 +-
 .../django_airavata_api/js/models/TaskTypes.js     |    1 +
 .../js/models/UnverifiedEmailUserProfile.js        |   15 +-
 .../js/models/UserConfigurationData.js             |   30 +-
 .../js/models/UserPermission.js                    |    3 +-
 .../django_airavata_api/js/models/UserProfile.js   |   43 +-
 .../django_airavata_api/js/models/UserStatus.js    |    4 +
 .../js/models/UserStorageDirectory.js              |   22 +-
 .../js/models/UserStorageFile.js                   |   23 +-
 .../js/models/UserStoragePath.js                   |    8 +-
 .../js/utils/ExperimentUtils.js                    |   50 +-
 .../tests/models/proto-direct.test.js              |  129 +
 .../tests/utils/ExperimentUtils.test.js            |  264 +-
 .../apps/api/tests/test_data_products_contract.py  |  263 ++
 .../api/tests/test_full_experiment_contract.py     |  442 +++
 .../tests/test_group_resource_profiles_contract.py |  444 +++
 .../test_per_protocol_job_submission_contract.py   |  304 ++
 .../apps/api/tests/test_proto_render.py            |  153 +
 .../django_airavata/apps/api/tests/test_views.py   |   43 +-
 .../django_airavata/apps/api/urls.py               |   91 +-
 .../django_airavata/apps/api/view_utils.py         |  167 +-
 .../django_airavata/apps/api/views.py              | 2578 +++++++++------
 .../django_airavata/apps/api/web.py                |  996 ++++++
 .../django_airavata/apps/auth/admin.py             |    5 -
 .../django_airavata/apps/auth/backends.py          |  339 --
 .../django_airavata/apps/auth/forms.py             |  158 -
 .../django_airavata/apps/auth/middleware.py        |  183 +-
 .../auth/migrations/0020_remove_account_models.py  |   47 +
 .../django_airavata/apps/auth/models.py            |  362 ---
 .../django_airavata/apps/auth/serializers.py       |  372 ---
 .../django_airavata/apps/auth/signals.py           |   34 -
 .../js/components/ExtendedUserProfileEditor.vue    |   57 -
 .../ExtendedUserProfileMultiChoiceValueEditor.vue  |  157 -
 .../ExtendedUserProfileSingleChoiceValueEditor.vue |  159 -
 .../ExtendedUserProfileTextValueEditor.vue         |   63 -
 ...ExtendedUserProfileUserAgreementValueEditor.vue |   81 -
 .../components/ExtendedUserProfileValueEditor.vue  |   47 -
 .../js/components/UserProfileEditor.vue            |  123 -
 .../js/containers/UserProfileContainer.vue         |  138 -
 .../django_airavata_auth/js/entry-user-profile.js  |   11 -
 .../static/django_airavata_auth/js/store/index.js  |   18 -
 .../js/store/modules/extendedUserProfile.js        |  191 --
 .../js/store/modules/userProfile.js                |   64 -
 .../django_airavata_auth/callback-error.html       |   22 -
 .../django_airavata_auth/create_account.html       |   34 -
 .../django_airavata_auth/forgot_password.html      |   24 -
 .../templates/django_airavata_auth/logged_out.html |    8 +
 .../django_airavata_auth/login-desktop.html        |   18 +-
 .../auth/templates/django_airavata_auth/login.html |   16 -
 .../login_username_password.html                   |   18 -
 .../partials/externals_login_form.html             |   27 -
 .../django_airavata_auth/partials/form.html        |   17 -
 .../django_airavata_auth/partials/form_field.html  |   25 -
 .../partials/non_field_errors.html                 |    6 -
 .../partials/username_password_login_form.html     |   50 -
 .../django_airavata_auth/reset_password.html       |   23 -
 .../settings_local.py.template                     |   14 -
 .../django_airavata_auth/verify_email.html         |   22 -
 .../apps/auth/tests/test_backends.py               |  178 --
 .../apps/auth/tests/test_middleware.py             |  123 -
 .../django_airavata/apps/auth/tests/test_models.py |  383 ---
 .../django_airavata/apps/auth/tests/test_views.py  |  302 --
 .../apps/auth/token_authentication.py              |   85 +-
 .../django_airavata/apps/auth/urls.py              |   30 +-
 .../django_airavata/apps/auth/utils.py             |  154 +-
 .../django_airavata/apps/auth/views.py             |  642 +---
 .../django_airavata/apps/auth/vite.config.js       |   11 +-
 .../js/containers/ParserDetailsContainer.vue       |    2 +-
 .../js/parser-components/ParserEditor.vue          |   14 +-
 .../js/parser-components/ParserListItem.vue        |    4 +-
 .../js/containers/GroupCreateContainer.vue         |    2 +-
 .../js/group_components/GroupList.vue              |    2 +-
 .../js/group_components/GroupListItem.vue          |   22 +-
 .../js/group_components/GroupMembersEditor.vue     |   22 +-
 .../ComputationalResourceSchedulingEditor.vue      |   36 +-
 .../js/components/experiment/ExperimentEditor.vue  |   82 +-
 .../js/components/experiment/ExperimentSummary.vue |  110 +-
 .../experiment/GroupResourceProfileSelector.vue    |    6 +-
 .../components/experiment/QueueSettingsEditor.vue  |  142 +-
 .../experiment/input-editors/FileInputEditor.vue   |   15 +-
 .../input-editors/InputEditorContainer.vue         |    4 +-
 .../experiment/input-editors/InputFileSelector.vue |    2 +-
 .../output-displays/DefaultOutputDisplay.vue       |   51 +-
 .../output-displays/OutputDisplayContainer.vue     |   26 +-
 .../WorkspaceNoticesManagementContainer.vue        |    8 +-
 .../js/components/project/ProjectList.vue          |    2 +-
 .../js/components/project/ProjectListItem.vue      |    6 +-
 .../storage/ExperimentStoragePathViewer.vue        |   37 +-
 .../components/storage/UserStorageCreateView.vue   |    2 +-
 .../storage/UserStorageFileSelectionContainer.vue  |    2 +-
 .../components/storage/UserStoragePathViewer.vue   |   71 +-
 .../storage-edit/UserStorageTextEditViewer.vue     |    6 +-
 .../js/containers/CreateExperimentContainer.vue    |    8 +-
 .../js/containers/DashboardContainer.vue           |   22 +-
 .../js/containers/EditExperimentContainer.vue      |    8 +-
 .../js/containers/ExperimentListContainer.vue      |   35 +-
 .../js/containers/RecentExperimentsContainer.vue   |   10 +-
 .../js/containers/UserStorageContainer.vue         |   23 +-
 .../js/store/modules/view-experiment.js            |   38 +-
 .../django_airavata_workspace/js/utils/urls.js     |   10 +-
 .../js/web-components/ExperimentEditor.vue         |   10 +-
 .../GroupResourceProfileSelector.vue               |    4 +-
 .../js/web-components/ProjectSelector.vue          |   10 +-
 .../js/web-components/QueueSettingsEditor.vue      |   12 +-
 .../input-editors/WebComponentInputEditorMixin.js  |    2 +-
 .../js/web-components/store.js                     |  182 +-
 .../input-editors/InputEditorContainer.spec.js     |    4 +-
 .../tests/unit/web-components/store.spec.js        |  164 +-
 .../django_airavata/apps/workspace/views.py        |    8 +-
 .../django_airavata/context_processors.py          |   41 +-
 .../django_airavata/log_utils.py                   |    8 +
 airavata-django-portal/django_airavata/settings.py |   79 +-
 .../common/js/components/ApplicationCard.vue       |    8 +-
 .../common/js/components/ApplicationName.vue       |    2 +-
 .../common/js/components/ComputeResourceName.vue   |    2 +-
 .../common/js/components/DataProductViewer.vue     |    4 +-
 .../common/js/components/GatewayGroupsBadge.vue    |   12 +-
 .../static/common/js/components/ShareButton.vue    |   60 +-
 .../common/js/components/SharedEntityEditor.vue    |   81 +-
 .../django_airavata/templates/base.html            |    2 +-
 airavata-django-portal/django_airavata/urls.py     |    6 +-
 airavata-django-portal/requirements.txt            |   26 +-
 airavata-django-portal/setup.py                    |   14 +-
 231 files changed, 7768 insertions(+), 11416 deletions(-)

diff --git a/airavata-django-portal/compose/dbinit/01-init-db.sql b/airavata-django-portal/compose/dbinit/01-init-db.sql
deleted file mode 100644
index 8df43145f..000000000
--- a/airavata-django-portal/compose/dbinit/01-init-db.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-CREATE DATABASE IF NOT EXISTS django_default;
-
-CREATE USER IF NOT EXISTS 'django'@'%' IDENTIFIED BY '123456';
-ALTER USER 'django'@'%' IDENTIFIED BY '123456';
-
-GRANT ALL PRIVILEGES ON *.* TO 'django'@'%';
-
-FLUSH PRIVILEGES;
diff --git a/airavata-django-portal/compose/docker-compose.yaml b/airavata-django-portal/compose/docker-compose.yaml
deleted file mode 100644
index 25e9d2d57..000000000
--- a/airavata-django-portal/compose/docker-compose.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-version: "3.8"
-services:
-  db:
-    image: mariadb:lts-ubi9
-    restart: always
-    command:
-      --default-authentication-plugin=mysql_native_password
-      --character-set-server=utf8
-      --collation-server=utf8_general_ci
-    ports:
-      - "13306:3306"
-    environment:
-      MARIADB_ROOT_USER: root
-      MARIADB_ROOT_PASSWORD: 123456
-      MARIADB_USER: django
-      MARIADB_PASSWORD: 123456
-      MAX_ALLOWED_PACKET: 1073741824
-    healthcheck:
-      test: ["CMD-SHELL", "mariadb-admin ping --silent -u root --password=$$MARIADB_ROOT_PASSWORD"]
-      interval: 10s
-      timeout: 5s
-      retries: 5
-    volumes:
-      - ./dbinit:/docker-entrypoint-initdb.d
-      - mysql_data:/var/lib/mysql
-
-  adminer:
-    image: adminer
-    restart: always
-    ports:
-      - 18888:8080
-
-volumes:
-  mysql_data:
diff --git a/airavata-django-portal/django_airavata/__init__.py b/airavata-django-portal/django_airavata/__init__.py
index 2085355a5..e69de29bb 100644
--- a/airavata-django-portal/django_airavata/__init__.py
+++ b/airavata-django-portal/django_airavata/__init__.py
@@ -1,10 +0,0 @@
-from django.conf import settings
-
-# Only use PyMySQL as the db driver when in a local dev environment
-if settings.DEBUG:
-    try:
-        import pymysql
-
-        pymysql.install_as_MySQLdb()
-    except ImportError:
-        pass
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
index 1b55ae9b0..864f2ac9b 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/BatchQueueResourcePolicy.vue
@@ -4,20 +4,20 @@
       <b-form-group
         label="Maximum Allowed Nodes"
         label-for="max-allowed-nodes"
-        :invalid-feedback="validationFeedback.maxAllowedNodes.invalidFeedback"
-        :state="validationFeedback.maxAllowedNodes.state"
+        :invalid-feedback="validationFeedback.max_allowed_nodes.invalidFeedback"
+        :state="validationFeedback.max_allowed_nodes.state"
       >
         <b-form-input
           id="max-allowed-nodes"
           type="number"
-          v-model="data.maxAllowedNodes"
+          v-model="data.max_allowed_nodes"
           :readonly="readonly"
           @input="policyUpdated"
           min="1"
-          :max="batchQueue.maxNodes"
+          :max="batchQueue.max_nodes"
           :formatter="numberFormatter"
-          :placeholder="'Max Nodes: ' + batchQueue.maxNodes"
-          :state="validationFeedback.maxAllowedNodes.state"
+          :placeholder="'Max Nodes: ' + batchQueue.max_nodes"
+          :state="validationFeedback.max_allowed_nodes.state"
         >
         </b-form-input>
       </b-form-group>
@@ -26,20 +26,20 @@
       <b-form-group
         label="Maximum Allowed Cores"
         label-for="max-allowed-cores"
-        :invalid-feedback="validationFeedback.maxAllowedCores.invalidFeedback"
-        :state="validationFeedback.maxAllowedCores.state"
+        :invalid-feedback="validationFeedback.max_allowed_cores.invalidFeedback"
+        :state="validationFeedback.max_allowed_cores.state"
       >
         <b-form-input
           id="max-allowed-cores"
           type="number"
-          v-model="data.maxAllowedCores"
+          v-model="data.max_allowed_cores"
           :readonly="readonly"
           @input="policyUpdated"
           min="1"
-          :max="batchQueue.maxProcessors"
+          :max="batchQueue.max_processors"
           :formatter="numberFormatter"
-          :placeholder="'Max Cores: ' + batchQueue.maxProcessors"
-          :state="validationFeedback.maxAllowedCores.state"
+          :placeholder="'Max Cores: ' + batchQueue.max_processors"
+          :state="validationFeedback.max_allowed_cores.state"
         >
         </b-form-input>
       </b-form-group>
@@ -49,21 +49,21 @@
         label="Maximum Allowed Wall Time"
         label-for="max-allowed-walltime"
         :invalid-feedback="
-          validationFeedback.maxAllowedWalltime.invalidFeedback
+          validationFeedback.max_allowed_walltime.invalidFeedback
         "
-        :state="validationFeedback.maxAllowedWalltime.state"
+        :state="validationFeedback.max_allowed_walltime.state"
       >
         <b-form-input
           id="max-allowed-walltime"
           type="number"
-          v-model="data.maxAllowedWalltime"
+          v-model="data.max_allowed_walltime"
           :readonly="readonly"
           @input="policyUpdated"
           min="1"
-          :max="batchQueue.maxRunTime"
+          :max="batchQueue.max_run_time"
           :formatter="numberFormatter"
-          :placeholder="'Max Wall Time: ' + batchQueue.maxRunTime"
-          :state="validationFeedback.maxAllowedWalltime.state"
+          :placeholder="'Max Wall Time: ' + batchQueue.max_run_time"
+          :state="validationFeedback.max_allowed_walltime.state"
         >
         </b-form-input>
       </b-form-group>
@@ -99,7 +99,7 @@ export default {
     const localValue = this.value
       ? this.value.clone()
       : new models.BatchQueueResourcePolicy();
-    localValue.queuename = this.batchQueue.queueName;
+    localValue.queuename = this.batchQueue.queue_name;
     return {
       data: localValue,
     };
@@ -107,9 +107,9 @@ export default {
   methods: {
     policyUpdated: function () {
       if (
-        this.data.maxAllowedNodes ||
-        this.data.maxAllowedCores ||
-        this.data.maxAllowedWalltime
+        this.data.max_allowed_nodes ||
+        this.data.max_allowed_cores ||
+        this.data.max_allowed_walltime
       ) {
         this.$emit("input", this.data);
       } else {
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 7bbd2a2e2..be75f91bb 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
@@ -8,9 +8,9 @@
             class="group-resource-profile-name text-muted text-uppercase"
           >
             <i class="fa fa-server" aria-hidden="true"></i>
-            {{ localGroupResourceProfile.groupResourceProfileName }}
+            {{ localGroupResourceProfile.group_resource_profile_name }}
           </div>
-          {{ computeResource.hostName }}
+          {{ computeResource.host_name }}
         </h1>
       </div>
     </div>
@@ -22,16 +22,16 @@
               label="Login Username"
               label-for="login-username"
               :invalid-feedback="
-                validationFeedback.loginUserName.invalidFeedback
+                validationFeedback.login_user_name.invalidFeedback
               "
-              :state="validationFeedback.loginUserName.state"
+              :state="validationFeedback.login_user_name.state"
             >
               <b-form-input
                 id="login-username"
                 type="text"
                 required
-                v-model="data.loginUserName"
-                :state="validationFeedback.loginUserName.state"
+                v-model="data.login_user_name"
+                :state="validationFeedback.login_user_name.state"
                 :disabled="!userHasWriteAccess"
                 @input="validate"
               >
@@ -42,14 +42,14 @@
               label-for="credential-store-token"
             >
               <ssh-credential-selector
-                v-model="data.resourceSpecificCredentialStoreToken"
+                v-model="data.resource_specific_credential_store_token"
                 v-if="localGroupResourceProfile"
                 :readonly="!userHasWriteAccess"
                 :null-option-default-credential-token="
-                  localGroupResourceProfile.defaultCredentialStoreToken
+                  localGroupResourceProfile.default_credential_store_token
                 "
                 :null-option-disabled="
-                  !localGroupResourceProfile.defaultCredentialStoreToken
+                  !localGroupResourceProfile.default_credential_store_token
                 "
               >
                 <template
@@ -58,7 +58,7 @@
                 >
                   <span v-if="nullOptionLabelScope.defaultCredentialSummary">
                     Use the default SSH credential for
-                    {{ localGroupResourceProfile.groupResourceProfileName }} ({{
+                    {{ localGroupResourceProfile.group_resource_profile_name }} ({{
                       nullOptionLabelScope.defaultCredentialSummary.username
                     }}
                     -
@@ -73,15 +73,15 @@
             <b-form-group
               label="Resource Type"
               label-for="resource-type"
-              :invalid-feedback="validationFeedback.resourceType.invalidFeedback"
-              :state="validationFeedback.resourceType.state"
+              :invalid-feedback="validationFeedback.resource_type.invalidFeedback"
+              :state="validationFeedback.resource_type.state"
             >
               <b-form-select
                 id="resource-type"
-                v-model="data.resourceType"
+                v-model="data.resource_type"
                 :options="resourceTypeOptions"
                 :disabled="!userHasWriteAccess"
-                :state="validationFeedback.resourceType.state"
+                :state="validationFeedback.resource_type.state"
                 @change="onResourceTypeChange"
               >
                 <template slot="first">
@@ -98,7 +98,7 @@
                 <b-form-input
                   id="allocation-number"
                   type="text"
-                  v-model="data.allocationProjectNumber"
+                  v-model="data.allocation_project_number"
                   :disabled="!userHasWriteAccess"
                 >
                 </b-form-input>
@@ -113,7 +113,7 @@
                 <b-form-input
                   id="aws-region"
                   type="text"
-                  v-model="data.specificPreferences.region"
+                  v-model="data.specific_preferences.region"
                   :disabled="!userHasWriteAccess"
                 >
                 </b-form-input>
@@ -125,7 +125,7 @@
                 <b-form-input
                   id="preferred-ami-id"
                   type="text"
-                  v-model="data.specificPreferences.preferredAmiId"
+                  v-model="data.specific_preferences.preferred_ami_id"
                   :disabled="!userHasWriteAccess"
                 >
                 </b-form-input>
@@ -137,7 +137,7 @@
                 <b-form-input
                   id="preferred-instance-type"
                   type="text"
-                  v-model="data.specificPreferences.preferredInstanceType"
+                  v-model="data.specific_preferences.preferred_instance_type"
                   :disabled="!userHasWriteAccess"
                 >
                 </b-form-input>
@@ -147,17 +147,17 @@
               label="Scratch Location"
               label-for="scratch-location"
               :invalid-feedback="
-                validationFeedback.scratchLocation.invalidFeedback
+                validationFeedback.scratch_location.invalidFeedback
               "
-              :state="validationFeedback.scratchLocation.state"
+              :state="validationFeedback.scratch_location.state"
             >
               <b-form-input
                 id="scratch-location"
                 type="text"
                 required
-                v-model="data.scratchLocation"
+                v-model="data.scratch_location"
                 :disabled="!userHasWriteAccess"
-                :state="validationFeedback.scratchLocation.state"
+                :state="validationFeedback.scratch_location.state"
                 @input="validate"
               >
               </b-form-input>
@@ -172,7 +172,7 @@
           <div class="card-body">
             <h5 class="card-title">Policy</h5>
             <compute-resource-policy-editor
-              :batch-queues="computeResource.batchQueues"
+              :batch-queues="computeResource.batch_queues"
               :compute-resource-policy="localComputeResourcePolicy"
               :batch-queue-resource-policies="localBatchQueueResourcePolicies"
               :readonly="!userHasWriteAccess"
@@ -221,7 +221,7 @@
         :disabled="!userHasWriteAccess"
         @delete="remove">
         Are you sure you want to remove the preferences for compute resource
-        <strong>{{ computeResource.hostName }}</strong
+        <strong>{{ computeResource.host_name }}</strong
         >?
       </delete-button>
       <b-button class="ml-2" variant="secondary" @click="cancel"
@@ -268,13 +268,13 @@ export default {
   mounted: function () {
     const computeResourcePromise = this.fetchComputeResource(this.host_id);
     if (this.localGroupResourceProfile) {
-      this.userHasWriteAccess = this.localGroupResourceProfile.userHasWriteAccess;
+      this.userHasWriteAccess = this.localGroupResourceProfile.user_has_write_access;
     }
     if (!this.value && this.id && this.host_id) {
       services.GroupResourceProfileService.retrieve({lookup: this.id}).then(
         (groupResourceProfile) => {
           this.localGroupResourceProfile = groupResourceProfile;
-          this.userHasWriteAccess = this.localGroupResourceProfile.userHasWriteAccess;
+          this.userHasWriteAccess = this.localGroupResourceProfile.user_has_write_access;
           const computeResourcePreference = groupResourceProfile.getComputePreference(
             this.host_id
           );
@@ -308,7 +308,7 @@ export default {
       data: this.value
         ? this.value.clone()
         : new models.GroupComputeResourcePreference({
-          computeResourceId: this.host_id,
+          compute_resource_id: this.host_id,
         }),
       localGroupResourceProfile: this.groupResourceProfile
         ? this.groupResourceProfile.clone()
@@ -320,8 +320,8 @@ export default {
         ? this.batchQueueResourcePolicies.map((pol) => pol.clone())
         : [],
       computeResource: {
-        batchQueues: [],
-        jobSubmissionInterfaces: [],
+        batch_queues: [],
+        job_submission_interfaces: [],
       },
       validationErrors: null,
       reservationsInvalid: false,
@@ -351,7 +351,7 @@ export default {
       );
     },
     queueNames() {
-      return this.computeResource.batchQueues.map((bq) => bq.queueName);
+      return this.computeResource.batch_queues.map((bq) => bq.queue_name);
     },
   },
   mixins: [mixins.VModelMixin],
@@ -377,20 +377,20 @@ export default {
             name: "group_resource_preference",
             params: {
               value: groupResourceProfile,
-              id: groupResourceProfile.groupResourceProfileId,
+              id: groupResourceProfile.group_resource_profile_id,
             },
           });
         })
         .catch((error) => {
           if (
             errors.ErrorUtils.isValidationError(error) &&
-            "computePreferences" in error.details.response
+            "compute_preferences" in error.details.response
           ) {
-            const computePreferencesIndex = groupResourceProfile.computePreferences.findIndex(
-              (cp) => cp.computeResourceId === this.host_id
+            const computePreferencesIndex = groupResourceProfile.compute_preferences.findIndex(
+              (cp) => cp.compute_resource_id === this.host_id
             );
             this.validationErrors =
-              error.details.response.computePreferences[
+              error.details.response.compute_preferences[
                 computePreferencesIndex
                 ];
           } else {
@@ -452,10 +452,10 @@ export default {
     createDefaultComputeResourcePolicy: function (computeResourcePromise) {
       computeResourcePromise.then((computeResource) => {
         const defaultComputeResourcePolicy = new models.ComputeResourcePolicy();
-        defaultComputeResourcePolicy.computeResourceId = this.host_id;
-        defaultComputeResourcePolicy.groupResourceProfileId = this.id;
-        defaultComputeResourcePolicy.allowedBatchQueues = computeResource.batchQueues.map(
-          (queue) => queue.queueName
+        defaultComputeResourcePolicy.compute_resource_id = this.host_id;
+        defaultComputeResourcePolicy.group_resource_profile_id = this.id;
+        defaultComputeResourcePolicy.allowed_batch_queues = computeResource.batch_queues.map(
+          (queue) => queue.queue_name
         );
         this.localComputeResourcePolicy = defaultComputeResourcePolicy;
       });
@@ -470,7 +470,7 @@ export default {
     addReservation(reservation) {
       this.data.reservations.push(reservation);
       this.data.reservations.sort((a, b) =>
-        a.startTime < b.startTime ? -1 : 1
+        a.start_time < b.start_time ? -1 : 1
       );
     },
     deleteReservation(reservation) {
@@ -486,21 +486,21 @@ export default {
       this.data.reservations.splice(reservationIndex, 1, reservation);
     },
     onResourceTypeChange() {
-      if (!this.data.resourceType) {
-        this.data.specificPreferences = null;
+      if (!this.data.resource_type) {
+        this.data.specific_preferences = null;
         this.validate();
         return;
       }
       if (this.data.resetSpecificPreferences) {
         this.data.resetSpecificPreferences();
       } else {
-        const resourceTypeName = this.data.resourceType.name;
+        const resourceTypeName = this.data.resource_type.name;
         const modelClassName = this._getPreferenceModelClassName(resourceTypeName);
         if (modelClassName && models[modelClassName]) {
           const PreferenceModel = models[modelClassName];
-          this.data.specificPreferences = new PreferenceModel();
+          this.data.specific_preferences = new PreferenceModel();
         } else {
-          this.data.specificPreferences = null;
+          this.data.specific_preferences = null;
         }
       }
       this.validate();
@@ -517,7 +517,7 @@ export default {
       if (this.data.isResourceType) {
         return this.data.isResourceType(resourceTypeName);
       }
-      return this.data.resourceType && this.data.resourceType.name === resourceTypeName;
+      return this.data.resource_type && this.data.resource_type.name === resourceTypeName;
     },
   },
   beforeRouteEnter: function (to, from, next) {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicyEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicyEditor.vue
index fb83bff63..01665144a 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicyEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicyEditor.vue
@@ -2,32 +2,32 @@
   <b-form-group
     label="Allowed Queues"
     v-if="localComputeResourcePolicy"
-    :invalid-feedback="validationFeedback.allowedBatchQueues.invalidFeedback"
-    :state="validationFeedback.allowedBatchQueues.state"
+    :invalid-feedback="validationFeedback.allowed_batch_queues.invalidFeedback"
+    :state="validationFeedback.allowed_batch_queues.state"
   >
-    <div v-for="batchQueue in batchQueues" :key="batchQueue.queueName">
+    <div v-for="batchQueue in batchQueues" :key="batchQueue.queue_name">
       <b-form-checkbox
         :checked="
-          localComputeResourcePolicy.allowedBatchQueues.includes(
-            batchQueue.queueName
+          localComputeResourcePolicy.allowed_batch_queues.includes(
+            batchQueue.queue_name
           )
         "
         :disabled="readonly"
         @input="batchQueueChecked(batchQueue, $event)"
       >
-        {{ batchQueue.queueName }}
+        {{ batchQueue.queue_name }}
       </b-form-checkbox>
       <batch-queue-resource-policy
         v-if="
-          localComputeResourcePolicy.allowedBatchQueues.includes(
-            batchQueue.queueName
+          localComputeResourcePolicy.allowed_batch_queues.includes(
+            batchQueue.queue_name
           )
         "
         :batch-queue="batchQueue"
         :readonly="readonly"
         :value="
           localBatchQueueResourcePolicies.find(
-            (pol) => pol.queuename === batchQueue.queueName
+            (pol) => pol.queuename === batchQueue.queue_name
           )
         "
         @input="updatedBatchQueueResourcePolicy(batchQueue, $event)"
@@ -94,27 +94,27 @@ export default {
     },
     allowedInvalidBatchQueueResourcePolicies() {
       return this.invalidBatchQueueResourcePolicies.filter((queueName) =>
-        this.localComputeResourcePolicy.allowedBatchQueues.includes(queueName)
+        this.localComputeResourcePolicy.allowed_batch_queues.includes(queueName)
       );
     },
   },
   methods: {
     batchQueueChecked: function (batchQueue, checked) {
       if (checked) {
-        this.localComputeResourcePolicy.allowedBatchQueues.push(
-          batchQueue.queueName
+        this.localComputeResourcePolicy.allowed_batch_queues.push(
+          batchQueue.queue_name
         );
       } else {
-        const queueIndex = this.localComputeResourcePolicy.allowedBatchQueues.indexOf(
-          batchQueue.queueName
+        const queueIndex = this.localComputeResourcePolicy.allowed_batch_queues.indexOf(
+          batchQueue.queue_name
         );
-        this.localComputeResourcePolicy.allowedBatchQueues.splice(
+        this.localComputeResourcePolicy.allowed_batch_queues.splice(
           queueIndex,
           1
         );
         // Remove batchQueueResourcePolicy if it exists
         const policyIndex = this.localBatchQueueResourcePolicies.findIndex(
-          (pol) => pol.queuename === batchQueue.queueName
+          (pol) => pol.queuename === batchQueue.queue_name
         );
         if (policyIndex >= 0) {
           this.localBatchQueueResourcePolicies.splice(policyIndex, 1);
@@ -134,7 +134,7 @@ export default {
       batchQueue,
       batchQueueResourcePolicy
     ) {
-      const queueName = batchQueue.queueName;
+      const queueName = batchQueue.queue_name;
       if (batchQueueResourcePolicy) {
         const existingPolicy = this.localBatchQueueResourcePolicies.find(
           (pol) => pol.queuename === queueName
@@ -162,10 +162,10 @@ export default {
     },
     recordValidBatchQueueResourcePolicy(batchQueue) {
       if (
-        this.invalidBatchQueueResourcePolicies.includes(batchQueue.queueName)
+        this.invalidBatchQueueResourcePolicies.includes(batchQueue.queue_name)
       ) {
         const index = this.invalidBatchQueueResourcePolicies.indexOf(
-          batchQueue.queueName
+          batchQueue.queue_name
         );
         this.invalidBatchQueueResourcePolicies.splice(index, 1);
       }
@@ -173,9 +173,9 @@ export default {
     },
     recordInvalidBatchQueueResourcePolicy(batchQueue) {
       if (
-        !this.invalidBatchQueueResourcePolicies.includes(batchQueue.queueName)
+        !this.invalidBatchQueueResourcePolicies.includes(batchQueue.queue_name)
       ) {
-        this.invalidBatchQueueResourcePolicies.push(batchQueue.queueName);
+        this.invalidBatchQueueResourcePolicies.push(batchQueue.queue_name);
       }
       this.validate(); // propagate validation
     },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicySummary.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicySummary.vue
index f0ae38ed6..520b6b92d 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicySummary.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourcePolicySummary.vue
@@ -6,24 +6,24 @@
         (<span title="Max Allowed Nodes"
           >N:
           {{
-            queuePolicy.policy.maxAllowedNodes
-              ? queuePolicy.policy.maxAllowedNodes
+            queuePolicy.policy.max_allowed_nodes
+              ? queuePolicy.policy.max_allowed_nodes
               : "Unlimited"
           }}</span
         >,
         <span title="Max Allowed Cores"
           >C:
           {{
-            queuePolicy.policy.maxAllowedCores
-              ? queuePolicy.policy.maxAllowedCores
+            queuePolicy.policy.max_allowed_cores
+              ? queuePolicy.policy.max_allowed_cores
               : "Unlimited"
           }}</span
         >,
         <span title="Max Allowed Walltime"
           >W:
           {{
-            queuePolicy.policy.maxAllowedWalltime
-              ? queuePolicy.policy.maxAllowedWalltime
+            queuePolicy.policy.max_allowed_walltime
+              ? queuePolicy.policy.max_allowed_walltime
               : "Unlimited"
           }}</span
         >)
@@ -51,8 +51,8 @@ export default {
       const computeResourcePolicy = this.groupResourceProfile.getComputeResourcePolicy(
         this.computeResourceId
       );
-      if (computeResourcePolicy && computeResourcePolicy.allowedBatchQueues) {
-        const queues = computeResourcePolicy.allowedBatchQueues.slice();
+      if (computeResourcePolicy && computeResourcePolicy.allowed_batch_queues) {
+        const queues = computeResourcePolicy.allowed_batch_queues.slice();
         queues.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
         return queues;
       } else {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationEditor.vue
index c58227c4b..854cc875f 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationEditor.vue
@@ -8,7 +8,7 @@
     >
       <b-form-input
         id="reservation-name"
-        v-model="data.reservationName"
+        v-model="data.reservation_name"
         type="text"
         @input="nameInputBegins = true"
         :state="nameValidationState"
@@ -17,8 +17,8 @@
     <b-form-group
       label="Start Time"
       label-for="start-time"
-      :invalid-feedback="getValidationFeedback('startTime')"
-      :state="getValidationState('startTime')"
+      :invalid-feedback="getValidationFeedback('start_time')"
+      :state="getValidationState('start_time')"
     >
       <datetime
         id="start-time"
@@ -39,14 +39,14 @@
         :week-start="7"
         use12-hour
         auto
-        @input="data.startTime = stringToDate($event)"
+        @input="data.start_time = stringToDate($event)"
       ></datetime>
     </b-form-group>
     <b-form-group
       label="End Time"
       label-for="end-time"
-      :invalid-feedback="getValidationFeedback('endTime')"
-      :state="getValidationState('endTime')"
+      :invalid-feedback="getValidationFeedback('end_time')"
+      :state="getValidationState('end_time')"
     >
       <datetime
         id="end-time"
@@ -54,7 +54,7 @@
         :value="endTimeAsString"
         :input-class="{
           'form-control': true,
-          'is-invalid': getValidationState('endTime'),
+          'is-invalid': getValidationState('end_time'),
         }"
         :format="{
           year: 'numeric',
@@ -71,20 +71,20 @@
         :min-datetime="startTimeAsString"
         use12-hour
         auto
-        @input="data.endTime = stringToDate($event)"
+        @input="data.end_time = stringToDate($event)"
       ></datetime>
     </b-form-group>
     <b-form-group
       label="Queues"
       label-for="queues"
-      :invalid-feedback="getValidationFeedback('queueNames')"
-      :state="getValidationState('queueNames')"
+      :invalid-feedback="getValidationFeedback('queue_names')"
+      :state="getValidationState('queue_names')"
     >
       <b-form-checkbox-group
         id="queues"
-        v-model="data.queueNames"
+        v-model="data.queue_names"
         :options="queueNameOptions"
-        :state="getValidationState('queueNames')"
+        :state="getValidationState('queue_names')"
       />
     </b-form-group>
   </b-form>
@@ -117,19 +117,19 @@ export default {
   },
   computed: {
     startTimeAsString() {
-      return this.data.startTime.toISOString();
+      return this.data.start_time.toISOString();
     },
     endTimeAsString() {
-      return this.data.endTime.toISOString();
+      return this.data.end_time.toISOString();
     },
     nameValidationFeedback() {
-      return this.getValidationFeedback("reservationName");
+      return this.getValidationFeedback("reservation_name");
     },
     nameValidationState() {
       if (this.nameInputBegins === false) {
         return null;
       }
-      return this.getValidationState("reservationName");
+      return this.getValidationState("reservation_name");
     },
     queueNameOptions() {
       return this.queues.slice().sort();
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationList.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationList.vue
index 8b04bcf90..434182286 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationList.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationList.vue
@@ -48,14 +48,14 @@
     </template>
     <template slot="item-list" slot-scope="slotProps">
       <b-table hover :fields="fields" :items="slotProps.items">
-        <template slot="cell(reservationName)" slot-scope="data">
+        <template slot="cell(reservation_name)" slot-scope="data">
           {{ data.value }}
           <b-badge v-if="data.item.isExpired">Expired</b-badge>
           <b-badge v-if="data.item.isActive" variant="success">Active</b-badge>
           <b-badge v-if="data.item.isUpcoming" variant="info">Upcoming</b-badge>
         </template>
-        <template slot="cell(queueNames)" slot-scope="data">
-          <ul v-for="queueName in data.item.queueNames" :key="queueName">
+        <template slot="cell(queue_names)" slot-scope="data">
+          <ul v-for="queueName in data.item.queue_names" :key="queueName">
             <li>{{ queueName }}</li>
           </ul>
         </template>
@@ -75,7 +75,7 @@
             @delete="deleteReservation(data.item)"
           >
             Are you sure you want to delete reservation
-            <strong>{{ data.item.reservationName }}</strong
+            <strong>{{ data.item.reservation_name }}</strong
             >?
           </delete-link>
         </template>
@@ -142,21 +142,21 @@ export default {
       return [
         {
           label: "Name",
-          key: "reservationName",
+          key: "reservation_name",
         },
         {
           label: "Queues",
-          key: "queueNames",
+          key: "queue_names",
         },
         {
           label: "Start Time",
-          key: "startTime",
+          key: "start_time",
           formatter: (value) =>
             utils.dateFormatters.dateTimeInMinutesWithTimeZone.format(value),
         },
         {
           label: "End Time",
-          key: "endTime",
+          key: "end_time",
           formatter: (value) =>
             utils.dateFormatters.dateTimeInMinutesWithTimeZone.format(value),
         },
@@ -206,7 +206,7 @@ export default {
     addNewReservation() {
       this.newReservation = new models.ComputeResourceReservation();
       this.newReservationValid = false;
-      this.newReservation.queueNames = this.queues.slice();
+      this.newReservation.queue_names = this.queues.slice();
       this.showNewItemEditor = true;
     },
     saveNewReservation() {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationsSummary.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationsSummary.vue
index 498475b59..94a91b567 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationsSummary.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/ComputeResourceReservationsSummary.vue
@@ -35,7 +35,7 @@ export default {
         : [];
     },
     activeReservationNames() {
-      return this.activeReservations.map((r) => r.reservationName);
+      return this.activeReservations.map((r) => r.reservation_name);
     },
     upcomingReservations() {
       return this.reservations
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
index 2c8b077e3..153e7083a 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/admin/group_resource_preferences/GroupComputeResourcePreference.vue
@@ -16,7 +16,7 @@
               <b-form-input
                 id="profile-name"
                 type="text"
-                v-model="data.groupResourceProfileName"
+                v-model="data.group_resource_profile_name"
                 :disabled="!userHasWriteAccess"
                 required
                 placeholder="Name of this Group Resource Profile"
@@ -29,7 +29,7 @@
             >
               <ssh-credential-selector
                 id="default-credential-store-token"
-                v-model="data.defaultCredentialStoreToken"
+                v-model="data.default_credential_store_token"
                 :readonly="!userHasWriteAccess"
               >
               </ssh-credential-selector>
@@ -40,7 +40,7 @@
       </div>
     </div>
     <list-layout
-      :items="data.computePreferences"
+      :items="data.compute_preferences"
       :newButtonDisabled="!userHasWriteAccess"
       title="Compute Preferences"
       new-item-button-text="New Compute Preference"
@@ -51,16 +51,16 @@
           hover
           :fields="computePreferencesFields"
           :items="slotProps.items"
-          sort-by="computeResourceId"
+          sort-by="compute_resource_id"
         >
-          <template slot="cell(computeResourceId)" slot-scope="row">
+          <template slot="cell(compute_resource_id)" slot-scope="row">
             <compute-resource-name
-              :compute-resource-id="row.item.computeResourceId"
+              :compute-resource-id="row.item.compute_resource_id"
             />
           </template>
           <template slot="cell(policy)" slot-scope="row">
             <compute-resource-policy-summary
-              :compute-resource-id="row.item.computeResourceId"
+              :compute-resource-id="row.item.compute_resource_id"
               :group-resource-profile="data"
             />
           </template>
@@ -76,13 +76,13 @@
                 params: {
                   value: row.item,
                   id: id,
-                  host_id: row.item.computeResourceId,
+                  host_id: row.item.compute_resource_id,
                   groupResourceProfile: data,
                   computeResourcePolicy: data.getComputeResourcePolicy(
-                    row.item.computeResourceId
+                    row.item.compute_resource_id
                   ),
                   batchQueueResourcePolicies: data.getBatchQueueResourcePolicies(
-                    row.item.computeResourceId
+                    row.item.compute_resource_id
                   ),
                 },
               }"
@@ -99,13 +99,13 @@
                 params: {
                   value: row.item,
                   id: id,
-                  host_id: row.item.computeResourceId,
+                  host_id: row.item.compute_resource_id,
                   groupResourceProfile: data,
                   computeResourcePolicy: data.getComputeResourcePolicy(
-                    row.item.computeResourceId
+                    row.item.compute_resource_id
                   ),
                   batchQueueResourcePolicies: data.getBatchQueueResourcePolicies(
-                    row.item.computeResourceId
+                    row.item.compute_resource_id
                   ),
                 },
               }"
@@ -117,13 +117,13 @@
             <delete-link
               class="action-link"
               v-if="userHasWriteAccess"
-              @delete="removeComputePreference(row.item.computeResourceId)"
+              @delete="removeComputePreference(row.item.compute_resource_id)"
             >
               Are you sure you want to remove the preferences for compute
               resource
               <strong>
                 <compute-resource-name
-                  :compute-resource-id="row.item.computeResourceId"
+                  :compute-resource-id="row.item.compute_resource_id"
                 /> </strong
               >?
             </delete-link>
@@ -145,7 +145,7 @@
         @delete="removeGroupResourceProfile"
       >
         Are you sure you want to remove Group Resource Profile
-        <strong>{{ data.groupResourceProfileName }}</strong
+        <strong>{{ data.group_resource_profile_name }}</strong
         >?
       </delete-button>
       <b-button class="ml-2" variant="secondary" @click="cancel"
@@ -183,11 +183,11 @@ export default {
   },
   mounted: function () {
     if (this.id) {
-      if (!this.value.groupResourceProfileId) {
+      if (!this.value.group_resource_profile_id) {
         services.GroupResourceProfileService.retrieve({ lookup: this.id }).then(
           (grp) => {
             this.data = grp;
-            this.userHasWriteAccess = this.data.userHasWriteAccess;
+            this.userHasWriteAccess = this.data.user_has_write_access;
           }
         );
       }
@@ -207,20 +207,20 @@ export default {
       data: data,
       service: services.GroupResourceProfileService,
       sharedEntity: null,
-      userHasWriteAccess: data.userHasWriteAccess,
+      userHasWriteAccess: data.user_has_write_access,
       computePreferencesFields: [
         {
           label: "Name",
-          key: "computeResourceId",
+          key: "compute_resource_id",
           sortable: true,
         },
         {
           label: "Username",
-          key: "loginUserName",
+          key: "login_user_name",
         },
         {
           label: "Allocation",
-          key: "allocationProjectNumber",
+          key: "allocation_project_number",
         },
         {
           label: "Policy",
@@ -251,16 +251,16 @@ export default {
   },
   computed: {
     excludedComputeResourceIds() {
-      const currentPrefs = this.data.computePreferences
-        ? this.data.computePreferences.map(
-            (computePreference) => computePreference.computeResourceId
+      const currentPrefs = this.data.compute_preferences
+        ? this.data.compute_preferences.map(
+            (computePreference) => computePreference.compute_resource_id
           )
         : [];
       return currentPrefs;
     },
     title: function () {
       return this.id
-        ? this.data.groupResourceProfileName
+        ? this.data.group_resource_profile_name
         : "New Group Resource Profile";
     },
     owner() {
@@ -269,13 +269,13 @@ export default {
         : null;
     },
     ownerUserId() {
-      return this.owner ? this.owner.userId : null;
+      return this.owner ? this.owner.user_id : null;
     },
     ownerTitle() {
       return this.owner
-        ? this.owner.firstName +
+        ? this.owner.first_name +
             " " +
-            this.owner.lastName +
+            this.owner.last_name +
             " (" +
             this.owner.email +
             ")"
@@ -291,7 +291,7 @@ export default {
         persist = this.service.create({ data: this.data }).then((data) => {
           // Merge sharing settings with default sharing settings created when
           // Group Resource Profile was created
-          const groupResourceProfileId = data.groupResourceProfileId;
+          const groupResourceProfileId = data.group_resource_profile_id;
           return this.$refs.shareButton.mergeAndSave(groupResourceProfileId);
         });
       }
@@ -307,13 +307,13 @@ export default {
     },
     onSelectComputeResource: function (computeResourceId) {
       const computeResourcePreference = new models.GroupComputeResourcePreference();
-      computeResourcePreference.computeResourceId = computeResourceId;
+      computeResourcePreference.compute_resource_id = computeResourceId;
       this.$router.push({
         name: "compute_preference_for_new_group_resource_profile",
         params: {
           value: computeResourcePreference,
           id: this.id,
-          host_id: computeResourcePreference.computeResourceId,
+          host_id: computeResourcePreference.compute_resource_id,
           groupResourceProfile: this.data,
         },
       });
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue
index ecb5eba8d..100ed782f 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue
@@ -22,7 +22,7 @@
           <b-form-input
             id="executable-path"
             type="text"
-            v-model="data.executablePath"
+            v-model="data.executable_path"
             required
             :disabled="readonly"
           ></b-form-input>
@@ -44,7 +44,7 @@
         >
           <b-form-textarea
             id="deployment-description"
-            v-model="data.appDeploymentDescription"
+            v-model="data.app_deployment_description"
             :rows="3"
             :disabled="readonly"
           ></b-form-textarea>
@@ -52,43 +52,43 @@
         <command-objects-editor
           title="Module Load Commands"
           add-button-label="Add Module Load Command"
-          v-model="data.moduleLoadCmds"
+          v-model="data.module_load_cmds"
           :readonly="readonly"
         />
         <set-env-paths-editor
           title="Library Prepend Paths"
           add-button-label="Add a Library Prepend Path"
-          v-model="data.libPrependPaths"
+          v-model="data.lib_prepend_paths"
           :readonly="readonly"
         />
         <set-env-paths-editor
           title="Library Append Paths"
           add-button-label="Add a Library Append Path"
-          v-model="data.libAppendPaths"
+          v-model="data.lib_append_paths"
           :readonly="readonly"
         />
         <set-env-paths-editor
           title="Environment Variables"
           add-button-label="Add Environment Variable"
-          v-model="data.setEnvironment"
+          v-model="data.set_environment"
           :readonly="readonly"
         />
         <command-objects-editor
           title="Pre Job Commands"
           add-button-label="Add Pre Job Command"
-          v-model="data.preJobCommands"
+          v-model="data.pre_job_commands"
           :readonly="readonly"
         />
         <command-objects-editor
           title="Post Job Commands"
           add-button-label="Add Post Job Command"
-          v-model="data.postJobCommands"
+          v-model="data.post_job_commands"
           :readonly="readonly"
         />
         <b-form-group label="Default Queue Name" label-for="default-queue-name">
           <b-form-select
             id="default-queue-name"
-            v-model="data.defaultQueueName"
+            v-model="data.default_queue_name"
             :options="queueNameOptions"
             @change="defaultQueueChanged"
             :disabled="readonly"
@@ -102,7 +102,7 @@
           <b-form-input
             id="default-node-count"
             type="number"
-            v-model="data.defaultNodeCount"
+            v-model="data.default_node_count"
             min="0"
             :max="maxNodes"
             :disabled="defaultQueueAttributesDisabled"
@@ -112,7 +112,7 @@
           <b-form-input
             id="default-cpu-count"
             type="number"
-            v-model="data.defaultCPUCount"
+            v-model="data.default_cpu_count"
             min="0"
             :max="maxCPUCount"
             :disabled="defaultQueueAttributesDisabled"
@@ -128,7 +128,7 @@
           <b-form-input
             id="default-walltime"
             type="number"
-            v-model="data.defaultWalltime"
+            v-model="data.default_walltime"
             min="0"
             :max="maxWalltime"
             :disabled="defaultQueueAttributesDisabled"
@@ -184,9 +184,9 @@ export default {
   computed: {
     name() {
       if (this.computeResource) {
-        return this.computeResource.hostName;
+        return this.computeResource.host_name;
       } else {
-        return this.data.computeHostId.substring(0, 10) + "...";
+        return this.data.compute_host_id.substring(0, 10) + "...";
       }
     },
     parallelismTypeOptions() {
@@ -201,47 +201,47 @@ export default {
       if (!this.computeResource) {
         return [];
       }
-      return this.computeResource.batchQueues.map((queue) => {
+      return this.computeResource.batch_queues.map((queue) => {
         return {
-          value: queue.queueName,
-          text: queue.queueName,
+          value: queue.queue_name,
+          text: queue.queue_name,
         };
       });
     },
     maxNodes() {
       const queue = this.computeResource
-        ? this.computeResource.batchQueues.find(
-            (q) => q.queueName === this.data.defaultQueueName
+        ? this.computeResource.batch_queues.find(
+            (q) => q.queue_name === this.data.default_queue_name
           )
         : null;
-      return queue ? queue.maxNodes : 0;
+      return queue ? queue.max_nodes : 0;
     },
     maxCPUCount() {
       const queue = this.computeResource
-        ? this.computeResource.batchQueues.find(
-            (q) => q.queueName === this.data.defaultQueueName
+        ? this.computeResource.batch_queues.find(
+            (q) => q.queue_name === this.data.default_queue_name
           )
         : null;
-      return queue ? queue.maxProcessors : 0;
+      return queue ? queue.max_processors : 0;
     },
     maxWalltime() {
       const queue = this.computeResource
-        ? this.computeResource.batchQueues.find(
-            (q) => q.queueName === this.data.defaultQueueName
+        ? this.computeResource.batch_queues.find(
+            (q) => q.queue_name === this.data.default_queue_name
           )
         : null;
-      return queue ? queue.maxRuntime : 0;
+      return queue ? queue.max_run_time : 0;
     },
     cpuPerNode() {
       const queue = this.computeResource
-        ? this.computeResource.batchQueues.find(
-            (q) => q.queueName === this.data.defaultQueueName
+        ? this.computeResource.batch_queues.find(
+            (q) => q.queue_name === this.data.default_queue_name
           )
         : null;
-      return queue ? queue.cpuPerNode : 0;
+      return queue ? queue.cpu_per_node : 0;
     },
     defaultQueueAttributesDisabled() {
-      return !this.data.defaultQueueName || this.readonly;
+      return !this.data.default_queue_name || this.readonly;
     },
     owner() {
       return this.localSharedEntity && this.localSharedEntity.owner
@@ -249,13 +249,13 @@ export default {
         : null;
     },
     ownerUserId() {
-      return this.owner ? this.owner.userId : null;
+      return this.owner ? this.owner.user_id : null;
     },
     ownerTitle() {
       return this.owner
-        ? this.owner.firstName +
+        ? this.owner.first_name +
             " " +
-            this.owner.lastName +
+            this.owner.last_name +
             " (" +
             this.owner.email +
             ")"
@@ -264,7 +264,7 @@ export default {
   },
   created() {
     services.ComputeResourceService.retrieve({
-      lookup: this.data.computeHostId,
+      lookup: this.data.compute_host_id,
     }).then((computeResource) => {
       this.computeResource = computeResource;
     });
@@ -282,16 +282,16 @@ export default {
     },
     defaultQueueChanged(queueName) {
       if (queueName) {
-        const queue = this.computeResource.batchQueues.find(
-          (q) => q.queueName === queueName
+        const queue = this.computeResource.batch_queues.find(
+          (q) => q.queue_name === queueName
         );
-        this.data.defaultNodeCount = queue.defaultNodeCount;
-        this.data.defaultCPUCount = queue.defaultCPUCount;
-        this.data.defaultWalltime = queue.defaultWalltime;
+        this.data.default_node_count = queue.default_node_count;
+        this.data.default_cpu_count = queue.default_cpu_count;
+        this.data.default_walltime = queue.default_walltime;
       } else {
-        this.data.defaultNodeCount = null;
-        this.data.defaultCPUCount = null;
-        this.data.defaultWalltime = null;
+        this.data.default_node_count = null;
+        this.data.default_cpu_count = null;
+        this.data.default_walltime = null;
       }
     },
     savedSharedEntity(newSharedEntity) {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentsList.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentsList.vue
index 3c576c4cd..b754dc0fb 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentsList.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentsList.vue
@@ -13,17 +13,17 @@
           hover
           :fields="fields"
           :items="slotProps.items"
-          sort-by="computeHostId"
+          sort-by="compute_host_id"
         >
           <template slot="cell(action)" slot-scope="data">
             <router-link
               class="action-link"
-              v-if="!data.item.userHasWriteAccess"
+              v-if="!data.item.user_has_write_access"
               :to="{
                 name: 'application_deployment',
                 params: {
                   id: id,
-                  deploymentId: data.item.appDeploymentId,
+                  deploymentId: data.item.app_deployment_id,
                   readonly: true,
                 },
               }"
@@ -33,12 +33,12 @@
             </router-link>
             <router-link
               class="action-link"
-              v-if="data.item.userHasWriteAccess && data.item.appDeploymentId"
+              v-if="data.item.user_has_write_access && data.item.app_deployment_id"
               :to="{
                 name: 'application_deployment',
                 params: {
                   id: id,
-                  deploymentId: data.item.appDeploymentId,
+                  deploymentId: data.item.app_deployment_id,
                   readonly: false,
                 },
               }"
@@ -48,12 +48,12 @@
             </router-link>
             <router-link
               class="action-link"
-              v-if="data.item.userHasWriteAccess && !data.item.appDeploymentId"
+              v-if="data.item.user_has_write_access && !data.item.app_deployment_id"
               :to="{
                 name: 'new_application_deployment',
                 params: {
                   id: id,
-                  hostId: data.item.computeHostId,
+                  hostId: data.item.compute_host_id,
                   readonly: false,
                 },
               }"
@@ -62,13 +62,13 @@
               <i class="fa fa-edit" aria-hidden="true"></i>
             </router-link>
             <delete-link
-              v-if="data.item.userHasWriteAccess"
+              v-if="data.item.user_has_write_access"
               @delete="removeApplicationDeployment(data.item)"
               class="action-link"
             >
               Are you sure you want to remove the
               <strong>{{
-                getComputeResourceName(data.item.computeHostId)
+                getComputeResourceName(data.item.compute_host_id)
               }}</strong>
               deployment?
             </delete-link>
@@ -123,13 +123,13 @@ export default {
       return [
         {
           label: "Compute Resource",
-          key: "computeHostId",
+          key: "compute_host_id",
           sortable: true,
           formatter: (value) => this.getComputeResourceName(value),
         },
         {
           label: "Description",
-          key: "appDeploymentDescription",
+          key: "app_deployment_description",
         },
         {
           label: "Action",
@@ -144,9 +144,9 @@ export default {
         // Create a set of all computeResourceIds in GroupResourceProfiles
         const groupResourceProfileCompResources = {};
         for (const groupResourceProfile of this.groupResourceProfiles) {
-          for (const computePreference of groupResourceProfile.computePreferences) {
+          for (const computePreference of groupResourceProfile.compute_preferences) {
             groupResourceProfileCompResources[
-              computePreference.computeResourceId
+              computePreference.compute_resource_id
             ] = null;
           }
         }
@@ -178,7 +178,7 @@ export default {
       }
     },
     excludedComputeResourceIds() {
-      return this.deployments.map((dep) => dep.computeHostId);
+      return this.deployments.map((dep) => dep.compute_host_id);
     },
   },
   mounted() {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
index 67243c5df..34c498a23 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
@@ -45,7 +45,7 @@
           v-if="appModule"
           v-model="appModule"
           @input="appModuleIsDirty = true"
-          :readonly="!appModule.userHasWriteAccess"
+          :readonly="!appModule.user_has_write_access"
           :validation-errors="appModuleValidationErrors"
         />
         <router-view
@@ -53,7 +53,7 @@
           v-if="appInterface"
           v-model="appInterface"
           @input="appInterfaceIsDirty = true"
-          :readonly="!appInterface.userHasWriteAccess"
+          :readonly="!appInterface.user_has_write_access"
         />
         <router-view
           name="deployments"
@@ -61,7 +61,7 @@
           :deployments="appDeployments"
           @new="createNewDeployment"
           @delete="deleteApplicationDeployment"
-          :readonly="!appModule.userHasWriteAccess"
+          :readonly="!appModule.user_has_write_access"
         />
         <router-view
           name="deployment"
@@ -89,7 +89,7 @@
         @delete="deleteApplication"
       >
         Are you sure you want to delete the
-        <strong>{{ appModule ? appModule.appModuleName : "" }}</strong>
+        <strong>{{ appModule ? appModule.app_module_name : "" }}</strong>
         application?
       </delete-button>
       <b-button class="editor-button" variant="secondary" @click="cancel">
@@ -139,8 +139,8 @@ export default {
   computed: {
     title: function () {
       if (this.id) {
-        return this.appModule && this.appModule.appModuleName
-          ? this.appModule.appModuleName
+        return this.appModule && this.appModule.app_module_name
+          ? this.appModule.app_module_name
           : "";
       } else {
         return "Create a New Application";
@@ -155,7 +155,7 @@ export default {
       );
     },
     readonly() {
-      return this.appModule && !this.appModule.userHasWriteAccess;
+      return this.appModule && !this.appModule.user_has_write_access;
     },
   },
   created() {
@@ -170,9 +170,7 @@ export default {
           this.initializeDeploymentEditing();
         });
       } else {
-        this.appModule = new models.ApplicationModule({
-          userHasWriteAccess: true,
-        });
+        this.appModule = new models.ApplicationModule({ user_has_write_access: true });
       }
     },
     initializeDeploymentEditing() {
@@ -213,7 +211,7 @@ export default {
     updateApplicationModule(appModule) {
       return services.ApplicationModuleService.update(
         {
-          lookup: appModule.appModuleId,
+          lookup: appModule.app_module_id,
           data: appModule,
         },
         { ignoreErrors: true }
@@ -264,9 +262,7 @@ export default {
         .catch((error) => {
           if (error.details.status === 404) {
             // If there is no interface, just create a new instance
-            const appInterface = new models.ApplicationInterfaceDefinition({
-              userHasWriteAccess: true,
-            });
+            const appInterface = new models.ApplicationInterfaceDefinition({ user_has_write_access: true });
             appInterface.addStandardOutAndStandardErrorOutputs();
             this.appInterface = appInterface;
             this.appInterfaceIsDirty = true;
@@ -288,7 +284,7 @@ export default {
     },
     updateApplicationInterface(appInterface) {
       return services.ApplicationInterfaceService.update({
-        lookup: appInterface.applicationInterfaceId,
+        lookup: appInterface.application_interface_id,
         data: appInterface,
       }).then((appInterface) => {
         this.appInterfaceIsDirty = false;
@@ -297,16 +293,16 @@ export default {
       });
     },
     saveApplicationInterface(appInterface) {
-      appInterface.applicationName = this.appModule.appModuleName;
-      appInterface.applicationModules = [this.id];
-      return appInterface.applicationInterfaceId
+      appInterface.application_name = this.appModule.app_module_name;
+      appInterface.application_modules = [this.id];
+      return appInterface.application_interface_id
         ? this.updateApplicationInterface(appInterface)
         : this.createApplicationInterface(appInterface);
     },
     deleteApplicationInterface(appInterface) {
-      if (appInterface.applicationInterfaceId) {
+      if (appInterface.application_interface_id) {
         return services.ApplicationInterfaceService.delete({
-          lookup: appInterface.applicationInterfaceId,
+          lookup: appInterface.application_interface_id,
         }).then(() => (this.appInterfaceIsDirty = false));
       } else {
         this.appInterfaceIsDirty = false;
@@ -342,7 +338,7 @@ export default {
     },
     updateApplicationDeployment(appDeployment) {
       return services.ApplicationDeploymentService.update({
-        lookup: appDeployment.appDeploymentId,
+        lookup: appDeployment.app_deployment_id,
         data: appDeployment,
       }).then((appDeployment) => {
         this.removeDirtyAppDeploymentComputeHostId(appDeployment);
@@ -351,21 +347,21 @@ export default {
       });
     },
     saveApplicationDeployment(appDeployment) {
-      return appDeployment.appDeploymentId
+      return appDeployment.app_deployment_id
         ? this.updateApplicationDeployment(appDeployment)
         : this.createApplicationDeployment(appDeployment);
     },
     deleteApplicationDeployment(appDeployment) {
-      if (appDeployment.appDeploymentId) {
+      if (appDeployment.app_deployment_id) {
         return services.ApplicationDeploymentService.delete({
-          lookup: appDeployment.appDeploymentId,
+          lookup: appDeployment.app_deployment_id,
         }).then(() => {
           this.removeDirtyAppDeploymentComputeHostId(appDeployment);
           return this.loadApplicationDeployments(this.id);
         });
       } else {
         const depIndex = this.appDeployments.findIndex(
-          (dep) => dep.computeHostId === appDeployment.computeHostId
+          (dep) => dep.compute_host_id === appDeployment.compute_host_id
         );
         this.appDeployments.splice(depIndex, 1);
         this.removeDirtyAppDeploymentComputeHostId(appDeployment);
@@ -378,22 +374,22 @@ export default {
     },
     replaceAppDeployment(appDeployment) {
       const depIndex = this.appDeployments.findIndex(
-        (dep) => dep.computeHostId === appDeployment.computeHostId
+        (dep) => dep.compute_host_id === appDeployment.compute_host_id
       );
       this.appDeployments.splice(depIndex, 1, appDeployment);
     },
     setApplicationDeploymentDirty(appDeployment) {
       if (
         !this.dirtyAppDeploymentComputeHostIds.includes(
-          appDeployment.computeHostId
+          appDeployment.compute_host_id
         )
       ) {
-        this.dirtyAppDeploymentComputeHostIds.push(appDeployment.computeHostId);
+        this.dirtyAppDeploymentComputeHostIds.push(appDeployment.compute_host_id);
       }
     },
     removeDirtyAppDeploymentComputeHostId(appDeployment) {
       const hostIdIndex = this.dirtyAppDeploymentComputeHostIds.indexOf(
-        appDeployment.computeHostId
+        appDeployment.compute_host_id
       );
       if (hostIdIndex >= 0) {
         this.dirtyAppDeploymentComputeHostIds.splice(hostIdIndex, 1);
@@ -407,22 +403,22 @@ export default {
     },
     loadApplicationDeploymentSharedEntity(appDeployment) {
       return services.SharedEntityService.retrieve({
-        lookup: appDeployment.appDeploymentId,
+        lookup: appDeployment.app_deployment_id,
       }).then((sharedEntity) => {
         this.appDeploymentsSharedEntities[
-          appDeployment.computeHostId
+          appDeployment.compute_host_id
         ] = sharedEntity;
         this.removeAppDeploymentSharedEntityDirty(sharedEntity, appDeployment);
         return sharedEntity;
       });
     },
     setCurrentApplicationDeploymentSharedEntity(appDeployment) {
-      if (appDeployment.computeHostId in this.appDeploymentsSharedEntities) {
+      if (appDeployment.compute_host_id in this.appDeploymentsSharedEntities) {
         this.currentDeploymentSharedEntity = this.appDeploymentsSharedEntities[
-          appDeployment.computeHostId
+          appDeployment.compute_host_id
         ];
         return Promise.resolve(this.currentDeploymentSharedEntity);
-      } else if (appDeployment.appDeploymentId) {
+      } else if (appDeployment.app_deployment_id) {
         return this.loadApplicationDeploymentSharedEntity(appDeployment).then(
           (sharedEntity) => (this.currentDeploymentSharedEntity = sharedEntity)
         );
@@ -453,7 +449,7 @@ export default {
     mergeSharedEntity(sharedEntity, appDeployment) {
       return services.SharedEntityService.merge({
         data: sharedEntity,
-        lookup: appDeployment.appDeploymentId,
+        lookup: appDeployment.app_deployment_id,
       }).then((sharedEntity) => {
         this.replaceAppDeploymentSharedEntity(sharedEntity, appDeployment);
         this.removeAppDeploymentSharedEntityDirty(sharedEntity, appDeployment);
@@ -463,7 +459,7 @@ export default {
     updateSharedEntity(sharedEntity, appDeployment) {
       return services.SharedEntityService.update({
         data: sharedEntity,
-        lookup: appDeployment.appDeploymentId,
+        lookup: appDeployment.app_deployment_id,
       }).then((sharedEntity) => {
         this.replaceAppDeploymentSharedEntity(sharedEntity, appDeployment);
         this.removeAppDeploymentSharedEntityDirty(sharedEntity, appDeployment);
@@ -471,24 +467,24 @@ export default {
       });
     },
     saveSharedEntity(sharedEntity, appDeployment) {
-      return sharedEntity.entityId
+      return sharedEntity.entity_id
         ? this.updateSharedEntity(sharedEntity, appDeployment)
         : this.mergeSharedEntity(sharedEntity, appDeployment);
     },
     setApplicationDeploymentSharedEntityDirty(sharedEntity, appDeployment) {
       if (
         !this.dirtyAppDeploymentSharedEntityComputeHostIds.includes(
-          appDeployment.computeHostId
+          appDeployment.compute_host_id
         )
       ) {
         this.dirtyAppDeploymentSharedEntityComputeHostIds.push(
-          appDeployment.computeHostId
+          appDeployment.compute_host_id
         );
       }
     },
     removeAppDeploymentSharedEntityDirty(sharedEntity, appDeployment) {
       const hostIdIndex = this.dirtyAppDeploymentSharedEntityComputeHostIds.indexOf(
-        appDeployment.computeHostId
+        appDeployment.compute_host_id
       );
       if (hostIdIndex >= 0) {
         this.dirtyAppDeploymentSharedEntityComputeHostIds.splice(
@@ -499,12 +495,12 @@ export default {
     },
     replaceAppDeploymentSharedEntity(sharedEntity, appDeployment) {
       this.appDeploymentsSharedEntities[
-        appDeployment.computeHostId
+        appDeployment.compute_host_id
       ] = sharedEntity;
     },
     setCurrentDeploymentFromAppDeploymentId(appDeploymentId) {
       this.currentDeployment = this.appDeployments.find(
-        (dep) => dep.appDeploymentId === appDeploymentId
+        (dep) => dep.app_deployment_id === appDeploymentId
       );
       if (!this.currentDeployment) {
         throw new Error(
@@ -515,15 +511,13 @@ export default {
     },
     setCurrentDeploymentFromComputeHostId(computeHostId) {
       this.currentDeployment = this.appDeployments.find(
-        (dep) => dep.computeHostId === computeHostId
+        (dep) => dep.compute_host_id === computeHostId
       );
       if (!this.currentDeployment) {
         // Create a new deployment
-        const deployment = new models.ApplicationDeploymentDescription({
-          userHasWriteAccess: true,
-        });
-        deployment.appModuleId = this.id;
-        deployment.computeHostId = computeHostId;
+        const deployment = new models.ApplicationDeploymentDescription({ user_has_write_access: true });
+        deployment.app_module_id = this.id;
+        deployment.compute_host_id = computeHostId;
         this.currentDeployment = deployment;
         this.appDeployments.push(deployment);
         this.setApplicationDeploymentDirty(deployment);
@@ -561,23 +555,23 @@ export default {
           return Promise.all(
             this.dirtyAppDeploymentComputeHostIds.map((computeHostId) => {
               const deployment = this.appDeployments.find(
-                (dep) => dep.computeHostId === computeHostId
+                (dep) => dep.compute_host_id === computeHostId
               );
               return this.saveApplicationDeployment(deployment).catch(
                 (error) => {
                   // Navigate to the route that has the error
-                  if (deployment.appDeploymentId) {
+                  if (deployment.app_deployment_id) {
                     this.$router.push({
                       name: "application_deployment",
                       params: {
                         id: this.id,
-                        deploymentId: deployment.appDeploymentId,
+                        deploymentId: deployment.app_deployment_id,
                       },
                     });
                   } else {
                     this.$router.push({
                       name: "new_application_deployment",
-                      params: { id: this.id, hostId: deployment.computeHostId },
+                      params: { id: this.id, hostId: deployment.compute_host_id },
                     });
                   }
                   return Promise.reject(error);
@@ -594,17 +588,17 @@ export default {
                   computeHostId
                 ];
                 const deployment = this.appDeployments.find(
-                  (dep) => dep.computeHostId === computeHostId
+                  (dep) => dep.compute_host_id === computeHostId
                 );
                 return this.saveSharedEntity(sharedEntity, deployment).catch(
                   (error) => {
                     // Navigate to the route that has the error
-                    if (deployment.appDeploymentId) {
+                    if (deployment.app_deployment_id) {
                       this.$router.push({
                         name: "application_deployment",
                         params: {
                           id: this.id,
-                          deploymentId: deployment.appDeploymentId,
+                          deploymentId: deployment.app_deployment_id,
                         },
                       });
                     } else {
@@ -612,7 +606,7 @@ export default {
                         name: "new_application_deployment",
                         params: {
                           id: this.id,
-                          hostId: deployment.computeHostId,
+                          hostId: deployment.compute_host_id,
                         },
                       });
                     }
@@ -631,19 +625,19 @@ export default {
               duration: 5,
             })
           );
-          if (!this.id && this.appModule.appModuleId) {
+          if (!this.id && this.appModule.app_module_id) {
             // if we just create a new module, navigate to app module route now
             // that we have an id
             this.$router.push({
               name: "application_module",
-              params: { id: this.appModule.appModuleId },
+              params: { id: this.appModule.app_module_id },
             });
           }
           if (this.hostId) {
             // If creating a new deployment, navigate to the deployments list
             this.$router.push({
               name: "application_deployments",
-              params: { id: this.appModule.appModuleId },
+              params: { id: this.appModule.app_module_id },
             });
           } else {
             // Reinitialize deployment editing so that deployment being edited is
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
index e87e4f1b4..72d5c6bbe 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue
@@ -54,7 +54,7 @@
         <b-form-input
           :id="id + '-override-filename'"
           type="text"
-          v-model="data.overrideFilename"
+          v-model="data.override_filename"
           :disabled="readonly"
         ></b-form-input>
       </b-form-group>
@@ -62,7 +62,7 @@
         <b-form-input
           :id="id + '-argument'"
           type="text"
-          v-model="data.applicationArgument"
+          v-model="data.application_argument"
           :disabled="readonly"
         ></b-form-input>
       </b-form-group>
@@ -74,7 +74,7 @@
       >
         <b-form-radio-group
           :id="id + '-required-command-line'"
-          v-model="data.requiredToAddedToCommandLine"
+          v-model="data.required_to_added_to_command_line"
           :options="trueFalseOptions"
           :disabled="readonly"
         >
@@ -88,7 +88,7 @@
         >
           <b-form-radio-group
             :id="id + '-required'"
-            v-model="data.isRequired"
+            v-model="data.is_required"
             :options="trueFalseOptions"
             :disabled="readonly"
           >
@@ -101,7 +101,7 @@
         >
           <b-form-radio-group
             :id="id + '-read-only'"
-            v-model="data.isReadOnly"
+            v-model="data.is_read_only"
             :options="trueFalseOptions"
             :disabled="readonly"
           >
@@ -114,7 +114,7 @@
       >
         <b-form-textarea
           :id="id + '-user-friendly-description'"
-          v-model="data.userFriendlyDescription"
+          v-model="data.user_friendly_description"
           :rows="3"
           :disabled="readonly"
         />
@@ -126,7 +126,7 @@
       >
         <json-editor
           :id="id + '-metadata'"
-          v-model="data.metaData"
+          v-model="metadata"
           :rows="5"
           :disabled="readonly"
         />
@@ -186,6 +186,20 @@ export default {
     showOverrideFilenameField() {
       return this.data.type === models.DataType.URI;
     },
+    // meta_data is a raw JSON string on the wire; JSONEditor works with objects.
+    metadata: {
+      get() {
+        if (!this.data.meta_data) {
+          return null;
+        }
+        return typeof this.data.meta_data === "string"
+          ? JSON.parse(this.data.meta_data)
+          : this.data.meta_data;
+      },
+      set(value) {
+        this.data.meta_data = value ? JSON.stringify(value) : null;
+      },
+    },
   },
   methods: {
     doFocus() {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue
index 3114370fa..caf3a2f25 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue
@@ -13,7 +13,7 @@
         >
           <b-form-radio-group
             id="archive-directory"
-            v-model="data.archiveWorkingDirectory"
+            v-model="data.archive_working_directory"
             :options="trueFalseOptions"
             :disabled="readonly"
           >
@@ -27,7 +27,7 @@
         >
           <b-form-radio-group
             id="show-queue-settings"
-            v-model="data.showQueueSettings"
+            v-model="data.show_queue_settings"
             :options="trueFalseOptions"
             :disabled="readonly"
           >
@@ -42,7 +42,7 @@
           description="Select function to automatically compute queue settings."
         >
           <b-form-select
-            v-model="data.queueSettingsCalculatorId"
+            v-model="data.queue_settings_calculator_id"
             :options="queueSettingsCalculatorOptions"
             :disabled="queueSettingsCalculatorOptions.length === 0"
           >
@@ -63,15 +63,15 @@
         <b-form-textarea
           id="application-description"
           :rows="5"
-          v-model="data.applicationDescription"
+          v-model="data.application_description"
           :state="
-            !data.applicationDescription ||
-            data.applicationDescription.length < 500
+            !data.application_description ||
+            data.application_description.length < 500
           "
         >
         </b-form-textarea>
-        <b-form-valid-feedback v-if="!!data.applicationDescription">
-          {{ data.applicationDescription.length }} / 500
+        <b-form-valid-feedback v-if="!!data.application_description">
+          {{ data.application_description.length }} / 500
         </b-form-valid-feedback>
         <b-form-invalid-feedback>
           Application instructions text is limited to 500 characters maximum.
@@ -82,13 +82,13 @@
       <div class="col">
         <h1 class="h5 mb-4">Input Fields</h1>
         <draggable
-          v-model="data.applicationInputs"
+          v-model="data.application_inputs"
           :options="dragOptions"
           @start="onDragStart"
           @end="onDragEnd"
         >
           <application-input-field-editor
-            v-for="input in data.applicationInputs"
+            v-for="input in data.application_inputs"
             :value="input"
             :key="input.key"
             :focus="input.key === focusApplicationInputKey"
@@ -115,7 +115,7 @@
       <div class="col">
         <h1 class="h5 mb-4">Output Fields</h1>
         <application-output-field-editor
-          v-for="output in data.applicationOutputs"
+          v-for="output in data.application_outputs"
           :value="output"
           :key="output.key"
           :focus="output.key === focusApplicationOutputKey"
@@ -206,38 +206,38 @@ export default {
       this.$emit("cancel");
     },
     updatedInput(newValue) {
-      const input = this.data.applicationInputs.find(
+      const input = this.data.application_inputs.find(
         (input) => input.key === newValue.key
       );
       Object.assign(input, newValue);
     },
     addApplicationInput() {
       const appInput = new models.InputDataObjectType();
-      this.data.applicationInputs.push(appInput);
+      this.data.application_inputs.push(appInput);
       this.focusApplicationInputKey = appInput.key;
     },
     deleteInput(input) {
-      const inputIndex = this.data.applicationInputs.findIndex(
+      const inputIndex = this.data.application_inputs.findIndex(
         (inp) => inp.key === input.key
       );
-      this.data.applicationInputs.splice(inputIndex, 1);
+      this.data.application_inputs.splice(inputIndex, 1);
     },
     updatedOutput(newValue) {
-      const output = this.data.applicationOutputs.find(
+      const output = this.data.application_outputs.find(
         (o) => o.key === newValue.key
       );
       Object.assign(output, newValue);
     },
     addApplicationOutput() {
       const newOutput = new models.OutputDataObjectType();
-      this.data.applicationOutputs.push(newOutput);
+      this.data.application_outputs.push(newOutput);
       this.focusApplicationOutputKey = newOutput.key;
     },
     deleteOutput(output) {
-      const outputIndex = this.data.applicationOutputs.findIndex(
+      const outputIndex = this.data.application_outputs.findIndex(
         (o) => o.key === output.key
       );
-      this.data.applicationOutputs.splice(outputIndex, 1);
+      this.data.application_outputs.splice(outputIndex, 1);
     },
     onDragStart() {
       this.collapseApplicationInputs = true;
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
index 6660c2e71..15298587c 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
@@ -6,16 +6,16 @@
         <b-form-group
           label="Application Name"
           label-for="application-name"
-          :invalid-feedback="validationFeedback.appModuleName.invalidFeedback"
-          :state="validationFeedback.appModuleName.state"
+          :invalid-feedback="validationFeedback.app_module_name.invalidFeedback"
+          :state="validationFeedback.app_module_name.state"
         >
           <b-form-input
             id="application-name"
             type="text"
-            v-model="data.appModuleName"
+            v-model="data.app_module_name"
             required
             :disabled="readonly"
-            :state="validationFeedback.appModuleName.state"
+            :state="validationFeedback.app_module_name.state"
           ></b-form-input>
         </b-form-group>
         <b-form-group
@@ -25,7 +25,7 @@
           <b-form-input
             id="application-version"
             type="text"
-            v-model="data.appModuleVersion"
+            v-model="data.app_module_version"
             :disabled="readonly"
           ></b-form-input>
         </b-form-group>
@@ -35,7 +35,7 @@
         >
           <b-form-textarea
             id="application-description"
-            v-model="data.appModuleDescription"
+            v-model="data.app_module_description"
             :rows="3"
             :disabled="readonly"
           ></b-form-textarea>
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationOutputFieldEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationOutputFieldEditor.vue
index d17d98a00..543bd8a52 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationOutputFieldEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationOutputFieldEditor.vue
@@ -41,7 +41,7 @@
       <b-form-input
         :id="id + '-argument'"
         type="text"
-        v-model="data.applicationArgument"
+        v-model="data.application_argument"
         :disabled="readonly"
       ></b-form-input>
     </b-form-group>
@@ -53,7 +53,7 @@
       >
         <b-form-radio-group
           :id="id + '-required'"
-          v-model="data.isRequired"
+          v-model="data.is_required"
           :options="trueFalseOptions"
           :disabled="readonly"
         >
@@ -66,7 +66,7 @@
       >
         <b-form-radio-group
           :id="id + '-required-command-line'"
-          v-model="data.requiredToAddedToCommandLine"
+          v-model="data.required_to_added_to_command_line"
           :options="trueFalseOptions"
           :disabled="readonly"
         >
@@ -80,7 +80,7 @@
     >
       <json-editor
         :id="id + '-metadata'"
-        v-model="data.metaData"
+        v-model="metadata"
         :rows="5"
         :disabled="readonly"
       />
@@ -129,6 +129,20 @@ export default {
     id() {
       return "id-" + this.data.key;
     },
+    // meta_data is a raw JSON string on the wire; JSONEditor works with objects.
+    metadata: {
+      get() {
+        if (!this.data.meta_data) {
+          return null;
+        }
+        return typeof this.data.meta_data === "string"
+          ? JSON.parse(this.data.meta_data)
+          : this.data.meta_data;
+      },
+      set(value) {
+        this.data.meta_data = value ? JSON.stringify(value) : null;
+      },
+    },
   },
   methods: {
     doFocus() {
@@ -139,10 +153,9 @@ export default {
       this.$emit("delete");
     },
     setPlainText() {
-      const metadata = this.data.metaData || {};
+      const metadata = this.metadata || {};
       metadata["file-metadata"] = { "mime-type": "text/plain" };
-      // Clone so that JSONEditor updates with new value
-      this.data.metaData = JSON.parse(JSON.stringify(metadata));
+      this.metadata = metadata;
     },
   },
   mounted() {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/credentials/SSHCredentialSelector.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/credentials/SSHCredentialSelector.vue
index 39411fc8e..9ccfb1617 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/credentials/SSHCredentialSelector.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/credentials/SSHCredentialSelector.vue
@@ -111,9 +111,9 @@ export default {
     },
     copySSHPublicKeyText() {
       return this.selectedCredential
-        ? this.selectedCredential.publicKey.trim()
+        ? this.selectedCredential.public_key.trim()
         : this.defaultCredentialSummary
-        ? this.defaultCredentialSummary.publicKey.trim()
+        ? this.defaultCredentialSummary.public_key.trim()
         : null;
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ApplicationsDashboard.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ApplicationsDashboard.vue
index 5962b7f1e..7044a8bd6 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ApplicationsDashboard.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ApplicationsDashboard.vue
@@ -12,7 +12,7 @@
         <application-card
           v-for="item in slotProps.items"
           v-bind:app-module="item"
-          v-bind:key="item.appModuleId"
+          v-bind:key="item.app_module_id"
           v-on:app-selected="clickHandler(item)"
         >
         </application-card>
@@ -43,7 +43,7 @@ export default {
       if (this.appModules) {
         return utils.StringUtils.sortIgnoreCase(
           this.appModules.slice(),
-          (a) => a.appModuleName
+          (a) => a.app_module_name
         );
       } else {
         return [];
@@ -57,7 +57,7 @@ export default {
     clickHandler(item) {
       this.$router.push({
         name: "application_module",
-        params: { id: item.appModuleId },
+        params: { id: item.app_module_id },
       });
     },
     newApplicationHandler() {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
index f7d565490..6469c1a0c 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/ComputeResourcePreferenceDashboard.vue
@@ -7,18 +7,18 @@
   >
     <template slot="item-list" slot-scope="slotProps">
       <b-table striped hover :fields="fields" :items="slotProps.items">
-        <template slot="cell(updatedTime)" slot-scope="data">
+        <template slot="cell(updated_time)" slot-scope="data">
           <human-date :date="data.value" />
         </template>
         <template slot="cell(action)" slot-scope="data">
           <router-link
             class="action-link"
-            v-if="data.item.userHasWriteAccess"
+            v-if="data.item.user_has_write_access"
             :to="{
               name: 'group_resource_preference',
               params: {
                 value: data.item,
-                id: data.item.groupResourceProfileId,
+                id: data.item.group_resource_profile_id,
               },
             }"
           >
@@ -27,12 +27,12 @@
           </router-link>
           <router-link
             class="action-link"
-            v-if="!data.item.userHasWriteAccess"
+            v-if="!data.item.user_has_write_access"
             :to="{
               name: 'group_resource_preference',
               params: {
                 value: data.item,
-                id: data.item.groupResourceProfileId,
+                id: data.item.group_resource_profile_id,
               },
             }"
           >
@@ -40,12 +40,12 @@
             <i class="fa fa-eye" aria-hidden="true"></i>
           </router-link>
           <delete-link
-            v-if="data.item.userHasWriteAccess"
+            v-if="data.item.user_has_write_access"
             class="action-link"
             @delete="removeGroupResourceProfile(data.item)"
           >
             Are you sure you want to delete Group Resource Profile
-            <strong>{{ data.item.groupResourceProfileName }}</strong
+            <strong>{{ data.item.group_resource_profile_name }}</strong
             >?
           </delete-link>
         </template>
@@ -71,11 +71,11 @@ export default {
       fields: [
         {
           label: "Name",
-          key: "groupResourceProfileName",
+          key: "group_resource_profile_name",
         },
         {
           label: "Updated",
-          key: "updatedTime",
+          key: "updated_time",
         },
         {
           label: "Action",
@@ -99,7 +99,7 @@ export default {
     },
     removeGroupResourceProfile: function (groupResourceProfile) {
       services.GroupResourceProfileService.delete({
-        lookup: groupResourceProfile.groupResourceProfileId,
+        lookup: groupResourceProfile.group_resource_profile_id,
       })
         .then(() => services.GroupResourceProfileService.list())
         .then(
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/CredentialStoreDashboard.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/CredentialStoreDashboard.vue
index 3505a7661..6b0958e17 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/CredentialStoreDashboard.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/dashboards/CredentialStoreDashboard.vue
@@ -24,16 +24,16 @@
               :auto-add-admin-groups="false"
             />
           </template>
-          <template slot="cell(persistedTime)" slot-scope="data">
+          <template slot="cell(persisted_time)" slot-scope="data">
             <human-date :date="data.value" />
           </template>
           <template slot="cell(action)" slot-scope="data">
             <clipboard-copy-link
-              :text="data.item.publicKey.trim()"
+              :text="data.item.public_key.trim()"
               class="mr-1"
             />
             <delete-link
-              v-if="data.item.userHasWriteAccess"
+              v-if="data.item.user_has_write_access"
               @delete="deleteSSHCredential(data.item)"
             >
               Are you sure you want to delete the
@@ -61,7 +61,7 @@
             <share-button :entity-id="data.item.token" :disallow-editing-admin-groups="false" :auto-add-admin-groups="false"/>
           </template>
           <template slot="cell(action)" slot-scope="data">
-            <delete-link v-if="data.item.userHasWriteAccess" @delete="deletePasswordCredential(data.item)">
+            <delete-link v-if="data.item.user_has_write_access" @delete="deletePasswordCredential(data.item)">
               Are you sure you want to delete the
               <strong>{{ data.item.description }}</strong> password credential?
             </delete-link>
@@ -116,7 +116,7 @@ export default {
         },
         {
           label: "Created",
-          key: "persistedTime",
+          key: "persisted_time",
         },
         {
           label: "Sharing",
@@ -169,7 +169,7 @@ export default {
     showNewSharedSSHCredentialModel(){
       if (!this.adminsGroup){
         services.GroupService.list({limit: -1}).then((groups) => {
-             this.adminsGroup = groups.filter((g) => g.isGatewayAdminsGroup)[0];
+             this.adminsGroup = groups.filter((g) => g.is_gateway_admins_group)[0];
              this.$refs.newSharedSSHCredentialModal.show();
           });
       }
@@ -186,7 +186,7 @@ export default {
             lookup: session.Session.username,
           }).then((userProfile) => {
             sharedEntity.owner =  userProfile;
-            sharedEntity.isOwner = session.Session.username == sharedEntity.owner.userId;
+            sharedEntity.is_owner = session.Session.username == sharedEntity.owner.user_id;
             sharedEntity.addGroup({
               group: this.adminsGroup,
               permissionType: models.ResourcePermissionType.MANAGE_SHARING,
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditor.vue
index aa5378f4a..6cb47c7fd 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditor.vue
@@ -2,7 +2,7 @@
   <div>
     <div class="row">
       <div class="col">
-        <h1 class="h4 mb-4">Gateway Resource Profile - {{ data.gatewayID }}</h1>
+        <h1 class="h4 mb-4">Gateway Resource Profile - {{ data.gateway_id }}</h1>
         <b-form-group
           label="Default SSH Credential"
           label-for="default-credential-store-token"
@@ -10,8 +10,8 @@
         >
           <ssh-credential-selector
             id="default-credential-store-token"
-            v-model="data.credentialStoreToken"
-            :readonly="!data.userHasWriteAccess"
+            v-model="data.credential_store_token"
+            :readonly="!data.user_has_write_access"
           />
         </b-form-group>
       </div>
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditorContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditorContainer.vue
index e1d247d39..a858b5d00 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditorContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/GatewayResourceProfileEditorContainer.vue
@@ -18,14 +18,14 @@
           <div class="card-body">
             <storage-preference-list
               v-if="gatewayResourceProfile"
-              :storagePreferences="gatewayResourceProfile.storagePreferences"
+              :storagePreferences="gatewayResourceProfile.storage_preferences"
               :default-credential-store-token="
-                gatewayResourceProfile.credentialStoreToken
+                gatewayResourceProfile.credential_store_token
               "
               @updated="updatedStoragePreference"
               @added="addedStoragePreference"
               @delete="deleteStoragePreference"
-              :readonly="!gatewayResourceProfile.userHasWriteAccess"
+              :readonly="!gatewayResourceProfile.user_has_write_access"
             />
           </div>
         </div>
@@ -33,7 +33,7 @@
     </div>
     <div
       class="row"
-      v-if="gatewayResourceProfile && gatewayResourceProfile.userHasWriteAccess"
+      v-if="gatewayResourceProfile && gatewayResourceProfile.user_has_write_access"
     >
       <div class="col">
         <b-button variant="primary" @click="save"> Save </b-button>
@@ -79,11 +79,11 @@ export default {
       this.gatewayResourceProfile = this.gatewayResourceProfileClone.clone();
     },
     updatedStoragePreference(updatedStoragePreference) {
-      const index = this.gatewayResourceProfile.storagePreferences.findIndex(
+      const index = this.gatewayResourceProfile.storage_preferences.findIndex(
         (sp) =>
-          sp.storageResourceId === updatedStoragePreference.storageResourceId
+          sp.storage_resource_id === updatedStoragePreference.storage_resource_id
       );
-      this.gatewayResourceProfile.storagePreferences.splice(
+      this.gatewayResourceProfile.storage_preferences.splice(
         index,
         1,
         updatedStoragePreference
@@ -93,17 +93,17 @@ export default {
       services.StoragePreferenceService.create({
         data: newStoragePreference,
       }).then((sp) => {
-        this.gatewayResourceProfile.storagePreferences.push(sp);
+        this.gatewayResourceProfile.storage_preferences.push(sp);
       });
     },
     deleteStoragePreference(storageResourceId) {
       services.StoragePreferenceService.delete({
         lookup: storageResourceId,
       }).then(() => {
-        const index = this.gatewayResourceProfile.storagePreferences.findIndex(
-          (sp) => sp.storageResourceId === storageResourceId
+        const index = this.gatewayResourceProfile.storage_preferences.findIndex(
+          (sp) => sp.storage_resource_id === storageResourceId
         );
-        this.gatewayResourceProfile.storagePreferences.splice(index, 1);
+        this.gatewayResourceProfile.storage_preferences.splice(index, 1);
       });
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceEditor.vue
index 70d752521..c61dad7ed 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceEditor.vue
@@ -3,7 +3,7 @@
     <b-form-group label="Login username" label-for="login-username">
       <b-form-input
         id="login-username"
-        v-model="data.loginUserName"
+        v-model="data.login_user_name"
         type="text"
       />
     </b-form-group>
@@ -13,7 +13,7 @@
     >
       <b-form-input
         id="filesystem-root-location"
-        v-model="data.fileSystemRootLocation"
+        v-model="data.file_system_root_location"
         type="text"
       />
     </b-form-group>
@@ -24,7 +24,7 @@
     >
       <ssh-credential-selector
         id="default-credential-store-token"
-        v-model="data.resourceSpecificCredentialStoreToken"
+        v-model="data.resource_specific_credential_store_token"
         :null-option-default-credential-token="defaultCredentialStoreToken"
         :null-option-disabled="!defaultCredentialStoreToken"
       >
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceList.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceList.vue
index 9a2def8fd..6789534cb 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceList.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/gatewayprofile/StoragePreferenceList.vue
@@ -11,7 +11,7 @@
         <b-form-group label="Storage Resource" label-for="storage-resource">
           <b-form-select
             id="storage-resource"
-            v-model="newStoragePreference.storageResourceId"
+            v-model="newStoragePreference.storage_resource_id"
             :options="storageResourceOptions"
           />
         </b-form-group>
@@ -37,17 +37,17 @@
         hover
         :fields="fields"
         :items="slotProps.items"
-        sort-by="storageResourceId"
+        sort-by="storage_resource_id"
       >
         <template
-          slot="cell(resourceSpecificCredentialStoreToken)"
+          slot="cell(resource_specific_credential_store_token)"
           slot-scope="data"
         >
           {{ data.value }}
           <b-badge
             v-if="
               defaultCredentialStoreToken &&
-              !data.item.resourceSpecificCredentialStoreToken
+              !data.item.resource_specific_credential_store_token
             "
           >
             Default
@@ -65,11 +65,11 @@
           <delete-link
             v-if="!readonly"
             class="action-link"
-            @delete="deleteStoragePreference(data.item.storageResourceId)"
+            @delete="deleteStoragePreference(data.item.storage_resource_id)"
           >
             Are you sure you want to delete the storage preference for
             <strong>{{
-              getStorageResourceName(data.item.storageResourceId)
+              getStorageResourceName(data.item.storage_resource_id)
             }}</strong
             >?
           </delete-link>
@@ -128,22 +128,22 @@ export default {
       return [
         {
           label: "Name",
-          key: "storageResourceId",
+          key: "storage_resource_id",
           sortable: true,
           formatter: (value) => this.getStorageResourceName(value),
         },
         {
           label: "Username",
-          key: "loginUserName",
+          key: "login_user_name",
         },
         {
           label: "SSH Credential",
-          key: "resourceSpecificCredentialStoreToken",
+          key: "resource_specific_credential_store_token",
           formatter: (value) => this.getCredentialName(value),
         },
         {
           label: "File System Location",
-          key: "fileSystemRootLocation",
+          key: "file_system_root_location",
         },
         {
           label: "Action",
@@ -154,12 +154,12 @@ export default {
     decoratedStoragePreferences() {
       return this.storagePreferences.map((sp) => {
         const spClone = sp.clone();
-        spClone._showDetails = this.showingDetails[spClone.storageResourceId];
+        spClone._showDetails = this.showingDetails[spClone.storage_resource_id];
         return spClone;
       });
     },
     currentStoragePreferenceIds() {
-      return this.storagePreferences.map((sp) => sp.storageResourceId);
+      return this.storagePreferences.map((sp) => sp.storage_resource_id);
     },
     storageResourceOptions() {
       const options = [];
@@ -225,8 +225,8 @@ export default {
     },
     toggleDetails(row) {
       row.toggleDetails();
-      this.showingDetails[row.item.storageResourceId] = !this.showingDetails[
-        row.item.storageResourceId
+      this.showingDetails[row.item.storage_resource_id] = !this.showingDetails[
+        row.item.storage_resource_id
       ];
     },
     deleteStoragePreference(storageResourceId) {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticeEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticeEditor.vue
index 9daedcad7..df017662e 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticeEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticeEditor.vue
@@ -29,7 +29,7 @@
         <b-form-textarea
           id="notice-message"
           type="text"
-          v-model="data.notificationMessage"
+          v-model="data.notification_message"
           required
           placeholder="Notice Message"
           :state="getValidationState('notificationMessage')"
@@ -103,12 +103,12 @@
       <b-form-group
         label="Show In Dashboard"
         label-for="showInDashboard"
-        :state="getValidationState('showInDashboard')"
+        :state="getValidationState('show_in_dashboard')"
       >
         <b-form-checkbox
           id="showInDashboard"
-          v-model="data.showInDashboard"
-          :state="getValidationState('showInDashboard')"
+          v-model="data.show_in_dashboard"
+          :state="getValidationState('show_in_dashboard')"
         >
         </b-form-checkbox>
       </b-form-group>
@@ -158,21 +158,21 @@ export default {
   },
   created() {
     //checks whether the component is used for editing or updating the notificaion
-    if (this.value.notificationId != null) {
+    if (this.value.notification_id != null) {
       this.editNotification = true;
       this.inputPublishedTime = new moment(
-        this.value.publishedTime.toISOString()
+        this.value.published_time.toISOString()
       )
         .utc()
         .format();
       this.inputExpirationTime = new moment(
-        this.value.expirationTime.toISOString()
+        this.value.expiration_time.toISOString()
       )
         .utc()
         .format();
       this.data.priority = this.value.priority.name;
-      this.data.showInDashboard = this.value.showInDashboard;
-      this.today = new moment(this.value.expirationTime.toISOString()).format();
+      this.data.show_in_dashboard = this.value.show_in_dashboard;
+      this.today = new moment(this.value.expiration_time.toISOString()).format();
     }
   },
   data() {
@@ -227,10 +227,10 @@ export default {
   },
   watch: {
     inputExpirationTime() {
-      this.data.expirationTime = this.inputExpirationTime;
+      this.data.expiration_time = this.inputExpirationTime;
     },
     inputPublishedTime() {
-      this.data.publishedTime = this.inputPublishedTime;
+      this.data.published_time = this.inputPublishedTime;
     },
   },
 };
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticesManagementContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticesManagementContainer.vue
index 1f5aadb6f..43672dc64 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticesManagementContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/notices/NoticesManagementContainer.vue
@@ -31,20 +31,20 @@
               </template>
               <template slot="item-list">
                 <b-table hover :fields="fields" :items="items">
-                  <template slot="cell(publishedTime)" slot-scope="data">
+                  <template slot="cell(published_time)" slot-scope="data">
                     <human-date :date="data.value" /> </template
                   >row
-                  <template slot="cell(expirationTime)" slot-scope="data">
+                  <template slot="cell(expiration_time)" slot-scope="data">
                     <human-date :date="data.value" />
                   </template>
                   <template slot="cell(action)" slot-scope="data">
-                    <template v-if="data.item.userHasWriteAccess">
+                    <template v-if="data.item.user_has_write_access">
                       <b-link class="action-link" @click="toggleDetails(data)">
                         Edit
                         <i class="fa fa-edit" aria-hidden="true"></i>
                       </b-link>
                       <delete-link
-                        @delete="deleteNotice(data.item.notificationId)"
+                        @delete="deleteNotice(data.item.notification_id)"
                       >
                         Are you sure you want to delete the notice?
                       </delete-link>
@@ -121,15 +121,15 @@ export default {
         },
         {
           label: "Message",
-          key: "notificationMessage",
+          key: "notification_message",
         },
         {
           label: "Publish Date",
-          key: "publishedTime",
+          key: "published_time",
         },
         {
           label: "Expiry Date",
-          key: "expirationTime",
+          key: "expiration_time",
         },
         {
           label: "Priority",
@@ -137,7 +137,7 @@ export default {
         },
         {
           label: "Show In Dashboard",
-          key: "showInDashboard",
+          key: "show_in_dashboard",
         },
         {
           label: "Action",
@@ -165,10 +165,10 @@ export default {
       const validation = this.updatedNotice.validate();
       if (Object.keys(validation).length === 0) {
         const index = this.notices.findIndex(
-          (sp) => sp.notificationId === this.updatedNotice.notificationId
+          (sp) => sp.notification_id === this.updatedNotice.notification_id
         );
         services.ManageNotificationService.update({
-          lookup: this.updatedNotice.notificationId,
+          lookup: this.updatedNotice.notification_id,
           data: this.updatedNotice,
         }).then((sp) => {
           this.notices.splice(index, 1, sp);
@@ -187,7 +187,7 @@ export default {
         lookup: notificationId,
       }).then(() => {
         const index = this.notices.findIndex(
-          (sp) => sp.notificationId === notificationId
+          (sp) => sp.notification_id === notificationId
         );
         this.notices.splice(index, 1);
       });
@@ -196,8 +196,8 @@ export default {
       (this.updatedNotice = new models.Notification()),
         (this.updatedNotice = row.item);
       row.toggleDetails();
-      this.showingDetails[row.item.notificationId] = !this.showingDetails[
-        row.item.notificationId
+      this.showingDetails[row.item.notification_id] = !this.showingDetails[
+        row.item.notification_id
       ];
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue
index 1f8062014..80a908779 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentDetailsView.vue
@@ -5,12 +5,12 @@
         <tr>
           <th scope="row">Name</th>
           <td>
-            <div :title="experiment.experimentId">
-              {{ experiment.experimentName }}
+            <div :title="experiment.experiment_id">
+              {{ experiment.experiment_name }}
             </div>
             <small class="text-muted">
-              ID: {{ experiment.experimentId }} (<clipboard-copy-link
-                :text="experiment.experimentId"
+              ID: {{ experiment.experiment_id }} (<clipboard-copy-link
+                :text="experiment.experiment_id"
                 :link-classes="['text-reset']"
               >
                 copy
@@ -35,7 +35,7 @@
         </tr>
         <tr>
           <th scope="row">Owner</th>
-          <td>{{ experiment.userName }}</td>
+          <td>{{ experiment.user_name }}</td>
         </tr>
         <tr>
           <th scope="row">Application</th>
@@ -43,7 +43,8 @@
             {{ fullExperiment.applicationName }}
           </td>
           <td v-else class="font-italic text-muted">
-            Unable to load interface {{ fullExperiment.experiment.executionId }}
+            Unable to load interface
+            {{ fullExperiment.experiment.execution_id }}
           </td>
         </tr>
         <tr>
@@ -67,7 +68,7 @@
         </tr>
         <tr
           v-if="
-            fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0
+            fullExperiment.job_details && fullExperiment.job_details.length > 0
           "
         >
           <th scope="row">Job</th>
@@ -81,14 +82,14 @@
               </thead>
               <tbody>
                 <tr
-                  v-for="(jobDetail, index) in fullExperiment.jobDetails"
-                  :key="jobDetail.jobId"
+                  v-for="(jobDetail, index) in fullExperiment.job_details"
+                  :key="jobDetail.job_id"
                 >
-                  <td>{{ jobDetail.jobName }}</td>
-                  <td>{{ jobDetail.jobId }}</td>
+                  <td>{{ jobDetail.job_name }}</td>
+                  <td>{{ jobDetail.job_id }}</td>
                   <td>{{ jobDetail.jobStatusStateName }}</td>
                   <td>
-                    <span :title="jobDetail.creationTime.toString()">{{
+                    <span :title="jobDetail.creation_time.toString()">{{
                       jobCreationTimes[index]
                     }}</span>
                   </td>
@@ -101,47 +102,47 @@
           <th scope="row">Notification List</th>
           <td>
             {{
-              experiment.emailAddresses
-                ? experiment.emailAddresses.join(", ")
+              experiment.email_addresses
+                ? experiment.email_addresses.join(", ")
                 : ""
             }}
           </td>
         </tr>
         <tr
           v-if="
-            fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0
+            fullExperiment.job_details && fullExperiment.job_details.length > 0
           "
         >
           <th scope="row">Working Dir</th>
           <td>
             <div
-              v-for="jobDetail in fullExperiment.jobDetails"
-              :key="jobDetail.jobId"
+              v-for="jobDetail in fullExperiment.job_details"
+              :key="jobDetail.job_id"
             >
-              {{ jobDetail.jobName }}: {{ jobDetail.workingDir }}
+              {{ jobDetail.job_name }}: {{ jobDetail.working_dir }}
             </div>
           </td>
         </tr>
         <tr
           v-if="
-            fullExperiment.jobDetails && fullExperiment.jobDetails.length > 0
+            fullExperiment.job_details && fullExperiment.job_details.length > 0
           "
         >
           <th scope="row">Job Description</th>
           <td>
             <b-card
-              v-for="jobDetail in fullExperiment.jobDetails"
-              :key="jobDetail.jobId"
-              :header="jobDetail.jobName"
+              v-for="jobDetail in fullExperiment.job_details"
+              :key="jobDetail.job_id"
+              :header="jobDetail.job_name"
             >
-              <pre>{{ jobDetail.jobDescription }}</pre>
+              <pre>{{ jobDetail.job_description }}</pre>
             </b-card>
           </td>
         </tr>
         <tr>
           <th scope="row">Creation Time</th>
           <td>
-            <span :title="experiment.creationTime.toString()">{{
+            <span :title="experiment.creation_time.toString()">{{
               creationTime
             }}</span>
           </td>
@@ -151,7 +152,7 @@
           <td>
             <span
               :title="
-                fullExperiment.experimentStatus.timeOfStateChange.toString()
+                fullExperiment.experimentStatus.time_of_state_change.toString()
               "
               >{{ lastModifiedTime }}</span
             >
@@ -161,8 +162,8 @@
           <th scope="row">Wall Time Limit</th>
           <td>
             {{
-              experiment.userConfigurationData.computationalResourceScheduling
-                .wallTimeLimit
+              experiment.user_configuration_data.computational_resource_scheduling
+                .wall_time_limit
             }}
             minutes
           </td>
@@ -171,8 +172,8 @@
           <th scope="row">CPU Count</th>
           <td>
             {{
-              experiment.userConfigurationData.computationalResourceScheduling
-                .totalCPUCount
+              experiment.user_configuration_data.computational_resource_scheduling
+                .total_cpu_count
             }}
           </td>
         </tr>
@@ -180,21 +181,21 @@
           <th scope="row">Node Count</th>
           <td>
             {{
-              experiment.userConfigurationData.computationalResourceScheduling
-                .nodeCount
+              experiment.user_configuration_data.computational_resource_scheduling
+                .node_count
             }}
           </td>
         </tr>
         <tr
           v-if="
-            experiment.userConfigurationData.computationalResourceScheduling
-              .totalPhysicalMemory
+            experiment.user_configuration_data.computational_resource_scheduling
+              .total_physical_memory
           "
         >
           <th scope="row">Total Physical Memory</th>
           <td>
             {{
-              experiment.userConfigurationData.computationalResourceScheduling.totalPhysicalMemory.toLocaleString()
+              experiment.user_configuration_data.computational_resource_scheduling.total_physical_memory.toLocaleString()
             }}
             MB
           </td>
@@ -203,8 +204,8 @@
           <th scope="row">Queue</th>
           <td>
             {{
-              experiment.userConfigurationData.computationalResourceScheduling
-                .queueName
+              experiment.user_configuration_data.computational_resource_scheduling
+                .queue_name
             }}
           </td>
         </tr>
@@ -213,7 +214,7 @@
           <td>
             <ul>
               <li
-                v-for="input in experiment.experimentInputs"
+                v-for="input in experiment.experiment_inputs"
                 :key="input.name"
               >
                 {{ input.name }}:
@@ -225,7 +226,7 @@
                   v-else-if="input.type.isFileValueType"
                   :data-product="dp"
                   :input-file="true"
-                  :key="dp.productUri"
+                  :key="dp.product_uri"
                 />
               </li>
             </ul>
@@ -236,7 +237,7 @@
           <td>
             <ul>
               <li
-                v-for="output in experiment.experimentOutputs"
+                v-for="output in experiment.experiment_outputs"
                 :key="output.name"
               >
                 {{ output.name }}:
@@ -247,7 +248,7 @@
                   v-for="dp in outputDataProducts[output.name]"
                   v-else-if="output.type.isFileValueType"
                   :data-product="dp"
-                  :key="dp.productUri"
+                  :key="dp.product_uri"
                 />
               </li>
             </ul>
@@ -269,23 +270,23 @@
           <td>
             <b-card
               v-for="error in experiment.errors"
-              :key="error.errorId"
+              :key="error.error_id"
               header="Error"
             >
-              <p>{{ error.userFriendlyMessage }}</p>
-              <pre class="pre-scrollable">{{ error.actualErrorMessage }}</pre>
+              <p>{{ error.user_friendly_message }}</p>
+              <pre class="pre-scrollable">{{ error.actual_error_message }}</pre>
             </b-card>
           </td>
         </tr>
         <template v-if="failedJobs.length > 0">
-          <tr v-for="job in failedJobs" :key="job.jobId">
+          <tr v-for="job in failedJobs" :key="job.job_id">
             <th scope="row">Job Submission Response</th>
             <td>
-              <b-card v-if="job.stdOut" :header="job.jobName + ' STDOUT'">
-                <pre class="pre-scrollable">{{ job.stdOut }}</pre>
+              <b-card v-if="job.std_out" :header="job.job_name + ' STDOUT'">
+                <pre class="pre-scrollable">{{ job.std_out }}</pre>
               </b-card>
-              <b-card v-if="job.stdErr" :header="job.jobName + ' STDERR'">
-                <pre class="pre-scrollable">{{ job.stdErr }}</pre>
+              <b-card v-if="job.std_err" :header="job.job_name + ' STDERR'">
+                <pre class="pre-scrollable">{{ job.std_err }}</pre>
               </b-card>
             </td>
           </tr>
@@ -295,23 +296,23 @@
     <h2 class="h5 mb-3">Process Details</h2>
     <b-card
       v-for="process in experiment.processes"
-      :key="process.processId"
-      :header="process.processId"
+      :key="process.process_id"
+      :header="process.process_id"
     >
       <b-card
         v-for="task in process.sortedTasks"
-        :key="task.taskId"
-        :header="task.taskId"
+        :key="task.task_id"
+        :header="task.task_id"
       >
         <table class="table table-sm">
           <tbody>
             <tr>
               <th scope="row">Task Id</th>
-              <td>{{ task.taskId }}</td>
+              <td>{{ task.task_id }}</td>
             </tr>
             <tr>
               <th scope="row">Task Type</th>
-              <td>{{ task.taskType.name }}</td>
+              <td>{{ task.task_type.name }}</td>
             </tr>
             <tr>
               <th scope="row">Task Status</th>
@@ -320,25 +321,25 @@
             <tr>
               <th scope="row">Task Status Time</th>
               <td>
-                <human-date :date="task.latestStatus.timeOfStateChange" />
+                <human-date :date="task.latestStatus.time_of_state_change" />
               </td>
             </tr>
             <tr>
               <th scope="row">Task Status Reason</th>
               <td>{{ task.latestStatus.reason }}</td>
             </tr>
-            <template v-if="task.taskErrors && task.taskErrors.length > 0">
+            <template v-if="task.task_errors && task.task_errors.length > 0">
               <tr>
                 <th scope="row">Task Errors</th>
                 <td>
                   <b-card
-                    v-for="error in task.taskErrors"
-                    :key="error.errorId"
-                    :header="error.errorId"
+                    v-for="error in task.task_errors"
+                    :key="error.error_id"
+                    :header="error.error_id"
                   >
-                    <p>{{ error.userFriendlyMessage }}</p>
+                    <p>{{ error.user_friendly_message }}</p>
                     <pre class="pre-scrollable">{{
-                      error.actualErrorMessage
+                      error.actual_error_message
                     }}</pre>
                   </b-card>
                 </td>
@@ -350,10 +351,10 @@
                 <td>
                   <b-card
                     v-for="job in task.jobs"
-                    :key="job.jobId"
-                    :header="job.jobName"
+                    :key="job.job_id"
+                    :header="job.job_name"
                   >
-                    <pre>{{ job.jobDescription }}</pre>
+                    <pre>{{ job.job_description }}</pre>
                   </b-card>
                 </td>
               </tr>
@@ -363,12 +364,12 @@
       </b-card>
 
       <b-card
-        v-for="error in process.processErrors"
-        :key="error.errorId"
-        :header="'Process Error ' + error.errorId"
+        v-for="error in process.process_errors"
+        :key="error.error_id"
+        :header="'Process Error ' + error.error_id"
       >
-        <p>{{ error.userFriendlyMessage }}</p>
-        <pre class="pre-scrollable">{{ error.actualErrorMessage }}</pre>
+        <p>{{ error.user_friendly_message }}</p>
+        <pre class="pre-scrollable">{{ error.actual_error_message }}</pre>
       </b-card>
     </b-card>
   </div>
@@ -402,11 +403,11 @@ export default {
   computed: {
     inputDataProducts() {
       const result = {};
-      if (this.fullExperiment && this.fullExperiment.inputDataProducts) {
-        this.fullExperiment.experiment.experimentInputs.forEach((input) => {
+      if (this.fullExperiment && this.fullExperiment.input_data_products) {
+        this.fullExperiment.experiment.experiment_inputs.forEach((input) => {
           result[input.name] = this.getDataProducts(
             input,
-            this.fullExperiment.inputDataProducts
+            this.fullExperiment.input_data_products
           );
         });
       }
@@ -414,45 +415,45 @@ export default {
     },
     outputDataProducts() {
       const result = {};
-      if (this.fullExperiment && this.fullExperiment.outputDataProducts) {
-        this.fullExperiment.experiment.experimentOutputs.forEach((output) => {
+      if (this.fullExperiment && this.fullExperiment.output_data_products) {
+        this.fullExperiment.experiment.experiment_outputs.forEach((output) => {
           result[output.name] = this.getDataProducts(
             output,
-            this.fullExperiment.outputDataProducts
+            this.fullExperiment.output_data_products
           );
         });
       }
       return result;
     },
     creationTime: function () {
-      return moment(this.fullExperiment.experiment.creationTime).fromNow();
+      return moment(this.fullExperiment.experiment.creation_time).fromNow();
     },
     lastModifiedTime: function () {
       return moment(
-        this.fullExperiment.experimentStatus.timeOfStateChange
+        this.fullExperiment.experimentStatus.time_of_state_change
       ).fromNow();
     },
     jobCreationTimes: function () {
-      return this.fullExperiment.jobDetails.map((jobDetail) =>
-        moment(jobDetail.creationTime).fromNow()
+      return this.fullExperiment.job_details.map((jobDetail) =>
+        moment(jobDetail.creation_time).fromNow()
       );
     },
     failedJobs() {
-      if (this.fullExperiment && this.fullExperiment.jobDetails) {
-        return this.fullExperiment.jobDetails.filter(
+      if (this.fullExperiment && this.fullExperiment.job_details) {
+        return this.fullExperiment.job_details.filter(
           (job) =>
             this.experiment.latestStatus.state ===
               models.ExperimentState.FAILED ||
             (job.latestJobStatus &&
-              job.latestJobStatus.jobState === models.JobState.FAILED)
+              job.latestJobStatus.job_state === models.JobState.FAILED)
         );
       } else {
         return [];
       }
     },
     experimentDataDir() {
-      if (this.experiment && this.experiment.userConfigurationData) {
-        return this.experiment.userConfigurationData.experimentDataDir;
+      if (this.experiment && this.experiment.user_configuration_data) {
+        return this.experiment.user_configuration_data.experiment_data_dir;
       } else {
         return null;
       }
@@ -463,10 +464,10 @@ export default {
   },
   created() {
     services.FullExperimentService.retrieve({
-      lookup: this.experiment.experimentId,
+      lookup: this.experiment.experiment_id,
     }).then((fullExperiment) => (this.fullExperiment = fullExperiment));
     services.ExperimentArchiveService.get({
-      experimentId: this.experiment.experimentId,
+      experimentId: this.experiment.experiment_id,
     }).then((result) => {
       this.experimentArchive = result;
     });
@@ -480,12 +481,12 @@ export default {
       if (io.type === models.DataType.URI_COLLECTION) {
         const dataProductURIs = io.value.split(",");
         dataProducts = dataProductURIs.map((uri) =>
-          collection.find((dp) => dp.productUri === uri)
+          collection.find((dp) => dp.product_uri === uri)
         );
       } else {
         const dataProductURI = io.value;
         dataProducts = collection.filter(
-          (dp) => dp.productUri === dataProductURI
+          (dp) => dp.product_uri === dataProductURI
         );
       }
       return dataProducts
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
index db4628e8a..0d80a0044 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/statistics/ExperimentStatisticsContainer.vue
@@ -178,9 +178,9 @@
               <experiment-statistics-card
                 bg-variant="primary"
                 header-text-variant="white"
-                :count="experimentStatistics.allExperimentCount || 0"
+                :count="experimentStatistics.all_experiment_count || 0"
                 title="Total Experiments"
-                @click="selectExperiments('allExperiments')"
+                @click="selectExperiments('all_experiments')"
               >
                 <span slot="link-text">All</span>
               </experiment-statistics-card>
@@ -188,10 +188,10 @@
             <div class="col-xl-2 col-md-4">
               <experiment-statistics-card
                 bg-variant="light"
-                :count="experimentStatistics.createdExperimentCount || 0"
+                :count="experimentStatistics.created_experiment_count || 0"
                 :states="createdStates"
                 title="Created Experiments"
-                @click="selectExperiments('createdExperiments')"
+                @click="selectExperiments('created_experiments')"
               >
               </experiment-statistics-card>
             </div>
@@ -199,10 +199,10 @@
               <experiment-statistics-card
                 bg-variant="light"
                 header-text-variant="success"
-                :count="experimentStatistics.runningExperimentCount || 0"
+                :count="experimentStatistics.running_experiment_count || 0"
                 :states="runningStates"
                 title="Running Experiments"
-                @click="selectExperiments('runningExperiments')"
+                @click="selectExperiments('running_experiments')"
               >
               </experiment-statistics-card>
             </div>
@@ -211,10 +211,10 @@
                 bg-variant="success"
                 header-text-variant="white"
                 link-variant="success"
-                :count="experimentStatistics.completedExperimentCount || 0"
+                :count="experimentStatistics.completed_experiment_count || 0"
                 :states="completedStates"
                 title="Completed Experiments"
-                @click="selectExperiments('completedExperiments')"
+                @click="selectExperiments('completed_experiments')"
               >
               </experiment-statistics-card>
             </div>
@@ -223,10 +223,10 @@
                 bg-variant="warning"
                 header-text-variant="white"
                 link-variant="warning"
-                :count="experimentStatistics.cancelledExperimentCount || 0"
+                :count="experimentStatistics.cancelled_experiment_count || 0"
                 :states="canceledStates"
                 title="Cancelled Experiments"
-                @click="selectExperiments('cancelledExperiments')"
+                @click="selectExperiments('cancelled_experiments')"
               >
               </experiment-statistics-card>
             </div>
@@ -235,10 +235,10 @@
                 bg-variant="danger"
                 header-text-variant="white"
                 link-variant="danger"
-                :count="experimentStatistics.failedExperimentCount || 0"
+                :count="experimentStatistics.failed_experiment_count || 0"
                 :states="failedStates"
                 title="Failed Experiments"
-                @click="selectExperiments('failedExperiments')"
+                @click="selectExperiments('failed_experiments')"
               >
               </experiment-statistics-card>
             </div>
@@ -247,21 +247,21 @@
             <div class="col">
               <b-card>
                 <b-table :fields="fields" :items="items">
-                  <template slot="cell(executionId)" slot-scope="data">
+                  <template slot="cell(execution_id)" slot-scope="data">
                     <application-name :application-interface-id="data.value" />
                   </template>
-                  <template slot="cell(resourceHostId)" slot-scope="data">
+                  <template slot="cell(resource_host_id)" slot-scope="data">
                     <compute-resource-name :compute-resource-id="data.value" />
                   </template>
-                  <template slot="cell(creationTime)" slot-scope="data">
+                  <template slot="cell(creation_time)" slot-scope="data">
                     <human-date :date="data.value" />
                   </template>
-                  <template slot="cell(experimentStatus)" slot-scope="data">
+                  <template slot="cell(experiment_status)" slot-scope="data">
                     <experiment-status-badge :status-name="data.value.name" />
                   </template>
                   <template slot="cell(actions)" slot-scope="data">
                     <b-link
-                      @click="showExperimentDetails(data.item.experimentId)"
+                      @click="showExperimentDetails(data.item.experiment_id)"
                     >
                       View Details
                       <i class="far fa-chart-bar" aria-hidden="true"></i>
@@ -270,7 +270,7 @@
                 </b-table>
               </b-card>
               <pager
-                v-if="experimentStatistics.allExperimentCount > 0"
+                v-if="experimentStatistics.all_experiment_count > 0"
                 :paginator="experimentStatisticsPaginator"
                 @next="experimentStatisticsPaginator.next()"
                 @previous="experimentStatisticsPaginator.previous()"
@@ -280,13 +280,13 @@
         </b-tab>
         <b-tab
           v-for="experimentTab in experimentDetailTabs"
-          :key="experimentTab.experiment.experimentId"
+          :key="experimentTab.experiment.experiment_id"
         >
           <template slot="title">
             {{ experimentTab.tabTitle }}
             <b-link
               @click="
-                removeExperimentDetailTab(experimentTab.experiment.experimentId)
+                removeExperimentDetailTab(experimentTab.experiment.experiment_id)
               "
               class="text-secondary"
             >
@@ -393,23 +393,23 @@ export default {
           label: "Name",
         },
         {
-          key: "userName",
+          key: "user_name",
           label: "Owner",
         },
         {
-          key: "executionId",
+          key: "execution_id",
           label: "Application",
         },
         {
-          key: "resourceHostId",
+          key: "resource_host_id",
           label: "Resource",
         },
         {
-          key: "creationTime",
+          key: "creation_time",
           label: "Creation Time",
         },
         {
-          key: "experimentStatus",
+          key: "experiment_status",
           label: "Status",
         },
         {
@@ -446,8 +446,8 @@ export default {
       if (this.appInterfaces) {
         const options = this.appInterfaces.map((appInterface) => {
           return {
-            value: appInterface.applicationInterfaceId,
-            text: appInterface.applicationName,
+            value: appInterface.application_interface_id,
+            text: appInterface.application_name,
           };
         });
         return utils.StringUtils.sortIgnoreCase(options, (o) => o.text);
@@ -461,7 +461,7 @@ export default {
         // First create a Set of all compute resource ids in the GRPs
         const groupResourceProfileCompResources = new Set(
           this.groupResourceProfiles.flatMap((grp) =>
-            grp.computePreferences.map((cp) => cp.computeResourceId)
+            grp.compute_preferences.map((cp) => cp.compute_resource_id)
           )
         );
         const options = this.computeResourceNames
@@ -478,21 +478,21 @@ export default {
       }
     },
     selectedExperimentsTabTitle() {
-      if (this.selectedExperimentSummariesKey === "allExperiments") {
+      if (this.selectedExperimentSummariesKey === "all_experiments") {
         return "All Experiments";
-      } else if (this.selectedExperimentSummariesKey === "createdExperiments") {
+      } else if (this.selectedExperimentSummariesKey === "created_experiments") {
         return "Created Experiments";
-      } else if (this.selectedExperimentSummariesKey === "runningExperiments") {
+      } else if (this.selectedExperimentSummariesKey === "running_experiments") {
         return "Running Experiments";
       } else if (
-        this.selectedExperimentSummariesKey === "completedExperiments"
+        this.selectedExperimentSummariesKey === "completed_experiments"
       ) {
         return "Completed Experiments";
       } else if (
-        this.selectedExperimentSummariesKey === "cancelledExperiments"
+        this.selectedExperimentSummariesKey === "cancelled_experiments"
       ) {
         return "Cancelled Experiments";
-      } else if (this.selectedExperimentSummariesKey === "failedExperiments") {
+      } else if (this.selectedExperimentSummariesKey === "failed_experiments") {
         return "Failed Experiments";
       } else {
         return "Experiments";
@@ -592,7 +592,7 @@ export default {
             { ignoreErrors: true }
           );
           this.experimentDetailTabs.push({
-            tabTitle: tabTitle || exp.experimentName,
+            tabTitle: tabTitle || exp.experiment_name,
             experiment: exp,
           });
           this.selectExperimentDetailsTab(experimentId);
@@ -635,7 +635,7 @@ export default {
           );
         }
         this.showExperimentDetails(
-          searchResults.results[0].experimentId,
+          searchResults.results[0].experiment_id,
           `Job ${jobId}`
         );
       }
@@ -651,7 +651,7 @@ export default {
     },
     getExperimentDetailTabsIndex(experimentId) {
       return this.experimentDetailTabs.findIndex(
-        (tab) => tab.experiment.experimentId === experimentId
+        (tab) => tab.experiment.experiment_id === experimentId
       );
     },
     removeExperimentDetailTab(experimentId) {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfilePanel.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfilePanel.vue
index 6ee8234ef..b4f0b25b6 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfilePanel.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfilePanel.vue
@@ -43,7 +43,7 @@ export default {
   created() {
     this.loadExtendedUserProfileFields();
     this.loadExtendedUserProfileValues({
-      username: this.iamUserProfile.userId,
+      username: this.iamUserProfile.user_id,
     });
   },
   computed: {
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/GroupMembershipDisplay.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/GroupMembershipDisplay.vue
index 2af0308e8..9f0654cd9 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/GroupMembershipDisplay.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/GroupMembershipDisplay.vue
@@ -30,20 +30,20 @@ export default {
   },
   computed: {
     adminsGroup() {
-      return this.groups.find((g) => g.isGatewayAdminsGroup);
+      return this.groups.find((g) => g.is_gateway_admins_group);
     },
     readOnlyAdminsGroup() {
-      return this.groups.find((g) => g.isReadOnlyGatewayAdminsGroup);
+      return this.groups.find((g) => g.is_read_only_gateway_admins_group);
     },
     defaultUsersGroup() {
-      return this.groups.find((g) => g.isDefaultGatewayUsersGroup);
+      return this.groups.find((g) => g.is_default_gateway_users_group);
     },
     nonGatewayGroups() {
       return this.groups.filter((g) => {
         return (
-          !g.isGatewayAdminsGroup &&
-          !g.isReadOnlyGatewayAdminsGroup &&
-          !g.isDefaultGatewayUsersGroup
+          !g.is_gateway_admins_group &&
+          !g.is_read_only_gateway_admins_group &&
+          !g.is_default_gateway_users_group
         );
       });
     },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
index 11a2e24b1..808b89b13 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
@@ -26,7 +26,7 @@
         <div class="card">
           <div class="card-body">
             <b-table hover :fields="fields" :items="items" :fixed="true">
-              <template slot="cell(creationTime)" slot-scope="data">
+              <template slot="cell(creation_time)" slot-scope="data">
                 <human-date :date="data.value" />
               </template>
               <template slot="cell(groups)" slot-scope="data">
@@ -34,7 +34,7 @@
               </template>
               <template slot="cell(action)" slot-scope="data">
                 <b-button
-                  v-if="data.item.userHasWriteAccess"
+                  v-if="data.item.user_has_write_access"
                   @click="toggleDetails(data)"
                 >
                   Edit
@@ -98,15 +98,15 @@ export default {
       return [
         {
           label: "First Name",
-          key: "firstName",
+          key: "first_name",
         },
         {
           label: "Last Name",
-          key: "lastName",
+          key: "last_name",
         },
         {
           label: "Username",
-          key: "userId",
+          key: "user_id",
         },
         {
           label: "Email",
@@ -118,7 +118,7 @@ export default {
         },
         {
           label: "Email Verified",
-          key: "emailVerified",
+          key: "email_verified",
         },
         {
           label: "Groups",
@@ -126,7 +126,7 @@ export default {
         },
         {
           label: "Created",
-          key: "creationTime",
+          key: "creation_time",
         },
         {
           label: "Action",
@@ -139,14 +139,14 @@ export default {
         ? this.usersPaginator.results.map((u) => {
             const user = u.clone();
             user._showDetails =
-              this.showingDetails[u.airavataInternalUserId] || false;
+              this.showingDetails[u.airavata_internal_user_id] || false;
             return user;
           })
         : [];
     },
     editableGroups() {
       return this.allGroups
-        ? this.allGroups.filter((g) => g.isAdmin || g.isOwner)
+        ? this.allGroups.filter((g) => g.is_admin || g.is_owner)
         : [];
     },
     currentOffset() {
@@ -162,7 +162,7 @@ export default {
     },
     groupsUpdated(user) {
       services.IAMUserProfileService.update({
-        lookup: user.userId,
+        lookup: user.user_id,
         data: user,
       }).finally(() => {
         this.reloadUserProfiles();
@@ -182,8 +182,8 @@ export default {
     },
     toggleDetails(row) {
       row.toggleDetails();
-      this.showingDetails[row.item.airavataInternalUserId] = !this
-        .showingDetails[row.item.airavataInternalUserId];
+      this.showingDetails[row.item.airavata_internal_user_id] = !this
+        .showingDetails[row.item.airavata_internal_user_id];
     },
     searchUsers() {
       // Reset paginator when starting a search
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue
index d13822794..4f8e1f577 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue
@@ -5,12 +5,12 @@
         <div class="card">
           <div class="card-body">
             <b-table hover :fields="fields" :items="items" :fixed="true">
-              <template slot="cell(creationTime)" slot-scope="data">
+              <template slot="cell(creation_time)" slot-scope="data">
                 <human-date :date="data.value" />
               </template>
               <template slot="cell(action)" slot-scope="data">
                 <b-button
-                  v-if="data.item.userHasWriteAccess"
+                  v-if="data.item.user_has_write_access"
                   @click="toggleDetails(data)"
                 >
                   Edit
@@ -18,14 +18,14 @@
               </template>
               <template slot="row-details" slot-scope="data">
                 <enable-user-panel
-                  v-if="!data.item.enabled && !data.item.emailVerified"
-                  :username="data.item.userId"
+                  v-if="!data.item.enabled && !data.item.email_verified"
+                  :username="data.item.user_id"
                   :email="data.item.email"
                   @enable-user="enableUser"
                 />
                 <delete-user-panel
-                  v-if="!data.item.enabled && !data.item.emailVerified"
-                  :username="data.item.userId"
+                  v-if="!data.item.enabled && !data.item.email_verified"
+                  :username="data.item.user_id"
                   @delete-user="deleteUser"
                 />
               </template>
@@ -71,15 +71,15 @@ export default {
       return [
         {
           label: "First Name",
-          key: "firstName",
+          key: "first_name",
         },
         {
           label: "Last Name",
-          key: "lastName",
+          key: "last_name",
         },
         {
           label: "Username",
-          key: "userId",
+          key: "user_id",
         },
         {
           label: "Email",
@@ -87,11 +87,11 @@ export default {
         },
         {
           label: "Email Verified",
-          key: "emailVerified",
+          key: "email_verified",
         },
         {
           label: "Created",
-          key: "creationTime",
+          key: "creation_time",
         },
         {
           label: "Action",
@@ -127,8 +127,8 @@ export default {
     },
     toggleDetails(row) {
       row.toggleDetails();
-      this.showingDetails[row.item.userId] = !this.showingDetails[
-        row.item.userId
+      this.showingDetails[row.item.user_id] = !this.showingDetails[
+        row.item.user_id
       ];
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
index 452e44d71..e972e835a 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
@@ -2,7 +2,7 @@
   <b-tabs content-class="mt-3 px-2">
     <b-tab
       title="User Profile"
-      :active="iamUserProfile.airavataUserProfileExists"
+      :active="iamUserProfile.airavata_user_profile_exists"
     >
       <b-alert
         variant="warning"
@@ -18,42 +18,42 @@
         <strong>Troubleshooting</strong> tab to fix the user's username.
       </b-alert>
       <edit-groups-panel
-        v-if="iamUserProfile.airavataUserProfileExists"
+        v-if="iamUserProfile.airavata_user_profile_exists"
         :value="localIAMUserProfile.groups"
         :editable-groups="editableGroups"
-        :airavata-internal-user-id="iamUserProfile.airavataInternalUserId"
+        :airavata-internal-user-id="iamUserProfile.airavata_internal_user_id"
         @save="groupsUpdated"
       />
       <user-profile-panel :iamUserProfile="iamUserProfile" />
       <extended-user-profile-panel :iamUserProfile="iamUserProfile" />
       <external-idp-user-info-panel
         v-if="hasExternalIDPUserInfo"
-        :externalIDPUserInfo="localIAMUserProfile.externalIDPUserInfo"
+        :externalIDPUserInfo="localIAMUserProfile.external_idp_user_info"
       />
     </b-tab>
     <b-tab
       title="Troubleshooting"
-      :active="!iamUserProfile.airavataUserProfileExists"
+      :active="!iamUserProfile.airavata_user_profile_exists"
     >
       <activate-user-panel
         v-if="
           iamUserProfile.enabled &&
-          iamUserProfile.emailVerified &&
+          iamUserProfile.email_verified &&
           iamUserProfile.userProfileComplete &&
-          !iamUserProfile.airavataUserProfileExists
+          !iamUserProfile.airavata_user_profile_exists
         "
-        :username="iamUserProfile.userId"
+        :username="iamUserProfile.user_id"
         @activate-user="$emit('enable-user', $event)"
       />
       <enable-user-panel
-        v-if="!iamUserProfile.enabled && !iamUserProfile.emailVerified"
-        :username="iamUserProfile.userId"
+        v-if="!iamUserProfile.enabled && !iamUserProfile.email_verified"
+        :username="iamUserProfile.user_id"
         :email="iamUserProfile.email"
         @enable-user="$emit('enable-user', $event)"
       />
       <delete-user-panel
-        v-if="!iamUserProfile.enabled && !iamUserProfile.emailVerified"
-        :username="iamUserProfile.userId"
+        v-if="!iamUserProfile.enabled && !iamUserProfile.email_verified"
+        :username="iamUserProfile.user_id"
         @delete-user="$emit('delete-user', $event)"
       />
       <b-alert variant="danger" show v-if="isUsernameInvalid">
@@ -61,9 +61,9 @@
         they can complete their user profile.
       </b-alert>
       <change-username-panel
-        :username="iamUserProfile.userId"
+        :username="iamUserProfile.user_id"
         :email="iamUserProfile.email"
-        :airavata-user-profile-exists="iamUserProfile.airavataUserProfileExists"
+        :airavata-user-profile-exists="iamUserProfile.airavata_user_profile_exists"
         @update-username="$emit('update-username', $event)"
       />
     </b-tab>
@@ -121,12 +121,12 @@ export default {
   computed: {
     hasExternalIDPUserInfo() {
       return (
-        Object.keys(this.localIAMUserProfile.externalIDPUserInfo).length !== 0
+        Object.keys(this.localIAMUserProfile.external_idp_user_info).length !== 0
       );
     },
     isUsernameInvalid() {
       return (
-        this.iamUserProfile.userProfileInvalidFields.indexOf("username") >= 0
+        this.iamUserProfile.user_profile_invalid_fields.indexOf("username") >= 0
       );
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
index 40b891523..a630f6c31 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
@@ -71,9 +71,9 @@ export default {
       return this.combinedGroups
         ? this.combinedGroups.filter((g) => {
             return (
-              !g.isDefaultGatewayUsersGroup &&
-              !g.isGatewayAdminsGroup &&
-              !g.isReadOnlyGatewayAdminsGroup
+              !g.is_default_gateway_users_group &&
+              !g.is_gateway_admins_group &&
+              !g.is_read_only_gateway_admins_group
             );
           })
         : [];
@@ -85,7 +85,7 @@ export default {
       return utils.StringUtils.sortIgnoreCase(options, (o) => o.text);
     },
     gatewayUsersGroup() {
-      return this.combinedGroups.find((g) => g.isDefaultGatewayUsersGroup);
+      return this.combinedGroups.find((g) => g.is_default_gateway_users_group);
     },
     gatewayUsersGroupOption() {
       return this.gatewayUsersGroup
@@ -93,13 +93,13 @@ export default {
         : null;
     },
     adminsGroup() {
-      return this.combinedGroups.find((g) => g.isGatewayAdminsGroup);
+      return this.combinedGroups.find((g) => g.is_gateway_admins_group);
     },
     adminsGroupOption() {
       return this.adminsGroup ? this.createGroupOption(this.adminsGroup) : null;
     },
     readOnlyAdminsGroup() {
-      return this.combinedGroups.find((g) => g.isReadOnlyGatewayAdminsGroup);
+      return this.combinedGroups.find((g) => g.is_read_only_gateway_admins_group);
     },
     readOnlyAdminsGroupOption() {
       return this.readOnlyAdminsGroup
@@ -132,7 +132,7 @@ export default {
         value: group.id,
         disabled:
           !group.userHasWriteAccess ||
-          group.ownerId === this.airavataInternalUserId,
+          group.owner_id === this.airavataInternalUserId,
       };
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserProfilePanel.vue b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserProfilePanel.vue
index 346895623..7cb7219fb 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserProfilePanel.vue
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserProfilePanel.vue
@@ -30,7 +30,7 @@ export default {
         return [
           {
             name: "Username",
-            value: this.iamUserProfile.userId,
+            value: this.iamUserProfile.user_id,
             valid: this.isValid("username"),
           },
           {
@@ -40,12 +40,12 @@ export default {
           },
           {
             name: "First Name",
-            value: this.iamUserProfile.firstName,
+            value: this.iamUserProfile.first_name,
             valid: this.isValid("first_name"),
           },
           {
             name: "Last Name",
-            value: this.iamUserProfile.lastName,
+            value: this.iamUserProfile.last_name,
             valid: this.isValid("last_name"),
           },
         ];
@@ -55,7 +55,7 @@ export default {
   methods: {
     isValid(fieldName) {
       return (
-        this.iamUserProfile.userProfileInvalidFields.indexOf(fieldName) < 0
+        this.iamUserProfile.user_profile_invalid_fields.indexOf(fieldName) < 0
       );
     },
   },
diff --git a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
index 922c8225d..c721f5484 100644
--- a/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
+++ b/airavata-django-portal/django_airavata/apps/admin/static/django_airavata_admin/tests/unit/components/statistics/ExperimentStatisticsContainer.spec.js
@@ -66,18 +66,18 @@ test("load experiment by job id when job id matches unique experiment", async ()
         next: null,
         previous: null,
         results: {
-          allExperimentCount: 0,
-          completedExperimentCount: 0,
-          cancelledExperimentCount: 0,
-          failedExperimentCount: 0,
-          createdExperimentCount: 0,
-          runningExperimentCount: 0,
-          allExperiments: [],
-          completedExperiments: [],
-          failedExperiments: [],
-          cancelledExperiments: [],
-          createdExperiments: [],
-          runningExperiments: [],
+          all_experiment_count: 0,
+          completed_experiment_count: 0,
+          cancelled_experiment_count: 0,
+          failed_experiment_count: 0,
+          created_experiment_count: 0,
+          running_experiment_count: 0,
+          all_experiments: [],
+          completed_experiments: [],
+          failed_experiments: [],
+          cancelled_experiments: [],
+          created_experiments: [],
+          running_experiments: [],
         },
         limit: 50,
         offset: 0,
@@ -92,19 +92,19 @@ test("load experiment by job id when job id matches unique experiment", async ()
         count: 1,
         next: null,
         previous: null,
-        results: [{ experimentId: "test-experiment-id" }],
+        results: [{ experiment_id: "test-experiment-id" }],
       },
       models.ExperimentSummary
     )
   );
   // Mock just enough of Experiment and FullExperiment to get ExperimentDetailsView to render
   const experiment = new models.Experiment({
-    experimentId: "test-experiment-id",
-    experimentName: "Test Experiment",
-    creationTime: Date.now(),
-    experimentStatus: [
+    experiment_id: "test-experiment-id",
+    experiment_name: "Test Experiment",
+    creation_time: Date.now(),
+    experiment_status: [
       new ExperimentStatus({
-        timeOfStateChange: Date.now(),
+        time_of_state_change: Date.now(),
         state: models.ExperimentState.COMPLETED,
       }),
     ],
@@ -112,7 +112,7 @@ test("load experiment by job id when job id matches unique experiment", async ()
   services.ExperimentService.retrieve.mockResolvedValue(experiment);
   services.FullExperimentService.retrieve.mockResolvedValue(
     new models.FullExperiment({
-      experimentId: "test-experiment-id",
+      experiment_id: "test-experiment-id",
       experiment,
     })
   );
@@ -150,14 +150,14 @@ test("load experiment by job id when job id matches unique experiment", async ()
   // Double check that the experiment services were called to load the experiment
   expect(services.ExperimentService.retrieve).toHaveBeenCalledWith(
     {
-      lookup: experiment.experimentId,
+      lookup: experiment.experiment_id,
     },
     {
       ignoreErrors: true,
     }
   );
   expect(services.FullExperimentService.retrieve).toHaveBeenCalledWith({
-    lookup: experiment.experimentId,
+    lookup: experiment.experiment_id,
   });
 });
 
@@ -171,18 +171,18 @@ test("Hostname filter only shows compute resources that are configured in a GRP"
         next: null,
         previous: null,
         results: {
-          allExperimentCount: 0,
-          completedExperimentCount: 0,
-          cancelledExperimentCount: 0,
-          failedExperimentCount: 0,
-          createdExperimentCount: 0,
-          runningExperimentCount: 0,
-          allExperiments: [],
-          completedExperiments: [],
-          failedExperiments: [],
-          cancelledExperiments: [],
-          createdExperiments: [],
-          runningExperiments: [],
+          all_experiment_count: 0,
+          completed_experiment_count: 0,
+          cancelled_experiment_count: 0,
+          failed_experiment_count: 0,
+          created_experiment_count: 0,
+          running_experiment_count: 0,
+          all_experiments: [],
+          completed_experiments: [],
+          failed_experiments: [],
+          cancelled_experiments: [],
+          created_experiments: [],
+          running_experiments: [],
         },
         limit: 50,
         offset: 0,
@@ -200,22 +200,22 @@ test("Hostname filter only shows compute resources that are configured in a GRP"
 
   services.GroupResourceProfileService.list.mockResolvedValue([
     new models.GroupResourceProfile({
-      computePreferences: [
+      compute_preferences: [
         new models.GroupComputeResourcePreference({
-          computeResourceId: "compute1-abcd",
+          compute_resource_id: "compute1-abcd",
         }),
         new models.GroupComputeResourcePreference({
-          computeResourceId: "compute3-abcd",
+          compute_resource_id: "compute3-abcd",
         }),
       ],
     }),
     new models.GroupResourceProfile({
-      computePreferences: [
+      compute_preferences: [
         new models.GroupComputeResourcePreference({
-          computeResourceId: "compute1-abcd",
+          compute_resource_id: "compute1-abcd",
         }),
         new models.GroupComputeResourcePreference({
-          computeResourceId: "compute4-abcd",
+          compute_resource_id: "compute4-abcd",
         }),
       ],
     }),
diff --git a/airavata-django-portal/django_airavata/apps/api/admin.py b/airavata-django-portal/django_airavata/apps/api/admin.py
deleted file mode 100644
index a5d03645c..000000000
--- a/airavata-django-portal/django_airavata/apps/api/admin.py
+++ /dev/null
@@ -1,26 +0,0 @@
-
-# Register your models here.
-
-from django.contrib import admin
-
-from .models import ApplicationTemplate, ApplicationTemplateContextProcessor
-
-
-class ApplicationTemplateContextProcessorInline(admin.StackedInline):
-    model = ApplicationTemplateContextProcessor
-    extra = 1
-
-
-class ApplicationTemplateAdmin(admin.ModelAdmin):
-    fields = ['application_module_id', 'template_path']
-    list_display = ['application_module_id', 'template_path', 'updated_by', 'updated']
-    inlines = [ApplicationTemplateContextProcessorInline]
-
-    def save_model(self, request, obj, form, change):
-        obj.updated_by = request.user
-        if not obj.pk:
-            obj.created_by = request.user
-        return super().save_model(request, obj, form, change)
-
-
-admin.site.register(ApplicationTemplate, ApplicationTemplateAdmin)
diff --git a/airavata-django-portal/django_airavata/apps/api/authentication.py b/airavata-django-portal/django_airavata/apps/api/authentication.py
deleted file mode 100644
index 2cf95e4d6..000000000
--- a/airavata-django-portal/django_airavata/apps/api/authentication.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import logging
-
-from django.contrib.auth import authenticate
-from rest_framework import authentication, exceptions
-
-from django_airavata.apps.auth import utils
-from django_airavata.apps.auth.middleware import set_admin_group_attributes
-
-logger = logging.getLogger(__name__)
-
-
-class OAuthAuthentication(authentication.BaseAuthentication):
-    def authenticate(self, request):
-
-        if 'HTTP_AUTHORIZATION' in request.META:
-            try:
-                user = authenticate(request=request)
-                if user is None:
-                    raise exceptions.AuthenticationFailed(
-                        "Token failed to authenticate")
-                _, token = request.META.get('HTTP_AUTHORIZATION').split()
-
-                logger.debug(f"OAuthAuthentication authenticated user {user}")
-                # Set request attributes that are normally set by middleware
-                request.authz_token = utils.get_authz_token(request, user=user, access_token=token)
-                request.user = user
-                set_admin_group_attributes(request)
-                return (user, token)
-            except Exception as e:
-                raise exceptions.AuthenticationFailed(
-                    "Token failed to authenticate") from e
-        else:
-            return None
diff --git a/airavata-django-portal/django_airavata/apps/api/downloads.py b/airavata-django-portal/django_airavata/apps/api/downloads.py
index 43fa30959..dbef27283 100644
--- a/airavata-django-portal/django_airavata/apps/api/downloads.py
+++ b/airavata-django-portal/django_airavata/apps/api/downloads.py
@@ -9,11 +9,12 @@ through the ``download``/``download-file`` views instead.
 import io
 import logging
 import os
+import zipfile
 
-import zipstream
 from django.http import StreamingHttpResponse
 from django.utils.text import get_valid_filename
-from rest_framework.decorators import api_view
+
+from django_airavata.apps.api.web import api_view
 
 from .views import _user_storage_path
 
@@ -63,16 +64,26 @@ def _join(base, rel):
 
 
 def _zip_response(storage, filename, entries):
-    zf = zipstream.ZipFile(compression=zipstream.ZIP_DEFLATED, allowZip64=True)
-    for archive_name, abs_path, size in entries:
-        zf.write_iter(archive_name, _read_bytes(storage, abs_path), buffer_size=size or 0)
-    response = StreamingHttpResponse(zf, content_type='application/zip')
+    # Files are already fully read into memory (storage.download_file().content),
+    # so build the whole archive in a BytesIO with stdlib zipfile and stream the
+    # buffer back. NOTE: this buffers the entire zip in memory — fine for dev, but
+    # for large directories in production this should be replaced with a true
+    # streaming zip (the previous zipstream-new behavior).
+    buf = io.BytesIO()
+    with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as zf:
+        for archive_name, abs_path, size in entries:
+            zf.writestr(archive_name, storage.download_file(abs_path).content)
+    buf.seek(0)
+    response = StreamingHttpResponse(
+        _iter_buffer(buf), content_type='application/zip')
     response['Content-Disposition'] = f'attachment; filename={filename}'
     return response
 
 
-def _read_bytes(storage, abs_path):
-    """Yield a stored file's bytes in buffer-sized chunks for the zip stream."""
-    content = storage.download_file(abs_path).content
-    for i in range(0, len(content), io.DEFAULT_BUFFER_SIZE):
-        yield content[i:i + io.DEFAULT_BUFFER_SIZE]
+def _iter_buffer(buf):
+    """Yield the in-memory archive in buffer-sized chunks."""
+    while True:
+        chunk = buf.read(io.DEFAULT_BUFFER_SIZE)
+        if not chunk:
+            break
+        yield chunk
diff --git a/airavata-django-portal/django_airavata/apps/api/exceptions.py b/airavata-django-portal/django_airavata/apps/api/exceptions.py
index 3b703f469..7a673b54b 100644
--- a/airavata-django-portal/django_airavata/apps/api/exceptions.py
+++ b/airavata-django-portal/django_airavata/apps/api/exceptions.py
@@ -1,79 +1,12 @@
-import logging
-import sys
+from http import HTTPStatus
 
-import grpc
-from django.core.exceptions import ObjectDoesNotExist
 from django.http import JsonResponse
-from rest_framework import status
-from rest_framework.exceptions import NotAuthenticated
-from rest_framework.response import Response
-from rest_framework.views import exception_handler
 
-log = logging.getLogger(__name__)
 
-# Map gRPC status codes to HTTP responses (the portal talks only to the gRPC
-# facade now; the legacy Thrift exception handlers are gone).
-GRPC_STATUS_TO_HTTP = {
-    grpc.StatusCode.NOT_FOUND: status.HTTP_404_NOT_FOUND,
-    grpc.StatusCode.PERMISSION_DENIED: status.HTTP_403_FORBIDDEN,
-    grpc.StatusCode.UNAUTHENTICATED: status.HTTP_401_UNAUTHORIZED,
-    grpc.StatusCode.INVALID_ARGUMENT: status.HTTP_400_BAD_REQUEST,
-    grpc.StatusCode.FAILED_PRECONDITION: status.HTTP_400_BAD_REQUEST,
-    grpc.StatusCode.ALREADY_EXISTS: status.HTTP_409_CONFLICT,
-    grpc.StatusCode.UNIMPLEMENTED: status.HTTP_501_NOT_IMPLEMENTED,
-}
-
-
-def custom_exception_handler(exc, context):
-    # Call REST framework's default exception handler first,
-    # to get the standard error response.
-    response = exception_handler(exc, context)
-
-    if isinstance(exc, grpc.RpcError):
-        code = exc.code()
-        detail = exc.details() or str(exc)
-        if code == grpc.StatusCode.UNAVAILABLE:
-            log.warning("gRPC UNAVAILABLE", exc_info=exc)
-            return Response(
-                {'detail': detail, 'apiServerDown': True},
-                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-        http_status = GRPC_STATUS_TO_HTTP.get(
-            code, status.HTTP_500_INTERNAL_SERVER_ERROR)
-        if http_status >= 500:
-            log.error("gRPC error %s", code, exc_info=exc,
-                      extra={'request': context['request']})
-        else:
-            log.warning("gRPC error %s", code, exc_info=exc)
-        return Response({'detail': detail}, status=http_status)
-
-    if isinstance(exc, ObjectDoesNotExist):
-        log.warning("ObjectDoesNotExist", exc_info=exc)
-        return Response(
-            {'detail': str(exc)},
-            status=status.HTTP_404_NOT_FOUND)
-
-    if isinstance(exc, NotAuthenticated):
-        log.debug("NotAuthenticated", exc_info=exc)
-        if response is not None:
-            response.data['is_authenticated'] = False
-
-    if isinstance(exc, UnicodeEncodeError):
-        fse = sys.getfilesystemencoding()
-        if fse != 'utf-8':
-            log.error(f"filesystem encoding is {fse}, not 'utf-8'. File paths with Unicode characters will produce errors.")
-
-    # Generic handler
-    if response is None:
-        log.error("API exception", exc_info=exc, extra={'request': context['request']})
-        return Response(
-            {'detail': str(exc)},
-            status=status.HTTP_500_INTERNAL_SERVER_ERROR
-        )
-
-    return response
-
-
-# For non-Django REST Framework error responses
+# For ad-hoc JSON error responses from function views that don't go through the
+# web.py dispatch exception mapping (e.g. the tus upload-finish handler). The
+# grpc->HTTP mapping that ``custom_exception_handler`` used to provide now lives
+# in ``web.exception_to_response`` (``web.GRPC_STATUS_TO_HTTP``).
 def generic_json_exception_response(
-        exc, status=status.HTTP_500_INTERNAL_SERVER_ERROR):
+        exc, status=HTTPStatus.INTERNAL_SERVER_ERROR):
     return JsonResponse({'detail': str(exc)}, status=status)
diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py b/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
deleted file mode 100644
index 817b8dea8..000000000
--- a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Map gRPC ``FileMetadataResponse`` messages to the plain-dict shape the user
-storage serializers read.
-
-The user-storage file/directory serializers consume dicts keyed the way the
-legacy ``user_storage.listdir`` produced; these helpers convert the gRPC
-``FileMetadataResponse`` into that dict shape. (All the Thrift-attribute-shape
-read adapters that once lived here are gone — every serializer is proto-native.)
-"""
-
-
-# --- User storage file/directory listings -----------------------------------
-# The storage serializers (UserStorageFileSerializer / UserStorageDirectorySerializer)
-# read plain dicts keyed the way the legacy user_storage.listdir produced. The gRPC
-# FileMetadataResponse carries name/path/size/modified_time(epoch ms)/content_type/
-# data_product_uri; map it to that dict shape. ``relative_path`` overrides the path the
-# serializer reports (e.g. experiment-dir listings expose a path relative to the data dir).
-
-def _epoch_millis_to_datetime(value):
-    """Epoch milliseconds -> aware UTC datetime, or None when unset (0)."""
-    if not value:
-        return None
-    import datetime
-    return datetime.datetime.fromtimestamp(value / 1000, tz=datetime.timezone.utc)
-
-
-def user_storage_file(pb, relative_path=None):
-    """gRPC ``FileMetadataResponse`` (a file) -> UserStorageFileSerializer dict."""
-    modified = _epoch_millis_to_datetime(pb.modified_time)
-    return {
-        'name': pb.name,
-        'path': relative_path if relative_path is not None else pb.path,
-        'data-product-uri': pb.data_product_uri or None,
-        # The adaptor exposes only a modified time; reuse it for created time so
-        # the serializer's createdTime/modifiedTime both render (SFTP has no ctime).
-        'created_time': modified,
-        'modified_time': modified,
-        'mime_type': pb.content_type or None,
-        'size': pb.size,
-        'hidden': False,
-    }
-
-
-def user_storage_directory(pb, relative_path=None):
-    """gRPC ``FileMetadataResponse`` (a directory) -> UserStorageDirectorySerializer dict."""
-    modified = _epoch_millis_to_datetime(pb.modified_time)
-    return {
-        'name': pb.name,
-        'path': relative_path if relative_path is not None else pb.path,
-        'created_time': modified,
-        'modified_time': modified,
-        'size': pb.size,
-        'hidden': False,
-    }
diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
deleted file mode 100644
index 6165b2286..000000000
--- a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""Adapters from the Thrift-shaped objects the DRF serializers produce to the
-gRPC protobuf request messages the facade expects.
-
-Track D (D3 writes): the ``apps/api`` serializers were generated from the Thrift
-models, so ``serializer.save()`` yields a Thrift model instance (Thrift attribute
-names: ``projectID``, ``gatewayId``, ...). These adapters convert that instance
-into the corresponding protobuf message (``project_id``, ``gateway_id``, ...) so
-the gRPC facade can send it. They are the write-direction mirror of
-``grpc_adapters`` and are removed once the serializers are made protobuf-native.
-
-proto3 scalar fields cannot hold ``None``, so optional Thrift values that may be
-``None`` are coerced to the proto default (``''``/``0``/``[]``).
-"""
-
-import importlib
-
-_GEN = "airavata_sdk.generated.org.apache.airavata.model"
-
-
-def _pb2(path):
-    return importlib.import_module(f"{_GEN}.{path}")
-
-
-def password_credential(gateway_id, portal_user_name, login_user_name,
-                         password, description):
-    """Build a proto ``PasswordCredential`` from the create-password request."""
-    return _pb2("credential.store.credential_store_pb2").PasswordCredential(
-        gateway_id=gateway_id or '',
-        portal_user_name=portal_user_name or '',
-        login_user_name=login_user_name or '',
-        password=password or '',
-        description=description or '',
-    )
-
-
-def data_product_for_upload(*, gateway_id, owner_name, product_name, file_path,
-                            storage_resource_id, content_type=None, product_size=0):
-    """Build a proto ``DataProductModel`` to register for a freshly uploaded file.
-
-    The gRPC ``storage.upload_file`` only transfers the bytes and returns a
-    minimal ``DataProductModel``; the portal registers the full data product via
-    ``research.register_data_product`` so the file gets a canonical product URI.
-    Mirrors the legacy ``user_storage._create_data_product`` shape: a single
-    GATEWAY_DATA_STORE / TRANSIENT replica pointing at ``file_path``, with the
-    content type recorded under ``mime-type`` metadata.
-    """
-    rc = _pb2("data.replica.replica_catalog_pb2")
-    product_metadata = {"mime-type": content_type} if content_type else {}
-    return rc.DataProductModel(
-        gateway_id=gateway_id,
-        owner_name=owner_name,
-        product_name=product_name,
-        data_product_type=rc.DataProductType.FILE,
-        product_size=product_size or 0,
-        product_metadata=product_metadata,
-        replica_locations=[rc.DataReplicaLocationModel(
-            replica_name="{} gateway data store copy".format(product_name),
-            replica_location_category=rc.ReplicaLocationCategory.GATEWAY_DATA_STORE,
-            replica_persistent_type=rc.ReplicaPersistentType.TRANSIENT,
-            storage_resource_id=storage_resource_id or '',
-            file_path=file_path,
-        )],
-    )
diff --git a/airavata-django-portal/django_airavata/apps/api/helpers.py b/airavata-django-portal/django_airavata/apps/api/helpers.py
index ee19990e8..1c932afc1 100644
--- a/airavata-django-portal/django_airavata/apps/api/helpers.py
+++ b/airavata-django-portal/django_airavata/apps/api/helpers.py
@@ -1,73 +1,152 @@
 import logging
 
-from django.conf import settings
-from django.core.exceptions import ObjectDoesNotExist
+from django.core.cache import cache
 
-from . import models
+from airavata_sdk.helpers import compute_resources
 
 logger = logging.getLogger(__name__)
 
 
+# Per-user workspace preferences (most-recent project/group/compute + per-app
+# favorites) live in the cache, not the DB (was api_workspacepreferences /
+# api_applicationpreferences). Pure UX state; cache eviction just reseeds the
+# defaults on the next read.
+def _prefs_key(username):
+    return f"workspace_prefs:{username}"
+
+
+def _load_prefs(username):
+    return cache.get(_prefs_key(username))
+
+
+def _store_prefs(username, data):
+    cache.set(_prefs_key(username), data)
+
+
+class _ApplicationPreference:
+    """Cache-backed stand-in for the old ApplicationPreferences row.
+
+    Exposes the ``application_id`` / ``favorite`` attributes the serializer and
+    favorite/unfavorite views read; ``save()`` writes the favorite flag back
+    through the owning preferences object.
+    """
+
+    def __init__(self, prefs, application_id, favorite):
+        self._prefs = prefs
+        self.application_id = application_id
+        self.favorite = favorite
+
+    def save(self):
+        self._prefs._set_favorite(self.application_id, self.favorite)
+
+
+class _ApplicationPreferencesManager:
+    """Mimics the ORM reverse-FK manager (``applicationpreferences_set``)."""
+
+    def __init__(self, prefs):
+        self._prefs = prefs
+
+    def all(self):
+        return [
+            _ApplicationPreference(self._prefs, app_id, favorite)
+            for app_id, favorite in self._prefs._favorites.items()
+        ]
+
+    def get(self, application_id):
+        if application_id not in self._prefs._favorites:
+            from django.core.exceptions import ObjectDoesNotExist
+            raise ObjectDoesNotExist()
+        return _ApplicationPreference(
+            self._prefs, application_id,
+            self._prefs._favorites[application_id])
+
+    def create(self, username=None, application_id=None, favorite=False):
+        self._prefs._set_favorite(application_id, favorite)
+        return _ApplicationPreference(self._prefs, application_id, favorite)
+
+
+class WorkspacePreferences:
+    """Cache-backed stand-in for the old WorkspacePreferences row.
+
+    Preserves the attribute/method surface every call site relies on:
+    ``most_recent_*`` mutable attributes, ``save()``, and an
+    ``applicationpreferences_set`` reverse-manager.
+    """
+
+    def __init__(self, username, data):
+        self.username = username
+        self.most_recent_project_id = data.get("most_recent_project_id")
+        self.most_recent_group_resource_profile_id = data.get(
+            "most_recent_group_resource_profile_id")
+        self.most_recent_compute_resource_id = data.get(
+            "most_recent_compute_resource_id")
+        self._favorites = dict(data.get("application_preferences", {}))
+        self.applicationpreferences_set = _ApplicationPreferencesManager(self)
+
+    def _as_dict(self):
+        return {
+            "most_recent_project_id": self.most_recent_project_id,
+            "most_recent_group_resource_profile_id":
+                self.most_recent_group_resource_profile_id,
+            "most_recent_compute_resource_id":
+                self.most_recent_compute_resource_id,
+            "application_preferences": self._favorites,
+        }
+
+    def save(self):
+        _store_prefs(self.username, self._as_dict())
+
+    def _set_favorite(self, application_id, favorite):
+        self._favorites[application_id] = favorite
+        self.save()
+
+
 class WorkspacePreferencesHelper:
+    """Read/write a user's workspace preferences.
+
+    The record lives in this portal's cache; the server-side lookups needed to
+    seed/validate it (first writeable project, accessible group resource
+    profiles) are delegated to the SDK ``compute_resources`` helpers.
+    """
 
     def get(self, request):
-        try:
-            workspace_preferences = models.WorkspacePreferences.objects.get(
-                username=request.user.username)
-            self._check(request, workspace_preferences)
-        except ObjectDoesNotExist:
-            workspace_preferences = self._create_default(request)
+        username = request.user.username
+        data = _load_prefs(username)
+        if data is None:
+            workspace_preferences = self._create_default(request, username)
             workspace_preferences.save()
+        else:
+            workspace_preferences = WorkspacePreferences(username, data)
+            self._check(request, workspace_preferences)
         return workspace_preferences
 
-    def _create_default(self, request):
-        workspace_preferences = models.WorkspacePreferences.create(
-            request.user.username)
-        most_recent_project = self._get_most_recent_project(request)
-        workspace_preferences.most_recent_project_id = (
-            most_recent_project.project_id if most_recent_project else None)
-        first_grp = \
-            self._get_first_group_resource_profile(request)
-        workspace_preferences.most_recent_group_resource_profile_id = \
-            first_grp.group_resource_profile_id if first_grp else None
-        return workspace_preferences
-
-    def _get_most_recent_project(self, request):
-        "Return most recent writeable project."
-        projects = request.airavata.research.get_user_projects(
-            gateway_id=settings.GATEWAY_ID, user_name=request.user.username,
-            limit=-1, offset=0)
-        for project in projects:
-            if self._can_write(request, project.project_id):
-                return project
-        return None
-
-    def _get_first_group_resource_profile(self, request):
-        "Return first accessible group resource profile"
-
-        group_resource_profiles = \
-            request.airavata.compute.get_group_resource_list()
-        if len(group_resource_profiles) > 0:
-            return group_resource_profiles[0]
-        else:
-            return None
+    def _create_default(self, request, username):
+        defaults = compute_resources.resolve_workspace_defaults(
+            request.airavata)
+        return WorkspacePreferences(username, {
+            "most_recent_project_id": defaults["most_recent_project_id"],
+            "most_recent_group_resource_profile_id":
+                defaults["most_recent_group_resource_profile_id"],
+        })
 
     def _check(self, request, prefs):
         "Validate preference values and update as needed."
         if (not prefs.most_recent_project_id or
                 not self._can_write(request, prefs.most_recent_project_id)):
-            most_recent_project = self._get_most_recent_project(request)
-            if most_recent_project is not None:
-                logger.info("_check: updating most_recent_project_id to {}".format(most_recent_project.project_id))
-                prefs.most_recent_project_id = most_recent_project.project_id
+            most_recent_project_id = (
+                compute_resources.most_recent_writeable_project_id(
+                    request.airavata))
+            if most_recent_project_id is not None:
+                logger.info("_check: updating most_recent_project_id to {}".format(most_recent_project_id))
+                prefs.most_recent_project_id = most_recent_project_id
                 prefs.save()
             else:
                 logger.warning("_check: no writeable projects found, unsetting most_recent_project_id")
                 prefs.most_recent_project_id = None
                 prefs.save()
-        group_resource_profiles = \
-            request.airavata.compute.get_group_resource_list()
-        group_resource_profile_ids = [g.group_resource_profile_id for g in group_resource_profiles]
+        group_resource_profile_ids = (
+            compute_resources.accessible_group_resource_profile_ids(
+                request.airavata))
         if (not prefs.most_recent_group_resource_profile_id or
                 prefs.most_recent_group_resource_profile_id not in group_resource_profile_ids):
             first_grp_id = (group_resource_profile_ids[0]
@@ -80,9 +159,7 @@ class WorkspacePreferencesHelper:
             prefs.save()
 
     def _can_write(self, request, entity_id):
-        return request.airavata.sharing.user_has_access(
-            resource_id=entity_id, user_id=request.user.username,
-            permission_type="WRITE")
+        return compute_resources.user_can_write(request.airavata, entity_id)
 
     def _can_read(self, request, entity_id):
         return request.airavata.sharing.user_has_access(
diff --git a/airavata-django-portal/django_airavata/apps/api/output_views.py b/airavata-django-portal/django_airavata/apps/api/output_views.py
index 0df73208f..7833dc6bf 100644
--- a/airavata-django-portal/django_airavata/apps/api/output_views.py
+++ b/airavata-django-portal/django_airavata/apps/api/output_views.py
@@ -1,6 +1,5 @@
 import collections
 import inspect
-import io
 import json
 import logging
 import os
@@ -11,11 +10,10 @@ import papermill as pm
 from airavata_sdk.generated.org.apache.airavata.model.application.io.application_io_pb2 import (  # noqa: E501
     DataType,
 )
+from airavata_sdk.helpers import research_resources, storage_resources
 from django.conf import settings
 from nbconvert import HTMLExporter
 
-from . import view_utils
-
 logger = logging.getLogger(__name__)
 
 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -177,7 +175,8 @@ def generate_data(request,
                   **kwargs):
     output_view_provider = _get_output_view_provider(output_view_provider_id)
     # TODO if output_view_provider is None, return 404
-    experiment = request.airavata.research.get_experiment(experiment_id)
+    experiment = research_resources.get_experiment_proto(
+        request.airavata, experiment_id)
     experiment_output = [o
                          for o in experiment.experiment_outputs
                          if o.name == experiment_output_name]
@@ -219,16 +218,9 @@ def _generate_data(request,
                                      DataType.STDERR) and
           experiment_output.value.startswith("airavata-dp")):
         data_product_uris = experiment_output.value.split(",")
-        data_products = map(
-            lambda dpid: request.airavata.research.get_data_product(dpid),
-            data_product_uris)
-        for data_product in data_products:
-            file_path = view_utils.data_product_file_path(data_product)
-            if file_path and request.airavata.storage.file_exists(file_path):
-                resp = request.airavata.storage.download_file(file_path)
-                output_file = io.BytesIO(resp.content)
-                output_file.name = resp.name or os.path.basename(file_path)
-                output_files.append(output_file)
+        output_files.extend(
+            storage_resources.download_data_product_files(
+                request.airavata, data_product_uris))
 
     generate_data_func = output_view_provider.generate_data
     method_sig = inspect.signature(generate_data_func)
diff --git a/airavata-django-portal/django_airavata/apps/api/proto_render.py b/airavata-django-portal/django_airavata/apps/api/proto_render.py
new file mode 100644
index 000000000..41b8e2d03
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/api/proto_render.py
@@ -0,0 +1,98 @@
+"""Generic proto-direct → snake_case JSON rendering for migrated ViewSets.
+
+:func:`to_jsonable` turns an SDK return value (a proto ``Message``, a
+:class:`~airavata_sdk.helpers._envelope.WithAccess` envelope, a composed pydantic
+``BaseModel``, or a list/dict of those) into plain JSON-serializable Python with
+snake_case keys; :class:`ProtoJSONRenderer` applies it on the DRF response path.
+
+The ``MessageToDict`` options below are the source-of-truth read contract; the
+defaults (enums → member NAMES, int64 → decimal STRING) are deliberate and
+relied on by every migrated ViewSet — document any change.
+
+``preserving_proto_field_name=True``  (REQUIRED)
+    Emit proto field names verbatim (``project_id``) instead of the
+    lowerCamelCase JSON names — this is what makes the output snake_case.
+
+``always_print_fields_with_no_presence=True``
+    Emit every singular field even at its default value, so the JSON shape is
+    STABLE regardless of which fields the backend populated (the frontend reads a
+    fixed key set). Repeated/map and explicit-presence fields are unaffected — an
+    unset message stays absent, an empty list stays ``[]``.
+
+Enums render as the member NAME string (rename-stable, frontend-readable);
+int64 / uint64 / fixed64 render as decimal STRINGS (they exceed JS ``Number``
+safe-integer range), e.g. ``creation_time`` epoch-millis as ``"1705320000000"``.
+"""
+
+import json
+
+from django.core.serializers.json import DjangoJSONEncoder
+from google.protobuf.json_format import MessageToDict
+from google.protobuf.message import Message
+
+try:  # pydantic is optional on this path; only composed shapes use it
+    from pydantic import BaseModel as _PydanticBaseModel
+except Exception:  # pragma: no cover - pydantic always present in practice
+    _PydanticBaseModel = None
+
+from airavata_sdk.helpers._envelope import WithAccess, WithGroupAccess
+
+# The single source of truth for how every proto on the read path is rendered.
+_MESSAGE_TO_DICT_OPTS = dict(
+    preserving_proto_field_name=True,
+    always_print_fields_with_no_presence=True,
+)
+
+
+def proto_to_dict(message: Message) -> dict:
+    return MessageToDict(message, **_MESSAGE_TO_DICT_OPTS)
+
+
+def to_jsonable(obj):
+    """Convert an SDK return value into JSON-serializable snake_case Python.
+
+    An envelope (``WithAccess`` / ``WithGroupAccess``) renders its proto and
+    merges its access scalars on top — the extras are exactly the cross-service
+    fields kept OUT of the proto, so keys never collide. A ``dict`` recurses
+    values but passes keys through unchanged (callers here already use snake_case).
+    """
+    if isinstance(obj, Message):
+        return proto_to_dict(obj)
+    if isinstance(obj, WithAccess):
+        base = to_jsonable(obj.message)
+        base["is_owner"] = obj.is_owner
+        base["user_has_write_access"] = obj.user_has_write_access
+        return base
+    if isinstance(obj, WithGroupAccess):
+        base = to_jsonable(obj.message)
+        base["is_admin"] = obj.is_admin
+        base["is_owner"] = obj.is_owner
+        base["is_member"] = obj.is_member
+        base["is_gateway_admins_group"] = obj.is_gateway_admins_group
+        base["is_read_only_gateway_admins_group"] = \
+            obj.is_read_only_gateway_admins_group
+        base["is_default_gateway_users_group"] = \
+            obj.is_default_gateway_users_group
+        return base
+    if isinstance(obj, (list, tuple)):
+        return [to_jsonable(x) for x in obj]
+    if isinstance(obj, dict):
+        return {k: to_jsonable(v) for k, v in obj.items()}
+    if _PydanticBaseModel is not None and isinstance(obj, _PydanticBaseModel):
+        # Recurse field-by-field (NOT a flat ``model_dump()``) so a composed
+        # model may carry protos / ``WithAccess`` envelopes / nested dicts that
+        # the SAME renderer flattens.  Plain JSON-able fields are unchanged.
+        return {
+            name: to_jsonable(getattr(obj, name))
+            for name in type(obj).model_fields
+        }
+    return obj
+
+
+class ProtoJSONRenderer:
+    """Plain JSON renderer that runs :func:`to_jsonable` over the data, then
+    JSON-encodes it to bytes. Page views call ``.render(data) -> bytes``."""
+
+    def render(self, data, accepted_media_type=None, renderer_context=None):
+        return json.dumps(
+            to_jsonable(data), cls=DjangoJSONEncoder).encode('utf-8')
diff --git a/airavata-django-portal/django_airavata/apps/api/serializers.py b/airavata-django-portal/django_airavata/apps/api/serializers.py
index 5cbe68179..39c301537 100644
--- a/airavata-django-portal/django_airavata/apps/api/serializers.py
+++ b/airavata-django-portal/django_airavata/apps/api/serializers.py
@@ -1,63 +1,8 @@
-import datetime
-import enum
 import json
 import logging
-import os
-from urllib.parse import quote
-
-from airavata_sdk.helpers import experiment_orchestration
-from django.conf import settings
-from django.contrib.auth import get_user_model
-from django.urls import reverse
-from rest_framework import serializers
-
-from . import models, view_utils
 
 log = logging.getLogger(__name__)
 
-# Directory under user storage where uploaded experiment input files are staged
-# (matches the legacy airavata_django_portal_sdk convention).
-TMP_INPUT_FILE_UPLOAD_DIR = "tmp"
-
-
-# Historical Thrift enum member -> integer tables. The REST contract emits
-# these Thrift enum integers (e.g. dataProductType FILE == 0), which differ from
-# the proto enum integers; the proto-native serializers bridge by member NAME via
-# proto_enum_int_field / proto_enum_keyed_map_field. Hardcoded so the portal no
-# longer depends on the legacy Thrift model types for the enum values.
-_THRIFT_ENUM_INTS = {
-    'ApplicationParallelismType': {'CCM': 4, 'CRAY_MPI': 5, 'MPI': 1, 'OPENMP': 2, 'OPENMP_MPI': 3, 'SERIAL': 0},
-    'DataMovementProtocol': {'GridFTP': 3, 'LOCAL': 0, 'SCP': 1, 'SFTP': 2, 'UNICORE_STORAGE_SERVICE': 4},
-    'DataProductType': {'COLLECTION': 1, 'FILE': 0},
-    'DataType': {'FLOAT': 2, 'INTEGER': 1, 'STDERR': 6, 'STDOUT': 5, 'STRING': 0, 'URI': 3, 'URI_COLLECTION': 4},
-    'ExperimentState': {'CANCELED': 6, 'CANCELING': 5, 'COMPLETED': 7, 'CREATED': 0, 'EXECUTING': 4, 'FAILED': 8, 'LAUNCHED': 3, 'SCHEDULED': 2, 'VALIDATED': 1},
-    'ExperimentType': {'SINGLE_APPLICATION': 0, 'WORKFLOW': 1},
-    'FileSystems': {'ARCHIVE': 4, 'HOME': 0, 'LOCALTMP': 2, 'SCRATCH': 3, 'WORK': 1},
-    'IOType': {'FILE': 0, 'PROPERTY': 1},
-    'JobManagerCommand': {'CHECK_JOB': 3, 'DELETION': 2, 'JOB_MONITORING': 1, 'SHOW_CLUSTER_INFO': 7, 'SHOW_NO_OF_PENDING_JOBS': 9, 'SHOW_NO_OF_RUNNING_JOBS': 8, 'SHOW_QUEUE': 4, 'SHOW_RESERVATION': 5, 'SHOW_START': 6, 'SUBMISSION': 0},
-    'JobState': {'ACTIVE': 2, 'CANCELED': 4, 'COMPLETE': 3, 'FAILED': 5, 'NON_CRITICAL_FAIL': 8, 'QUEUED': 1, 'SUBMITTED': 0, 'SUSPENDED': 6, 'UNKNOWN': 7},
-    'JobSubmissionProtocol': {'CLOUD': 4, 'GLOBUS': 2, 'LOCAL': 0, 'LOCAL_FORK': 6, 'SSH': 1, 'SSH_FORK': 5, 'UNICORE': 3},
-    'MonitorMode': {'CLOUD_JOB_MONITOR': 1, 'FORK': 4, 'JOB_EMAIL_NOTIFICATION_MONITOR': 2, 'LOCAL': 5, 'POLL_JOB_MANAGER': 0, 'XSEDE_AMQP_SUBSCRIBE': 3},
-    'ProcessState': {'CANCELED': 13, 'CANCELLING': 12, 'COMPLETED': 10, 'CONFIGURING_WORKSPACE': 4, 'CREATED': 0, 'DEQUEUING': 15, 'EXECUTING': 6, 'FAILED': 11, 'INPUT_DATA_STAGING': 5, 'MONITORING': 7, 'OUTPUT_DATA_STAGING': 8, 'POST_PROCESSING': 9, 'PRE_PROCESSING': 3, 'QUEUED': 14, 'REQUEUED': 16, 'STARTED': 2, 'VALIDATED': 1},
-    'ProviderName': {'AWSEC2': 1, 'EC2': 0, 'RACKSPACE': 2},
-    'ReplicaLocationCategory': {'COMPUTE_RESOURCE': 1, 'GATEWAY_DATA_STORE': 0, 'LONG_TERM_STORAGE_RESOURCE': 2, 'OTHER': 3},
-    'ReplicaPersistentType': {'PERSISTENT': 1, 'TRANSIENT': 0},
-    'ResourceJobManagerType': {'AIRAVATA_CUSTOM': 6, 'CLOUD': 5, 'FORK': 0, 'HTCONDOR': 7, 'LSF': 3, 'PBS': 1, 'SLURM': 2, 'UGE': 4},
-    'ResourcePermissionType': {'MANAGE_SHARING': 3, 'OWNER': 2, 'READ': 1, 'WRITE': 0},
-    'ResourceType': {'AWS': 1, 'SLURM': 0},
-    'SecurityProtocol': {'GSI': 2, 'KERBEROS': 3, 'LOCAL': 5, 'OAUTH': 4, 'SSH_KEYS': 1, 'USERNAME_PASSWORD': 0},
-    'Status': {'ACTIVE': 0, 'APPROVED': 2, 'CONFIRMED': 1, 'DECLINED': 12, 'DELETED': 3, 'DENIED': 7, 'DUPLICATE': 4, 'EXPIRED': 13, 'GRACE_PERIOD': 5, 'INVITED': 6, 'PENDING': 8, 'PENDING_APPROVAL': 9, 'PENDING_CONFIRMATION': 10, 'SUSPENDED': 11},
-    'TaskState': {'CANCELED': 4, 'COMPLETED': 2, 'CREATED': 0, 'EXECUTING': 1, 'FAILED': 3},
-    'TaskTypes': {'DATA_STAGING': 1, 'ENV_CLEANUP': 3, 'ENV_SETUP': 0, 'JOB_SUBMISSION': 2, 'MONITORING': 4, 'OUTPUT_FETCHING': 5},
-}
-
-
-# The share/revoke set logic uses these as comparable sentinels (the sharing
-# facade is addressed by the member NAME, e.g. "WRITE"); a plain IntEnum mirroring
-# the historical Thrift values keeps the portal free of the Thrift model type.
-ResourcePermissionType = enum.IntEnum(
-    'ResourcePermissionType', _THRIFT_ENUM_INTS['ResourcePermissionType'])
-
 
 def user_has_access(request, resource_id, permission="WRITE"):
     """gRPC sharing access check (Track D — replaces the Thrift userHasAccess).
@@ -73,3106 +18,152 @@ def user_has_access(request, resource_id, permission="WRITE"):
         permission_type=permission)
 
 
-class FullyEncodedHyperlinkedIdentityField(
-        serializers.HyperlinkedIdentityField):
-    def get_url(self, obj, view_name, request, format):
-        if hasattr(obj, self.lookup_field):
-            lookup_value = getattr(obj, self.lookup_field)
-        else:
-            lookup_value = obj.get(self.lookup_field)
-        try:
-            encoded_lookup_value = quote(lookup_value, safe="")
-        except Exception:
-            log.warning(
-                "Failed to encode lookup_value [{}] for lookup_field "
-                "[{}] of object [{}]".format(
-                    lookup_value, self.lookup_field, obj))
-            raise
-        # Bit of a hack. Django's URL reversing does URL encoding but it
-        # doesn't encode all characters including some like '/' that are used
-        # in URL mappings.
-        kwargs = {self.lookup_url_kwarg: "__PLACEHOLDER__"}
-        url = self.reverse(view_name, kwargs=kwargs,
-                           request=request, format=format)
-        return url.replace("__PLACEHOLDER__", encoded_lookup_value)
-
-
-class UTCPosixTimestampDateTimeField(serializers.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 ProtoTimestampField(UTCPosixTimestampDateTimeField):
-    """Renders a protobuf int64 epoch-millis field as the same ISO timestamp the
-    Thrift-generated serializers produced.
-
-    proto3 scalar int fields default to ``0`` (never None), so an unset timestamp
-    reads as ``0``. When ``null_if_zero`` is set the field treats ``0`` as the
-    Thrift ``None`` and renders ``null`` (matching the old ``allow_null`` fields
-    whose adapters mapped ``pb.<time> or None``); otherwise ``0`` renders as the
-    epoch like the old non-nullable fields did.
-    """
-
-    def __init__(self, *args, null_if_zero=False, **kwargs):
-        self.null_if_zero = null_if_zero
-        if null_if_zero:
-            kwargs.setdefault('allow_null', True)
-        super().__init__(*args, **kwargs)
-
-    def to_representation(self, obj):
-        if self.null_if_zero and not obj:
-            return None
-        return super().to_representation(obj)
-
-
-class ProtoEnumNameField(serializers.Field):
-    """Renders a protobuf enum field as the enum member NAME, the same string the
-    Thrift-generated ``ThriftEnumField`` / ``EnumChoiceField`` emitted.
-
-    The instance is the protobuf message and ``source`` is the proto enum field
-    name; ``to_representation`` receives that field's integer value and resolves
-    it to the member name. Construct via :func:`proto_enum_name_field`, which
-    snapshots the proto enum descriptor into the plain ``by_number``/``by_name``
-    dicts this field holds (the descriptor itself cannot be deep-copied, and DRF
-    deep-copies field instances when binding them). ``proto_prefix`` strips a
-    proto-only member prefix (proto3 namespaces members that would otherwise
-    collide, e.g. ``NOTIFICATION_PRIORITY_LOW`` -> ``LOW``); the bare-named
-    ``*_UNKNOWN`` zero sentinel renders ``None`` to match the old nullable fields.
-    """
-
-    def __init__(self, by_number, by_name, proto_prefix='', **kwargs):
-        self._by_number = by_number
-        self._by_name = by_name
-        self.proto_prefix = proto_prefix
-        super().__init__(**kwargs)
-
-    def to_representation(self, value):
-        name = self._by_number[value]
-        if self.proto_prefix and name.startswith(self.proto_prefix):
-            name = name[len(self.proto_prefix):]
-        if name.endswith('UNKNOWN') and value == 0:
-            return None
-        return name
-
-    def to_internal_value(self, data):
-        # Writes pass the member name; map back to the proto integer value.
-        name = data
-        if self.proto_prefix and (self.proto_prefix + name) in self._by_name:
-            name = self.proto_prefix + name
-        try:
-            return self._by_name[name]
-        except KeyError:
-            self.fail('invalid_choice', input=data)
-
-
-def proto_enum_name_field(enum_descriptor, proto_prefix='', **kwargs):
-    """Build a :class:`ProtoEnumNameField` from a proto enum descriptor.
-
-    Snapshots the descriptor into plain int<->name dicts so the resulting field
-    is deep-copyable (DRF deep-copies fields when binding them to a serializer).
-    """
-    return ProtoEnumNameField(
-        by_number={v.number: v.name for v in enum_descriptor.values},
-        by_name={v.name: v.number for v in enum_descriptor.values},
-        proto_prefix=proto_prefix, **kwargs)
-
-
-class ProtoIntOrNoneField(serializers.IntegerField):
-    """Renders a protobuf int field as a raw integer, mapping the proto-0 default
-    to ``None`` to match the old auto-generated ``IntegerField`` whose adapter fed
-    it ``pb.<field> or None``.
-    """
-
-    def __init__(self, *args, **kwargs):
-        kwargs.setdefault('allow_null', True)
-        kwargs.setdefault('read_only', True)
-        super().__init__(*args, **kwargs)
-
-    def to_representation(self, value):
-        if not value:
-            return None
-        return super().to_representation(value)
-
-
-class ProtoEnumIntField(serializers.Field):
-    """Renders a protobuf enum field as the corresponding THRIFT enum integer.
-
-    Many serializers historically rendered an enum as a raw integer (an
-    auto-generated ``IntegerField``, not a name). proto and Thrift assign
-    different integers to the same member, so the adapter bridged by NAME to the
-    Thrift integer; this field does the same in one step via the
-    ``proto_to_thrift`` map (proto int -> Thrift int) built by
-    :func:`proto_enum_int_field`. proto-only members and the zero sentinel map to
-    ``None`` (these fields were nullable).
-    """
-
-    def __init__(self, proto_to_thrift, thrift_to_proto, **kwargs):
-        self._proto_to_thrift = proto_to_thrift
-        self._thrift_to_proto = thrift_to_proto
-        super().__init__(**kwargs)
-
-    def to_representation(self, value):
-        return self._proto_to_thrift.get(value)
-
-    def to_internal_value(self, data):
-        # Writes pass the Thrift integer; map it back to the proto integer.
-        return self._thrift_to_proto.get(int(data), 0)
-
-
-def proto_enum_int_field(enum_descriptor, thrift_ints, proto_prefix='',
-                         name_map=None, **kwargs):
-    """Build a :class:`ProtoEnumIntField` bridging a proto enum to the historical
-    Thrift enum INTEGER by member NAME (stripping ``proto_prefix`` from
-    proto-namespaced members). ``thrift_ints`` is the ``{member name: thrift int}``
-    table (see :data:`_THRIFT_ENUM_INTS`); members absent from it map to ``None``.
-
-    ``name_map`` overrides the proto-member-name -> Thrift-member-name mapping for
-    enums whose member names diverge beyond a simple prefix (proto3 namespacing of
-    colliding members, e.g. proto ``DATA_MOVEMENT_PROTOCOL_LOCAL`` -> Thrift
-    ``LOCAL``, proto ``JSP_CLOUD`` -> Thrift ``CLOUD``).
-    """
-    name_map = name_map or {}
-    proto_to_thrift = {}
-    thrift_to_proto = {}
-    for v in enum_descriptor.values:
-        if v.name in name_map:
-            name = name_map[v.name]
-        else:
-            name = v.name
-            if proto_prefix and name.startswith(proto_prefix):
-                name = name[len(proto_prefix):]
-        thrift_int = thrift_ints.get(name)
-        if thrift_int is not None:
-            proto_to_thrift[v.number] = thrift_int
-            thrift_to_proto[thrift_int] = v.number
-        else:
-            proto_to_thrift[v.number] = None
-    return ProtoEnumIntField(
-        proto_to_thrift=proto_to_thrift, thrift_to_proto=thrift_to_proto,
-        **kwargs)
-
-
-# proto enum member name -> Thrift member name for the protocol enums whose names
-# diverge beyond a simple prefix (proto3 namespaces colliding members). Used by
-# the compute/storage resource and resource-preference serializers.
-_JOB_SUBMISSION_PROTOCOL_NAME_MAP = {'JSP_CLOUD': 'CLOUD'}
-_DATA_MOVEMENT_PROTOCOL_NAME_MAP = {'DATA_MOVEMENT_PROTOCOL_LOCAL': 'LOCAL'}
-
-
-def job_submission_protocol_field(**kwargs):
-    """A :class:`ProtoEnumIntField` rendering proto ``JobSubmissionProtocol`` as
-    the Thrift integer (proto ``JSP_CLOUD`` -> Thrift ``CLOUD``)."""
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-        compute_resource_pb2,
-    )
-    return proto_enum_int_field(
-        compute_resource_pb2.JobSubmissionProtocol.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['JobSubmissionProtocol'],
-        proto_prefix='JOB_SUBMISSION_PROTOCOL_',
-        name_map=_JOB_SUBMISSION_PROTOCOL_NAME_MAP, **kwargs)
-
-
-def data_movement_protocol_field(**kwargs):
-    """A :class:`ProtoEnumIntField` rendering proto ``DataMovementProtocol`` as
-    the Thrift integer (proto ``DATA_MOVEMENT_PROTOCOL_LOCAL`` -> Thrift ``LOCAL``;
-    proto-only ``GRID_FTP`` -> None)."""
-    from airavata_sdk.generated.org.apache.airavata.model.data.movement import (
-        data_movement_pb2,
-    )
-    return proto_enum_int_field(
-        data_movement_pb2.DataMovementProtocol.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['DataMovementProtocol'],
-        proto_prefix='DATA_MOVEMENT_PROTOCOL_',
-        name_map=_DATA_MOVEMENT_PROTOCOL_NAME_MAP, **kwargs)
-
-
-class ProtoFileSystemsMapField(serializers.Field):
-    """Renders a proto ``map<int32, string>`` whose int key holds a proto
-    ``FileSystems`` value as the ``{Thrift FileSystems int (as a string): path}``
-    dict the old i32-keyed Thrift map produced.
-
-    proto and Thrift assign different integers to the same member (proto HOME=1
-    vs Thrift HOME=0), so the key is bridged by NAME; the JSON key is the Thrift
-    integer as a string (DRF ``DictField`` rendered ``str(key)`` for the i32 map).
-    """
-
-    def __init__(self, **kwargs):
-        kwargs.setdefault('read_only', True)
-        super().__init__(**kwargs)
-        self._proto_to_thrift = None
-
-    def _key_map(self):
-        if self._proto_to_thrift is None:
-            from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-                compute_resource_pb2,
-            )
-            proto_fs = compute_resource_pb2.FileSystems
-            thrift_fs = _THRIFT_ENUM_INTS['FileSystems']
-            self._proto_to_thrift = {
-                proto_fs.Value(name): thrift_fs[name]
-                for name in proto_fs.keys() if name in thrift_fs
-            }
-        return self._proto_to_thrift
-
-    def to_representation(self, value):
-        key_map = self._key_map()
-        return {
-            str(key_map[k]): v for k, v in value.items() if k in key_map}
-
-
-class ProtoEnumKeyedMapField(serializers.Field):
-    """Renders a proto ``map<int32, string>`` whose int key holds a proto enum
-    value as the ``{str(Thrift enum member): value}`` dict the old enum-keyed
-    Thrift map produced (e.g. ``{'JobManagerCommand.SUBMISSION': 'sbatch'}``).
-
-    Build via :func:`proto_enum_keyed_map_field`, which snapshots the proto-int ->
-    Thrift-member-str mapping. (Unlike ``ProtoFileSystemsMapField``, which mirrors
-    an i32-keyed Thrift map and emits the digit, this mirrors an enum-keyed Thrift
-    map and emits ``str(member)``.)
-    """
-
-    def __init__(self, key_labels, **kwargs):
-        self._key_labels = key_labels
-        kwargs.setdefault('read_only', True)
-        super().__init__(**kwargs)
-
-    def to_representation(self, value):
-        return {
-            self._key_labels[k]: v
-            for k, v in value.items() if k in self._key_labels}
-
-
-def proto_enum_keyed_map_field(proto_enum_descriptor, thrift_enum_name,
-                               thrift_ints, **kwargs):
-    """Build a :class:`ProtoEnumKeyedMapField` mapping each proto enum int key to
-    ``"<ThriftEnum>.<member>"`` — the string the old enum-keyed Thrift map's
-    ``DictField`` rendered (``str`` of a Thrift ``IntEnum`` member) — by member
-    name; members absent from ``thrift_ints`` (unknown/zero sentinels) are dropped.
-    """
-    key_labels = {}
-    for v in proto_enum_descriptor.values:
-        if v.name in thrift_ints:
-            key_labels[v.number] = "{}.{}".format(thrift_enum_name, v.name)
-    return ProtoEnumKeyedMapField(key_labels=key_labels, **kwargs)
-
-
-class StoredJSONField(serializers.JSONField):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-
-    def to_representation(self, value):
-        try:
-            if value:
-                return json.loads(value)
-            else:
-                return value
-        except Exception:
-            return value
-
-    def to_internal_value(self, data):
-        try:
-            return json.dumps(data)
-        except (TypeError, ValueError):
-            self.fail('invalid')
-
-
-class ProtoStoredJSONField(StoredJSONField):
-    """:class:`StoredJSONField` for a proto string field: the proto-empty default
-    ``''`` renders ``null`` (the old adapters mapped ``pb.meta_data or None``)."""
-
-    def to_representation(self, value):
-        if not value:
-            return None
-        return super().to_representation(value)
-
-
-class OrderedListField(serializers.ListField):
-
-    def __init__(self, *args, **kwargs):
-        self.order_by = kwargs.pop('order_by', None)
-        super().__init__(*args, **kwargs)
-
-    def to_representation(self, instance):
-        rep = super().to_representation(instance)
-        if rep is not None:
-            rep.sort(key=lambda item: item[self.order_by])
-        return rep
-
-    def to_internal_value(self, data):
-        validated_data = super().to_internal_value(data)
-        # Update order field based on order in array
-        items = validated_data if validated_data else []
-        for i in range(len(items)):
-            items[i][self.order_by] = i
-        return validated_data
-
-
-def _group_manager_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.group import (
-        group_manager_pb2,
-    )
-    return group_manager_pb2
-
-
-class GroupSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``GroupModel`` message.
-
-    On update the added/removed member/admin diffs the view needs are stashed on
-    the serializer instance (``self.added_members`` etc.) — a protobuf message
-    can't carry arbitrary attributes the way the old Thrift instance did.
-    """
-
-    id = serializers.CharField(read_only=True)
-    name = serializers.CharField()
-    ownerId = serializers.CharField(source='owner_id', read_only=True)
-    description = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    members = serializers.ListField(
-        child=serializers.CharField(), required=False)
-    admins = serializers.ListField(
-        child=serializers.CharField(), required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:group-detail',
-        lookup_field='id',
-        lookup_url_kwarg='group_id')
-    isAdmin = serializers.SerializerMethodField()
-    isOwner = serializers.SerializerMethodField()
-    isMember = serializers.SerializerMethodField()
-    isGatewayAdminsGroup = serializers.SerializerMethodField()
-    isReadOnlyGatewayAdminsGroup = serializers.SerializerMethodField()
-    isDefaultGatewayUsersGroup = serializers.SerializerMethodField()
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        if ret.get('description') == '':
-            ret['description'] = None
-        return ret
-
-    def create(self, validated_data):
-        return _group_manager_pb2().GroupModel(
-            name=validated_data.get('name', '') or '',
-            description=validated_data.get('description', '') or '',
-            members=list(validated_data.get('members', []) or []),
-            admins=list(validated_data.get('admins', []) or []),
-            owner_id=(self.context['request'].user.username + "@" +
-                      settings.GATEWAY_ID),
-        )
-
-    def update(self, instance, validated_data):
-        if 'name' in validated_data:
-            instance.name = validated_data['name'] or ''
-        if 'description' in validated_data:
-            instance.description = validated_data['description'] or ''
-        # Calculate added and removed members/admins; stash on the serializer for
-        # the view (a proto message can't carry the old Thrift ``_added_*`` attrs).
-        old_members = set(instance.members)
-        new_members = set(validated_data.get('members', instance.members))
-        self.removed_members = list(old_members - new_members)
-        self.added_members = list(new_members - old_members)
-        instance.members[:] = list(validated_data.get('members', instance.members))
-        old_admins = set(instance.admins)
-        new_admins = set(validated_data.get('admins', instance.admins))
-        self.removed_admins = list(old_admins - new_admins)
-        self.added_admins = list(new_admins - old_admins)
-        instance.admins[:] = list(validated_data.get('admins', instance.admins))
-        # Add new admins that aren't members to the added_members list.
-        extra = list(new_admins - new_members)
-        self.added_members.extend(extra)
-        instance.members.extend(extra)
-        return instance
-
-    def get_isAdmin(self, group):
-        request = self.context['request']
-        return request.airavata.sharing.gm_has_admin_access(
-            group.id,
-            request.user.username + "@" + settings.GATEWAY_ID)
-
-    def get_isOwner(self, group):
-        request = self.context['request']
-        return group.owner_id == (request.user.username +
-                                  "@" +
-                                  settings.GATEWAY_ID)
-
-    def get_isMember(self, group):
-        request = self.context['request']
-        username = request.user.username + "@" + settings.GATEWAY_ID
-        return group.members and username in group.members
-
-    def get_isGatewayAdminsGroup(self, group):
-        return group.id == self._gateway_groups()['adminsGroupId']
-
-    def get_isReadOnlyGatewayAdminsGroup(self, group):
-        return group.id == self._gateway_groups()['readOnlyAdminsGroupId']
-
-    def get_isDefaultGatewayUsersGroup(self, group):
-        return group.id == self._gateway_groups()['defaultGatewayUsersGroupId']
-
-    def _gateway_groups(self):
-        request = self.context['request']
-        # gateway_groups_middleware sets this session variable
-        if 'GATEWAY_GROUPS' in request.session:
-            return request.session['GATEWAY_GROUPS']
-        else:
-            gg = request.airavata.compute.get_gateway_groups()
-            return {
-                'adminsGroupId': gg.admins_group_id,
-                'readOnlyAdminsGroupId': gg.read_only_admins_group_id,
-                'defaultGatewayUsersGroupId': gg.default_gateway_users_group_id,
-            }
-
-
-class ProjectSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``Project`` message.
-
-    Reads protobuf fields directly (``project_id``, ``creation_time``, ...) and
-    emits the historical Thrift-named JSON keys (``projectID``, ``creationTime``,
-    ...) so the REST contract with the Vue frontend is unchanged. ``save()``
-    returns a proto ``Project`` the view passes straight to the gRPC facade.
-    """
-
-    projectID = serializers.CharField(source='project_id', read_only=True)
-    owner = serializers.CharField(read_only=True)
-    gatewayId = serializers.CharField(source='gateway_id', read_only=True)
-    name = serializers.CharField()
-    description = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoTimestampField(
-        source='creation_time', null_if_zero=True, read_only=True)
-    sharedUsers = serializers.ListField(
-        source='shared_users', child=serializers.CharField(),
-        read_only=True)
-    sharedGroups = serializers.ListField(
-        source='shared_groups', child=serializers.CharField(),
-        read_only=True)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:project-detail',
-        lookup_field='project_id',
-        lookup_url_kwarg='project_id')
-    experiments = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:project-experiments',
-        lookup_field='project_id',
-        lookup_url_kwarg='project_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-    isOwner = serializers.SerializerMethodField()
-
-    def create(self, validated_data):
-        from airavata_sdk.generated.org.apache.airavata.model.workspace import (
-            workspace_pb2,
-        )
-        return workspace_pb2.Project(
-            owner=validated_data.get('owner', '') or '',
-            gateway_id=validated_data.get('gateway_id', '') or '',
-            name=validated_data.get('name', '') or '',
-            description=validated_data.get('description', '') or '',
-        )
-
-    def update(self, instance, validated_data):
-        if 'name' in validated_data:
-            instance.name = validated_data['name'] or ''
-        if 'description' in validated_data:
-            instance.description = validated_data['description'] or ''
-        return instance
-
-    def get_userHasWriteAccess(self, project):
-        return user_has_access(self.context['request'], project.project_id)
-
-    def get_isOwner(self, project):
-        request = self.context['request']
-        return project.owner == request.user.username
-
-
-class ApplicationModuleSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ApplicationModule`` message.
-
-    Reads the protobuf directly and emits the historical Thrift-named JSON keys.
-    ``save()`` returns a proto ``ApplicationModule`` the view passes to the facade.
-    """
-
-    appModuleId = serializers.CharField(source='app_module_id', read_only=True)
-    appModuleName = serializers.CharField(source='app_module_name')
-    appModuleVersion = serializers.CharField(
-        source='app_module_version', allow_blank=True, allow_null=True,
-        required=False)
-    appModuleDescription = serializers.CharField(
-        source='app_module_description', allow_blank=True, allow_null=True,
-        required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-detail',
-        lookup_field='app_module_id',
-        lookup_url_kwarg='app_module_id')
-    applicationInterface = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-application-interface',
-        lookup_field='app_module_id',
-        lookup_url_kwarg='app_module_id')
-    applicationDeployments = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-application-deployments',
-        lookup_field='app_module_id',
-        lookup_url_kwarg='app_module_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def create(self, validated_data):
-        from airavata_sdk.generated.org.apache.airavata.model.appcatalog.appdeployment import (  # noqa: E501
-            app_deployment_pb2,
-        )
-        return app_deployment_pb2.ApplicationModule(
-            app_module_name=validated_data.get('app_module_name', '') or '',
-            app_module_version=validated_data.get('app_module_version', '') or '',
-            app_module_description=validated_data.get(
-                'app_module_description', '') or '',
-        )
-
-    def update(self, instance, validated_data):
-        if 'app_module_name' in validated_data:
-            instance.app_module_name = validated_data['app_module_name'] or ''
-        if 'app_module_version' in validated_data:
-            instance.app_module_version = (
-                validated_data['app_module_version'] or '')
-        if 'app_module_description' in validated_data:
-            instance.app_module_description = (
-                validated_data['app_module_description'] or '')
-        return instance
-
-    def get_userHasWriteAccess(self, appModule):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-
-class EnumChoiceField(serializers.ChoiceField):
-    def __init__(self, enum_class, **kwargs):
-        self.enum_class = enum_class
-        kwargs['choices'] = [(member.name, member.name) for member in enum_class]
-        super().__init__(**kwargs)
-
-    def to_internal_value(self, data):
-        if isinstance(data, int):
-            try:
-                return self.enum_class(data)
-            except ValueError:
-                self.fail('invalid_choice', input=data)
-        try:
-            return self.enum_class[data]
-        except KeyError:
-            self.fail('invalid_choice', input=data)
-
-    def to_representation(self, value):
-        return value.name
-
-
-def _application_io_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.application.io import (
-        application_io_pb2,
-    )
-    return application_io_pb2
-
-
-def _app_interface_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.appinterface import (  # noqa: E501
-        app_interface_pb2,
-    )
-    return app_interface_pb2
-
-
-def _data_type_field(**kwargs):
-    # DataType renders as the member NAME (proto STRING/INTEGER/... == Thrift names;
-    # proto prefixes only the zero DATA_TYPE_UNKNOWN sentinel).
-    return proto_enum_name_field(
-        _application_io_pb2().DataType.DESCRIPTOR,
-        proto_prefix='DATA_TYPE_', **kwargs)
-
-
-def _proto_input_data_object(d):
-    io = _application_io_pb2()
-    return io.InputDataObjectType(
-        name=d.get('name', '') or '',
-        value=d.get('value', '') or '',
-        type=d.get('type', 0) or 0,
-        application_argument=d.get('application_argument', '') or '',
-        standard_input=bool(d.get('standard_input', False)),
-        user_friendly_description=d.get('user_friendly_description', '') or '',
-        meta_data=d.get('meta_data', '') or '',
-        input_order=d.get('input_order', 0) or 0,
-        is_required=bool(d.get('is_required', False)),
-        required_to_added_to_command_line=bool(
-            d.get('required_to_added_to_command_line', False)),
-        data_staged=bool(d.get('data_staged', False)),
-        storage_resource_id=d.get('storage_resource_id', '') or '',
-        is_read_only=bool(d.get('is_read_only', False)),
-        override_filename=d.get('override_filename', '') or '',
-    )
-
-
-def _proto_output_data_object(d):
-    io = _application_io_pb2()
-    return io.OutputDataObjectType(
-        name=d.get('name', '') or '',
-        value=d.get('value', '') or '',
-        type=d.get('type', 0) or 0,
-        application_argument=d.get('application_argument', '') or '',
-        is_required=bool(d.get('is_required', False)),
-        required_to_added_to_command_line=bool(
-            d.get('required_to_added_to_command_line', False)),
-        data_movement=bool(d.get('data_movement', False)),
-        location=d.get('location', '') or '',
-        search_query=d.get('search_query', '') or '',
-        output_streaming=bool(d.get('output_streaming', False)),
-        storage_resource_id=d.get('storage_resource_id', '') or '',
-        meta_data=d.get('meta_data', '') or '',
-    )
-
-
-class InputDataObjectTypeSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``InputDataObjectType`` message."""
-
-    name = serializers.CharField()
-    value = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    type = _data_type_field(required=False)
-    applicationArgument = serializers.CharField(source='application_argument', allow_blank=True, allow_null=True, required=False)
-    standardInput = serializers.BooleanField(source='standard_input', default=False)
-    userFriendlyDescription = serializers.CharField(source='user_friendly_description', allow_blank=True, allow_null=True, required=False)
-    metaData = ProtoStoredJSONField(source='meta_data', allow_null=True, required=False)
-    inputOrder = serializers.IntegerField(source='input_order', required=False, allow_null=True)
-    isRequired = serializers.BooleanField(source='is_required', default=False)
-    requiredToAddedToCommandLine = serializers.BooleanField(source='required_to_added_to_command_line', default=False)
-    dataStaged = serializers.BooleanField(source='data_staged', default=False)
-    storageResourceId = serializers.CharField(source='storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    isReadOnly = serializers.BooleanField(source='is_read_only', default=False)
-    overrideFilename = serializers.CharField(source='override_filename', allow_blank=True, allow_null=True, required=False)
-
-    def create(self, validated_data):
-        return _proto_input_data_object(validated_data)
-
-
-class OutputDataObjectTypeSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``OutputDataObjectType`` message."""
-
-    name = serializers.CharField()
-    value = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    type = _data_type_field(required=False)
-    applicationArgument = serializers.CharField(source='application_argument', allow_blank=True, allow_null=True, required=False)
-    isRequired = serializers.BooleanField(source='is_required', default=False)
-    requiredToAddedToCommandLine = serializers.BooleanField(source='required_to_added_to_command_line', default=False)
-    dataMovement = serializers.BooleanField(source='data_movement', default=False)
-    location = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    searchQuery = serializers.CharField(source='search_query', allow_blank=True, allow_null=True, required=False)
-    outputStreaming = serializers.BooleanField(source='output_streaming', default=False)
-    storageResourceId = serializers.CharField(source='storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    metaData = ProtoStoredJSONField(source='meta_data', allow_null=True, required=False)
-
-    def create(self, validated_data):
-        return _proto_output_data_object(validated_data)
-
-
-class ApplicationInterfaceDescriptionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ApplicationInterfaceDescription``."""
-
-    applicationInterfaceId = serializers.CharField(source='application_interface_id', required=False, allow_null=True, allow_blank=True)
-    applicationName = serializers.CharField(source='application_name')
-    applicationDescription = serializers.CharField(source='application_description', allow_blank=True, allow_null=True, required=False)
-    applicationModules = serializers.ListField(source='application_modules', child=serializers.CharField(), allow_null=True, required=False)
-    applicationInputs = InputDataObjectTypeSerializer(source='application_inputs', many=True, allow_null=True, required=False)
-    applicationOutputs = OutputDataObjectTypeSerializer(source='application_outputs', many=True, allow_null=True, required=False)
-    archiveWorkingDirectory = serializers.BooleanField(source='archive_working_directory', default=False)
-    hasOptionalFileInputs = serializers.BooleanField(source='has_optional_file_inputs', default=False, read_only=True)
-
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-interface-detail',
-        lookup_field='application_interface_id',
-        lookup_url_kwarg='app_interface_id', read_only=True)
-    userHasWriteAccess = serializers.SerializerMethodField()
-    showQueueSettings = serializers.BooleanField(required=False)
-    queueSettingsCalculatorId = serializers.CharField(allow_null=True, required=False)
-
-    def create(self, validated_data):
-        io = _application_io_pb2()  # noqa: F841 (imported for side-effect symmetry)
-        inputs_data = validated_data.pop('application_inputs', None)
-        outputs_data = validated_data.pop('application_outputs', None)
-        # Remove Django-specific (non-proto) fields.
-        validated_data.pop('showQueueSettings', None)
-        validated_data.pop('queueSettingsCalculatorId', None)
-        ai = _app_interface_pb2()
-        application_interface = ai.ApplicationInterfaceDescription(
-            application_interface_id=validated_data.get(
-                'application_interface_id', '') or '',
-            application_name=validated_data.get('application_name', '') or '',
-            application_description=validated_data.get(
-                'application_description', '') or '',
-            application_modules=list(
-                validated_data.get('application_modules', []) or []),
-            archive_working_directory=bool(
-                validated_data.get('archive_working_directory', False)),
-        )
-        if inputs_data is not None:
-            application_interface.application_inputs.extend(
-                _proto_input_data_object(inp) for inp in inputs_data)
-        if outputs_data is not None:
-            application_interface.application_outputs.extend(
-                _proto_output_data_object(out) for out in outputs_data)
-        return application_interface
-
-    def update(self, instance, validated_data):
-        defaults = {}
-        if "showQueueSettings" in validated_data:
-            defaults["show_queue_settings"] = validated_data.pop("showQueueSettings")
-        if "queueSettingsCalculatorId" in validated_data:
-            defaults["queue_settings_calculator_id"] = validated_data.pop("queueSettingsCalculatorId")
-        application_module_id = instance.application_modules[0]
-        if defaults:
-            models.ApplicationSettings.objects.update_or_create(
-                application_module_id=application_module_id, defaults=defaults
-            )
-
-        inputs_data = validated_data.pop('application_inputs', None)
-        outputs_data = validated_data.pop('application_outputs', None)
-
-        for proto_field in ('application_interface_id', 'application_name',
-                            'application_description', 'archive_working_directory'):
-            if proto_field in validated_data:
-                value = validated_data[proto_field]
-                if proto_field == 'archive_working_directory':
-                    value = bool(value)
-                else:
-                    value = value or ''
-                setattr(instance, proto_field, value)
-        if 'application_modules' in validated_data:
-            instance.application_modules[:] = list(
-                validated_data['application_modules'] or [])
-
-        if inputs_data is not None:
-            del instance.application_inputs[:]
-            instance.application_inputs.extend(
-                _proto_input_data_object(inp) for inp in inputs_data)
-        if outputs_data is not None:
-            del instance.application_outputs[:]
-            instance.application_outputs.extend(
-                _proto_output_data_object(out) for out in outputs_data)
-
-        return instance
-
-    def get_userHasWriteAccess(self, appInterface):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-
-def _app_deployment_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.appdeployment import (  # noqa: E501
-        app_deployment_pb2,
-    )
-    return app_deployment_pb2
-
-
-def _parallelism_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.parallelism import (
-        parallelism_pb2,
-    )
-    return proto_enum_int_field(
-        parallelism_pb2.ApplicationParallelismType.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ApplicationParallelismType'],
-        proto_prefix='APPLICATION_PARALLELISM_TYPE_', **kwargs)
-
-
-class CommandObjectSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``CommandObject`` message."""
-
-    command = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    commandOrder = serializers.IntegerField(
-        source='command_order', allow_null=True, required=False)
-
-    def create(self, validated_data):
-        return _app_deployment_pb2().CommandObject(
-            command=validated_data.get('command', '') or '',
-            command_order=validated_data.get('command_order', 0) or 0,
-        )
-
-
-class SetEnvPathsSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``SetEnvPaths`` message."""
-
-    name = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    value = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    envPathOrder = serializers.IntegerField(
-        source='env_path_order', allow_null=True, required=False)
-
-    def create(self, validated_data):
-        return _app_deployment_pb2().SetEnvPaths(
-            name=validated_data.get('name', '') or '',
-            value=validated_data.get('value', '') or '',
-            env_path_order=validated_data.get('env_path_order', 0) or 0,
-        )
-
-
-class ApplicationDeploymentDescriptionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ApplicationDeploymentDescription``."""
-
-    appDeploymentId = serializers.CharField(
-        source='app_deployment_id', allow_blank=True, allow_null=True,
-        required=False)
-    appModuleId = serializers.CharField(
-        source='app_module_id', allow_blank=True, allow_null=True,
-        required=False)
-    computeHostId = serializers.CharField(
-        source='compute_host_id', allow_blank=True, allow_null=True,
-        required=False)
-    executablePath = serializers.CharField(
-        source='executable_path', allow_blank=True, allow_null=True,
-        required=False)
-    parallelism = _parallelism_field(required=False, allow_null=True)
-    appDeploymentDescription = serializers.CharField(
-        source='app_deployment_description', allow_blank=True, allow_null=True,
-        required=False)
-    moduleLoadCmds = OrderedListField(
-        source='module_load_cmds', order_by='commandOrder',
-        child=CommandObjectSerializer(), allow_null=True, required=False)
-    libPrependPaths = OrderedListField(
-        source='lib_prepend_paths', order_by='envPathOrder',
-        child=SetEnvPathsSerializer(), allow_null=True, required=False)
-    libAppendPaths = OrderedListField(
-        source='lib_append_paths', order_by='envPathOrder',
-        child=SetEnvPathsSerializer(), allow_null=True, required=False)
-    setEnvironment = OrderedListField(
-        source='set_environment', order_by='envPathOrder',
-        child=SetEnvPathsSerializer(), allow_null=True, required=False)
-    preJobCommands = OrderedListField(
-        source='pre_job_commands', order_by='commandOrder',
-        child=CommandObjectSerializer(), allow_null=True, required=False)
-    postJobCommands = OrderedListField(
-        source='post_job_commands', order_by='commandOrder',
-        child=CommandObjectSerializer(), allow_null=True, required=False)
-    defaultQueueName = serializers.CharField(
-        source='default_queue_name', allow_blank=True, allow_null=True,
-        required=False)
-    defaultNodeCount = ProtoIntOrNoneField(source='default_node_count')
-    defaultCPUCount = ProtoIntOrNoneField(source='default_cpu_count')
-    defaultWalltime = ProtoIntOrNoneField(source='default_walltime')
-    editableByUser = serializers.BooleanField(
-        source='editable_by_user', required=False, default=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-deployment-detail',
-        lookup_field='app_deployment_id',
-        lookup_url_kwarg='app_deployment_id')
-    queues = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:application-deployment-queues',
-        lookup_field='app_deployment_id',
-        lookup_url_kwarg='app_deployment_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        # The proto string default '' must render as null for the optional
-        # default-queue-name field (the old adapter mapped pb.<f> or None).
-        if ret.get('defaultQueueName') == '':
-            ret['defaultQueueName'] = None
-        return ret
-
-    def get_userHasWriteAccess(self, appDeployment):
-        return user_has_access(
-            self.context['request'], appDeployment.app_deployment_id)
-
-    def create(self, validated_data):
-        a = _app_deployment_pb2()
-        return a.ApplicationDeploymentDescription(
-            app_module_id=validated_data.get('app_module_id', '') or '',
-            compute_host_id=validated_data.get('compute_host_id', '') or '',
-            executable_path=validated_data.get('executable_path', '') or '',
-            parallelism=validated_data.get('parallelism', 0) or 0,
-            app_deployment_description=validated_data.get(
-                'app_deployment_description', '') or '',
-            module_load_cmds=[
-                CommandObjectSerializer().create(c)
-                for c in validated_data.get('module_load_cmds', []) or []],
-            lib_prepend_paths=[
-                SetEnvPathsSerializer().create(p)
-                for p in validated_data.get('lib_prepend_paths', []) or []],
-            lib_append_paths=[
-                SetEnvPathsSerializer().create(p)
-                for p in validated_data.get('lib_append_paths', []) or []],
-            set_environment=[
-                SetEnvPathsSerializer().create(p)
-                for p in validated_data.get('set_environment', []) or []],
-            pre_job_commands=[
-                CommandObjectSerializer().create(c)
-                for c in validated_data.get('pre_job_commands', []) or []],
-            post_job_commands=[
-                CommandObjectSerializer().create(c)
-                for c in validated_data.get('post_job_commands', []) or []],
-            default_queue_name=validated_data.get('default_queue_name', '') or '',
-            default_node_count=validated_data.get('default_node_count', 0) or 0,
-            default_cpu_count=validated_data.get('default_cpu_count', 0) or 0,
-            default_walltime=validated_data.get('default_walltime', 0) or 0,
-            editable_by_user=bool(validated_data.get('editable_by_user', False)),
-        )
-
-    def update(self, instance, validated_data):
-        return self.create(validated_data)
-
-
-class BatchQueueSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``BatchQueue`` message."""
-
-    queueName = serializers.CharField(
-        source='queue_name', allow_blank=True, allow_null=True, required=False)
-    queueDescription = serializers.CharField(
-        source='queue_description', allow_blank=True, allow_null=True,
-        required=False)
-    maxRunTime = serializers.IntegerField(
-        source='max_run_time', allow_null=True, required=False)
-    maxNodes = serializers.IntegerField(
-        source='max_nodes', allow_null=True, required=False)
-    maxProcessors = serializers.IntegerField(
-        source='max_processors', allow_null=True, required=False)
-    maxJobsInQueue = serializers.IntegerField(
-        source='max_jobs_in_queue', allow_null=True, required=False)
-    maxMemory = serializers.IntegerField(
-        source='max_memory', allow_null=True, required=False)
-    cpuPerNode = serializers.IntegerField(
-        source='cpu_per_node', allow_null=True, required=False)
-    defaultNodeCount = serializers.IntegerField(
-        source='default_node_count', allow_null=True, required=False)
-    defaultCPUCount = serializers.IntegerField(
-        source='default_cpu_count', allow_null=True, required=False)
-    defaultWalltime = serializers.IntegerField(
-        source='default_walltime', allow_null=True, required=False)
-    queueSpecificMacros = serializers.CharField(
-        source='queue_specific_macros', allow_blank=True, allow_null=True,
-        required=False)
-    isDefaultQueue = serializers.BooleanField(
-        source='is_default_queue', required=False, default=False)
-
-
-class JobSubmissionInterfaceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``JobSubmissionInterface`` message."""
-
-    jobSubmissionInterfaceId = serializers.CharField(
-        source='job_submission_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    jobSubmissionProtocol = job_submission_protocol_field(
-        source='job_submission_protocol', required=False, allow_null=True)
-    priorityOrder = serializers.IntegerField(
-        source='priority_order', allow_null=True, required=False)
-
-
-class DataMovementInterfaceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``DataMovementInterface`` message."""
-
-    dataMovementInterfaceId = serializers.CharField(
-        source='data_movement_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    dataMovementProtocol = data_movement_protocol_field(
-        source='data_movement_protocol', required=False, allow_null=True)
-    priorityOrder = serializers.IntegerField(
-        source='priority_order', allow_null=True, required=False)
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-    updateTime = ProtoIntOrNoneField(source='update_time')
-    storageResourceId = serializers.CharField(
-        source='storage_resource_id', allow_blank=True, allow_null=True,
-        required=False)
-
-
-class ComputeResourceDescriptionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ComputeResourceDescription`` message."""
-
-    computeResourceId = serializers.CharField(
-        source='compute_resource_id', allow_blank=True, allow_null=True,
-        required=False)
-    hostName = serializers.CharField(
-        source='host_name', allow_blank=True, allow_null=True, required=False)
-    hostAliases = serializers.ListField(
-        source='host_aliases', child=serializers.CharField(), required=False)
-    ipAddresses = serializers.ListField(
-        source='ip_addresses', child=serializers.CharField(), required=False)
-    resourceDescription = serializers.CharField(
-        source='resource_description', allow_blank=True, allow_null=True,
-        required=False)
-    enabled = serializers.BooleanField(required=False, default=False)
-    batchQueues = BatchQueueSerializer(
-        source='batch_queues', many=True, required=False)
-    fileSystems = ProtoFileSystemsMapField(source='file_systems', required=False)
-    jobSubmissionInterfaces = JobSubmissionInterfaceSerializer(
-        source='job_submission_interfaces', many=True, required=False)
-    dataMovementInterfaces = DataMovementInterfaceSerializer(
-        source='data_movement_interfaces', many=True, required=False)
-    maxMemoryPerNode = serializers.IntegerField(
-        source='max_memory_per_node', allow_null=True, required=False)
-    gatewayUsageReporting = serializers.BooleanField(
-        source='gateway_usage_reporting', required=False, default=False)
-    gatewayUsageModuleLoadCommand = serializers.CharField(
-        source='gateway_usage_module_load_command', allow_blank=True,
-        allow_null=True, required=False)
-    gatewayUsageExecutable = serializers.CharField(
-        source='gateway_usage_executable', allow_blank=True, allow_null=True,
-        required=False)
-    cpusPerNode = serializers.IntegerField(
-        source='cpus_per_node', allow_null=True, required=False)
-    defaultNodeCount = serializers.IntegerField(
-        source='default_node_count', allow_null=True, required=False)
-    defaultCPUCount = serializers.IntegerField(
-        source='default_cpu_count', allow_null=True, required=False)
-    defaultWalltime = serializers.IntegerField(
-        source='default_walltime', allow_null=True, required=False)
-
-
-# --- Per-protocol job-submission / data-movement interface details ----------
-# Admin-only detail views rendering a single protocol's submission/movement model.
-# SecurityProtocol/ResourceJobManagerType/ProviderName are prefix-aligned; MonitorMode
-# names diverge (proto MONITOR_FORK/MONITOR_LOCAL -> Thrift FORK/LOCAL).
-_MONITOR_MODE_NAME_MAP = {'MONITOR_FORK': 'FORK', 'MONITOR_LOCAL': 'LOCAL'}
-
-
-def _security_protocol_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.data.movement import (
-        data_movement_pb2,
-    )
-    return proto_enum_int_field(
-        data_movement_pb2.SecurityProtocol.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['SecurityProtocol'],
-        proto_prefix='SECURITY_PROTOCOL_', **kwargs)
-
-
-def _resource_job_manager_type_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-        compute_resource_pb2,
-    )
-    return proto_enum_int_field(
-        compute_resource_pb2.ResourceJobManagerType.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ResourceJobManagerType'],
-        proto_prefix='RESOURCE_JOB_MANAGER_TYPE_', **kwargs)
-
-
-def _provider_name_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-        compute_resource_pb2,
-    )
-    return proto_enum_int_field(
-        compute_resource_pb2.ProviderName.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ProviderName'],
-        proto_prefix='PROVIDER_NAME_', **kwargs)
-
-
-def _monitor_mode_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-        compute_resource_pb2,
-    )
-    return proto_enum_int_field(
-        compute_resource_pb2.MonitorMode.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['MonitorMode'],
-        name_map=_MONITOR_MODE_NAME_MAP, **kwargs)
-
-
-class ResourceJobManagerSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ResourceJobManager`` message."""
-
-    resourceJobManagerId = serializers.CharField(
-        source='resource_job_manager_id', allow_blank=True, allow_null=True,
-        required=False)
-    resourceJobManagerType = _resource_job_manager_type_field(
-        source='resource_job_manager_type', required=False, allow_null=True)
-    pushMonitoringEndpoint = serializers.CharField(
-        source='push_monitoring_endpoint', allow_blank=True, allow_null=True,
-        required=False)
-    jobManagerBinPath = serializers.CharField(
-        source='job_manager_bin_path', allow_blank=True, allow_null=True,
-        required=False)
-    jobManagerCommands = serializers.SerializerMethodField()
-    parallelismPrefix = serializers.SerializerMethodField()
-
-    def get_jobManagerCommands(self, pb):
-        from airavata_sdk.generated.org.apache.airavata.model.appcatalog.computeresource import (  # noqa: E501
-            compute_resource_pb2,
-        )
-        return proto_enum_keyed_map_field(
-            compute_resource_pb2.JobManagerCommand.DESCRIPTOR,
-            'JobManagerCommand', _THRIFT_ENUM_INTS['JobManagerCommand'],
-        ).to_representation(pb.job_manager_commands)
-
-    def get_parallelismPrefix(self, pb):
-        from airavata_sdk.generated.org.apache.airavata.model.parallelism import (
-            parallelism_pb2,
-        )
-        return proto_enum_keyed_map_field(
-            parallelism_pb2.ApplicationParallelismType.DESCRIPTOR,
-            'ApplicationParallelismType',
-            _THRIFT_ENUM_INTS['ApplicationParallelismType'],
-        ).to_representation(pb.parallelism_prefix)
-
-
-class LocalJobSubmissionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``LOCALSubmission`` message."""
-
-    jobSubmissionInterfaceId = serializers.CharField(
-        source='job_submission_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    resourceJobManager = ResourceJobManagerSerializer(
-        source='resource_job_manager', required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-
-
-class SshJobSubmissionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``SSHJobSubmission`` message."""
-
-    jobSubmissionInterfaceId = serializers.CharField(
-        source='job_submission_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-    resourceJobManager = ResourceJobManagerSerializer(
-        source='resource_job_manager', required=False)
-    alternativeSSHHostName = serializers.CharField(
-        source='alternative_ssh_host_name', allow_blank=True, allow_null=True,
-        required=False)
-    sshPort = ProtoIntOrNoneField(source='ssh_port')
-    monitorMode = _monitor_mode_field(
-        source='monitor_mode', required=False, allow_null=True)
-    batchQueueEmailSenders = serializers.ListField(
-        source='batch_queue_email_senders', child=serializers.CharField(),
-        required=False)
-
-
-class CloudJobSubmissionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``CloudJobSubmission`` message."""
-
-    jobSubmissionInterfaceId = serializers.CharField(
-        source='job_submission_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-    nodeId = serializers.CharField(
-        source='node_id', allow_blank=True, allow_null=True, required=False)
-    executableType = serializers.CharField(
-        source='executable_type', allow_blank=True, allow_null=True,
-        required=False)
-    providerName = _provider_name_field(
-        source='provider_name', required=False, allow_null=True)
-    userAccountName = serializers.CharField(
-        source='user_account_name', allow_blank=True, allow_null=True,
-        required=False)
-
-
-class UnicoreJobSubmissionSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``UnicoreJobSubmission`` message."""
-
-    jobSubmissionInterfaceId = serializers.CharField(
-        source='job_submission_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-    unicoreEndPointURL = serializers.CharField(
-        source='unicore_end_point_url', allow_blank=True, allow_null=True,
-        required=False)
-
-
-class LocalDataMovementSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``LOCALDataMovement`` message."""
-
-    dataMovementInterfaceId = serializers.CharField(
-        source='data_movement_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-
-
-class ScpDataMovementSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``SCPDataMovement`` message."""
-
-    dataMovementInterfaceId = serializers.CharField(
-        source='data_movement_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-    alternativeSCPHostName = serializers.CharField(
-        source='alternative_scp_host_name', allow_blank=True, allow_null=True,
-        required=False)
-    sshPort = ProtoIntOrNoneField(source='ssh_port')
-
-
-class GridFtpDataMovementSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``GridFTPDataMovement`` message."""
-
-    dataMovementInterfaceId = serializers.CharField(
-        source='data_movement_interface_id', allow_blank=True, allow_null=True,
-        required=False)
-    securityProtocol = _security_protocol_field(
-        source='security_protocol', required=False, allow_null=True)
-    gridFTPEndPoints = serializers.ListField(
-        source='grid_ftp_end_points', child=serializers.CharField(),
-        required=False)
-
-
-def _status_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.status import status_pb2
-    return status_pb2
-
-
-def _experiment_state_field(**kwargs):
-    return proto_enum_int_field(
-        _status_pb2().ExperimentState.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ExperimentState'],
-        proto_prefix='EXPERIMENT_STATE_', **kwargs)
-
-
-def _process_state_field(**kwargs):
-    return proto_enum_int_field(
-        _status_pb2().ProcessState.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ProcessState'],
-        proto_prefix='PROCESS_STATE_', **kwargs)
-
-
-def _task_state_field(**kwargs):
-    return proto_enum_int_field(
-        _status_pb2().TaskState.DESCRIPTOR, _THRIFT_ENUM_INTS['TaskState'],
-        proto_prefix='TASK_STATE_', **kwargs)
-
-
-def _job_state_field(**kwargs):
-    return proto_enum_int_field(
-        _status_pb2().JobState.DESCRIPTOR, _THRIFT_ENUM_INTS['JobState'],
-        proto_prefix='JOB_STATE_', **kwargs)
-
-
-class ExperimentStatusSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ExperimentStatus`` message."""
-
-    state = _experiment_state_field(required=False, allow_null=True)
-    timeOfStateChange = UTCPosixTimestampDateTimeField(source='time_of_state_change')
-    reason = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    statusId = serializers.CharField(source='status_id', allow_blank=True, allow_null=True, required=False)
-
-
-class ProcessStatusSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ProcessStatus`` message."""
-
-    state = _process_state_field(required=False, allow_null=True)
-    timeOfStateChange = UTCPosixTimestampDateTimeField(source='time_of_state_change')
-    reason = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    statusId = serializers.CharField(source='status_id', allow_blank=True, allow_null=True, required=False)
-    processId = serializers.CharField(source='process_id', allow_blank=True, allow_null=True, required=False)
-
-
-class _NestedProcessStatusSerializer(serializers.Serializer):
-    """``ProcessStatus`` nested in the experiment tree (timeOfStateChange is the
-    raw epoch-millis int, matching the old auto-generated field)."""
-
-    state = _process_state_field(required=False, allow_null=True)
-    timeOfStateChange = serializers.IntegerField(source='time_of_state_change', allow_null=True, required=False)
-    reason = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    statusId = serializers.CharField(source='status_id', allow_blank=True, allow_null=True, required=False)
-    processId = serializers.CharField(source='process_id', allow_blank=True, allow_null=True, required=False)
-
-
-class _TaskStatusSerializer(serializers.Serializer):
-    state = _task_state_field(required=False, allow_null=True)
-    timeOfStateChange = serializers.IntegerField(source='time_of_state_change', allow_null=True, required=False)
-    reason = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    statusId = serializers.CharField(source='status_id', allow_blank=True, allow_null=True, required=False)
-
-
-class _JobStatusSerializer(serializers.Serializer):
-    jobState = _job_state_field(source='job_state', required=False, allow_null=True)
-    timeOfStateChange = serializers.IntegerField(source='time_of_state_change', allow_null=True, required=False)
-    reason = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    statusId = serializers.CharField(source='status_id', allow_blank=True, allow_null=True, required=False)
-
-
-class _ErrorModelSerializer(serializers.Serializer):
-    errorId = serializers.CharField(source='error_id', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-    actualErrorMessage = serializers.CharField(source='actual_error_message', allow_blank=True, allow_null=True, required=False)
-    userFriendlyMessage = serializers.CharField(source='user_friendly_message', allow_blank=True, allow_null=True, required=False)
-    transientOrPersistent = serializers.BooleanField(source='transient_or_persistent', required=False, default=False)
-    rootCauseErrorIdList = serializers.ListField(source='root_cause_error_id_list', child=serializers.CharField(), required=False)
-
-
-class _ComputationalResourceSchedulingSerializer(serializers.Serializer):
-    resourceHostId = serializers.CharField(source='resource_host_id', allow_blank=True, allow_null=True, required=False)
-    totalCPUCount = serializers.IntegerField(source='total_cpu_count', allow_null=True, required=False)
-    nodeCount = serializers.IntegerField(source='node_count', allow_null=True, required=False)
-    numberOfThreads = serializers.IntegerField(source='number_of_threads', allow_null=True, required=False)
-    queueName = serializers.CharField(source='queue_name', allow_blank=True, allow_null=True, required=False)
-    wallTimeLimit = serializers.IntegerField(source='wall_time_limit', allow_null=True, required=False)
-    totalPhysicalMemory = serializers.IntegerField(source='total_physical_memory', allow_null=True, required=False)
-    chessisNumber = serializers.CharField(source='chessis_number', allow_blank=True, allow_null=True, required=False)
-    staticWorkingDir = serializers.CharField(source='static_working_dir', allow_blank=True, allow_null=True, required=False)
-    overrideLoginUserName = serializers.CharField(source='override_login_user_name', allow_blank=True, allow_null=True, required=False)
-    overrideScratchLocation = serializers.CharField(source='override_scratch_location', allow_blank=True, allow_null=True, required=False)
-    overrideAllocationProjectNumber = serializers.CharField(source='override_allocation_project_number', allow_blank=True, allow_null=True, required=False)
-    mGroupCount = serializers.IntegerField(source='m_group_count', allow_null=True, required=False)
-
-    def create(self, validated_data):
-        from airavata_sdk.generated.org.apache.airavata.model.scheduling import (
-            scheduling_pb2,
-        )
-        d = validated_data
-        return scheduling_pb2.ComputationalResourceSchedulingModel(
-            resource_host_id=d.get('resource_host_id', '') or '',
-            total_cpu_count=d.get('total_cpu_count', 0) or 0,
-            node_count=d.get('node_count', 0) or 0,
-            number_of_threads=d.get('number_of_threads', 0) or 0,
-            queue_name=d.get('queue_name', '') or '',
-            wall_time_limit=d.get('wall_time_limit', 0) or 0,
-            total_physical_memory=d.get('total_physical_memory', 0) or 0,
-            chessis_number=d.get('chessis_number', '') or '',
-            static_working_dir=d.get('static_working_dir', '') or '',
-            override_login_user_name=d.get('override_login_user_name', '') or '',
-            override_scratch_location=d.get('override_scratch_location', '') or '',
-            override_allocation_project_number=d.get('override_allocation_project_number', '') or '',
-            m_group_count=d.get('m_group_count', 0) or 0,
-        )
-
-
-class _UserConfigurationDataSerializer(serializers.Serializer):
-    airavataAutoSchedule = serializers.BooleanField(source='airavata_auto_schedule', required=False, default=False)
-    overrideManualScheduledParams = serializers.BooleanField(source='override_manual_scheduled_params', required=False, default=False)
-    shareExperimentPublicly = serializers.BooleanField(source='share_experiment_publicly', required=False, default=False)
-    computationalResourceScheduling = _ComputationalResourceSchedulingSerializer(
-        source='computational_resource_scheduling', required=False)
-    throttleResources = serializers.BooleanField(source='throttle_resources', required=False, default=False)
-    userDN = serializers.CharField(source='user_dn', allow_blank=True, allow_null=True, required=False)
-    generateCert = serializers.BooleanField(source='generate_cert', required=False, default=False)
-    inputStorageResourceId = serializers.CharField(source='input_storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    outputStorageResourceId = serializers.CharField(source='output_storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    experimentDataDir = serializers.CharField(source='experiment_data_dir', allow_blank=True, allow_null=True, required=False)
-    useUserCRPref = serializers.BooleanField(source='use_user_cr_pref', required=False, default=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    autoScheduledCompResourceSchedulingList = _ComputationalResourceSchedulingSerializer(source='auto_scheduled_comp_resource_scheduling_list', many=True, required=False)
-
-    def to_representation(self, ucd):
-        ret = super().to_representation(ucd)
-        # proto3 singular sub-message is always present; the old adapter rendered
-        # computationalResourceScheduling only when HasField, else None.
-        if not ucd.HasField('computational_resource_scheduling'):
-            ret['computationalResourceScheduling'] = None
-        return ret
-
-
-class JobSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``JobModel`` message."""
-
-    jobId = serializers.CharField(source='job_id', allow_blank=True, allow_null=True, required=False)
-    taskId = serializers.CharField(source='task_id', allow_blank=True, allow_null=True, required=False)
-    processId = serializers.CharField(source='process_id', allow_blank=True, allow_null=True, required=False)
-    jobDescription = serializers.CharField(source='job_description', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True)
-    jobStatuses = _JobStatusSerializer(source='job_statuses', many=True, required=False)
-    computeResourceConsumed = serializers.CharField(source='compute_resource_consumed', allow_blank=True, allow_null=True, required=False)
-    jobName = serializers.CharField(source='job_name', allow_blank=True, allow_null=True, required=False)
-    workingDir = serializers.CharField(source='working_dir', allow_blank=True, allow_null=True, required=False)
-    stdOut = serializers.CharField(source='std_out', allow_blank=True, allow_null=True, required=False)
-    stdErr = serializers.CharField(source='std_err', allow_blank=True, allow_null=True, required=False)
-    exitCode = serializers.IntegerField(source='exit_code', allow_null=True, required=False)
-
-
-class _NestedJobSerializer(JobSerializer):
-    """``JobModel`` nested in the process tree: ``creationTime`` is the raw
-    epoch-millis int (the old auto-generated field), unlike the standalone
-    ``JobSerializer`` (jobs action) which renders ISO."""
-
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-
-
-class _TaskModelSerializer(serializers.Serializer):
-    taskId = serializers.CharField(source='task_id', allow_blank=True, allow_null=True, required=False)
-    taskType = serializers.SerializerMethodField()
-    parentProcessId = serializers.CharField(source='parent_process_id', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-    lastUpdateTime = ProtoIntOrNoneField(source='last_update_time')
-    taskStatuses = _TaskStatusSerializer(source='task_statuses', many=True, required=False)
-    taskDetail = serializers.CharField(source='task_detail', allow_blank=True, allow_null=True, required=False)
-    subTaskModel = serializers.CharField(source='sub_task_model', allow_blank=True, allow_null=True, required=False)
-    taskErrors = _ErrorModelSerializer(source='task_errors', many=True, required=False)
-    jobs = _NestedJobSerializer(many=True, required=False)
-    maxRetry = serializers.IntegerField(source='max_retry', allow_null=True, required=False)
-    currentRetry = serializers.IntegerField(source='current_retry', allow_null=True, required=False)
-
-    def get_taskType(self, task):
-        from airavata_sdk.generated.org.apache.airavata.model.task import task_pb2
-        field = proto_enum_int_field(
-            task_pb2.TaskTypes.DESCRIPTOR, _THRIFT_ENUM_INTS['TaskTypes'],
-            proto_prefix='TASK_TYPES_')
-        return field.to_representation(task.task_type)
-
-
-class _ProcessModelSerializer(serializers.Serializer):
-    processId = serializers.CharField(source='process_id', allow_blank=True, allow_null=True, required=False)
-    experimentId = serializers.CharField(source='experiment_id', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-    lastUpdateTime = ProtoIntOrNoneField(source='last_update_time')
-    processStatuses = _NestedProcessStatusSerializer(source='process_statuses', many=True, required=False)
-    processDetail = serializers.CharField(source='process_detail', allow_blank=True, allow_null=True, required=False)
-    applicationInterfaceId = serializers.CharField(source='application_interface_id', allow_blank=True, allow_null=True, required=False)
-    applicationDeploymentId = serializers.CharField(source='application_deployment_id', allow_blank=True, allow_null=True, required=False)
-    computeResourceId = serializers.CharField(source='compute_resource_id', allow_blank=True, allow_null=True, required=False)
-    processInputs = InputDataObjectTypeSerializer(source='process_inputs', many=True, required=False)
-    processOutputs = OutputDataObjectTypeSerializer(source='process_outputs', many=True, required=False)
-    processResourceSchedule = serializers.SerializerMethodField()
-    tasks = _TaskModelSerializer(many=True, required=False)
-    taskDag = serializers.CharField(source='task_dag', allow_blank=True, allow_null=True, required=False)
-    processErrors = _ErrorModelSerializer(source='process_errors', many=True, required=False)
-    gatewayExecutionId = serializers.CharField(source='gateway_execution_id', allow_blank=True, allow_null=True, required=False)
-    enableEmailNotification = serializers.BooleanField(source='enable_email_notification', required=False, default=False)
-    emailAddresses = serializers.ListField(source='email_addresses', child=serializers.CharField(), required=False)
-    inputStorageResourceId = serializers.CharField(source='input_storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    outputStorageResourceId = serializers.CharField(source='output_storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    userDn = serializers.CharField(source='user_dn', allow_blank=True, allow_null=True, required=False)
-    generateCert = serializers.BooleanField(source='generate_cert', required=False, default=False)
-    experimentDataDir = serializers.CharField(source='experiment_data_dir', allow_blank=True, allow_null=True, required=False)
-    userName = serializers.CharField(source='user_name', allow_blank=True, allow_null=True, required=False)
-    useUserCRPref = serializers.BooleanField(source='use_user_cr_pref', required=False, default=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    processWorkflows = serializers.SerializerMethodField()
-
-    def get_processResourceSchedule(self, proc):
-        if not proc.HasField('process_resource_schedule'):
-            return None
-        return _ComputationalResourceSchedulingSerializer(
-            proc.process_resource_schedule, context=self.context).data
-
-    def get_processWorkflows(self, proc):
-        # The legacy workflow-engine subsystem is not adapted (rarely populated);
-        # an empty list matches the Thrift default for non-workflow processes.
-        return []
-
-
-def _experiment_type_field(**kwargs):
-    from airavata_sdk.generated.org.apache.airavata.model.experiment import (
-        experiment_pb2,
-    )
-    return proto_enum_int_field(
-        experiment_pb2.ExperimentType.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ExperimentType'],
-        proto_prefix='EXPERIMENT_TYPE_', **kwargs)
-
-
-def _experiment_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.experiment import (
-        experiment_pb2,
-    )
-    return experiment_pb2
-
-
-class ExperimentSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ExperimentModel`` message.
-
-    The deepest read model: the processes -> tasks -> jobs tree (each with its
-    status list), user configuration + scheduling, inputs/outputs, status, and
-    errors. ``save()`` returns a proto ``ExperimentModel`` for the facade.
-    """
-
-    experimentId = serializers.CharField(source='experiment_id', read_only=True)
-    projectId = serializers.CharField(source='project_id')
-    gatewayId = serializers.CharField(source='gateway_id', read_only=True)
-    experimentType = _experiment_type_field(source='experiment_type', required=False, allow_null=True)
-    userName = serializers.CharField(source='user_name', read_only=True)
-    experimentName = serializers.CharField(source='experiment_name')
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True, read_only=True)
-    description = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    executionId = serializers.CharField(source='execution_id', allow_blank=True, allow_null=True, required=False)
-    gatewayExecutionId = serializers.CharField(source='gateway_execution_id', allow_blank=True, allow_null=True, required=False)
-    gatewayInstanceId = serializers.CharField(source='gateway_instance_id', allow_blank=True, allow_null=True, required=False)
-    enableEmailNotification = serializers.BooleanField(source='enable_email_notification', required=False, default=False)
-    emailAddresses = serializers.ListField(source='email_addresses', child=serializers.CharField(), required=False)
-    userConfigurationData = _UserConfigurationDataSerializer(
-        source='user_configuration_data', required=False)
-    experimentInputs = OrderedListField(
-        source='experiment_inputs', order_by='inputOrder',
-        child=InputDataObjectTypeSerializer(), required=False)
-    experimentOutputs = OutputDataObjectTypeSerializer(
-        source='experiment_outputs', many=True, required=False)
-    experimentStatus = ExperimentStatusSerializer(
-        source='experiment_status', many=True, required=False)
-    errors = _ErrorModelSerializer(many=True, required=False)
-    processes = _ProcessModelSerializer(many=True, required=False)
-    workflow = serializers.SerializerMethodField()
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:experiment-detail',
-        lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
-    full_experiment = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:full-experiment-detail',
-        lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
-    project = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:project-detail',
-        lookup_field='project_id', lookup_url_kwarg='project_id')
-    jobs = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:experiment-jobs',
-        lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
-    shared_entity = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:shared-entity-detail',
-        lookup_field='experiment_id', lookup_url_kwarg='entity_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_workflow(self, experiment):
-        # The legacy workflow-engine subsystem is not adapted (rarely populated).
-        return None
-
-    def get_userHasWriteAccess(self, experiment):
-        return user_has_access(self.context['request'], experiment.experiment_id)
-
-    def to_representation(self, experiment):
-        result = super().to_representation(experiment)
-        # proto3 singular sub-message is always present; the old adapter rendered
-        # userConfigurationData only when HasField, else None.
-        if not experiment.HasField('user_configuration_data'):
-            result['userConfigurationData'] = None
-        self._add_intermediate_output_information(experiment, result)
-        return result
-
-    def create(self, validated_data):
-        return _experiment_request(validated_data)
-
-    def update(self, instance, validated_data):
-        return _experiment_request(validated_data)
-
-    def _add_intermediate_output_information(self, experiment, representation):
-        request = self.context['request']
-        from airavata_sdk.generated.org.apache.airavata.model.status import (
-            status_pb2,
-        )
-        # If experiment is EXECUTING, add intermediateOutput information to
-        # experiment outputs.
-        if (experiment.experiment_status and
-                experiment.experiment_status[-1].state ==
-                status_pb2.ExperimentState.EXPERIMENT_STATE_EXECUTING):
-            for output in representation["experimentOutputs"]:
-                output["intermediateOutput"] = {"processStatus": None}
-                try:
-                    can_fetch = experiment_orchestration.can_fetch_intermediate_output(request.airavata, experiment, output["name"])
-                    output["intermediateOutput"]["canFetch"] = can_fetch
-                    process_status = experiment_orchestration.get_intermediate_output_process_status(
-                        request.airavata, experiment, output["name"])
-                    if process_status:
-                        serializer = ProcessStatusSerializer(
-                            process_status, context={'request': request})
-                        output["intermediateOutput"]["processStatus"] = serializer.data
-                    data_products = experiment_orchestration.get_intermediate_output_data_products(
-                        request.airavata, experiment, output["name"])
-                    data_product_serializer = DataProductSerializer(
-                        data_products, context={'request': request}, many=True)
-                    output["intermediateOutput"]["dataProducts"] = data_product_serializer.data
-                except Exception:
-                    log.debug("Failed to get intermediate output status", exc_info=True)
-
-
-def _experiment_request(validated_data):
-    """Build a proto ``ExperimentModel`` from validated write data.
-
-    Only the user-submittable fields are carried; status / errors / processes /
-    workflow are server-managed (the old write adapter dropped them too).
-    """
-    e = _experiment_pb2()
-    d = validated_data
-    ucd = d.get('user_configuration_data')
-    kwargs = dict(
-        experiment_id=d.get('experiment_id', '') or '',
-        project_id=d.get('project_id', '') or '',
-        gateway_id=d.get('gateway_id', '') or '',
-        experiment_type=d.get('experiment_type', 0) or 0,
-        user_name=d.get('user_name', '') or '',
-        experiment_name=d.get('experiment_name', '') or '',
-        description=d.get('description', '') or '',
-        execution_id=d.get('execution_id', '') or '',
-        enable_email_notification=bool(d.get('enable_email_notification', False)),
-        email_addresses=list(d.get('email_addresses', []) or []),
-        experiment_inputs=[
-            _proto_input_data_object(i)
-            for i in d.get('experiment_inputs', []) or []],
-        experiment_outputs=[
-            _proto_output_data_object(o)
-            for o in d.get('experiment_outputs', []) or []],
-    )
-    if ucd is not None:
-        kwargs['user_configuration_data'] = _user_configuration_request(ucd)
-    return e.ExperimentModel(**kwargs)
-
-
-def _user_configuration_request(d):
-    e = _experiment_pb2()
-    crs = d.get('computational_resource_scheduling')
-    kwargs = dict(
-        airavata_auto_schedule=bool(d.get('airavata_auto_schedule', False)),
-        override_manual_scheduled_params=bool(d.get('override_manual_scheduled_params', False)),
-        share_experiment_publicly=bool(d.get('share_experiment_publicly', False)),
-        throttle_resources=bool(d.get('throttle_resources', False)),
-        user_dn=d.get('user_dn', '') or '',
-        generate_cert=bool(d.get('generate_cert', False)),
-        input_storage_resource_id=d.get('input_storage_resource_id', '') or '',
-        output_storage_resource_id=d.get('output_storage_resource_id', '') or '',
-        experiment_data_dir=d.get('experiment_data_dir', '') or '',
-        use_user_cr_pref=bool(d.get('use_user_cr_pref', False)),
-        group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-    )
-    if crs is not None:
-        kwargs['computational_resource_scheduling'] = (
-            _ComputationalResourceSchedulingSerializer().create(crs))
-    return e.UserConfigurationDataModel(**kwargs)
-
-
-def _replica_catalog_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.data.replica import (
-        replica_catalog_pb2,
-    )
-    return replica_catalog_pb2
-
-
-def _data_product_type_field(**kwargs):
-    return proto_enum_int_field(
-        _replica_catalog_pb2().DataProductType.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['DataProductType'],
-        proto_prefix='DATA_PRODUCT_TYPE_', **kwargs)
-
-
-def _replica_location_category_field(**kwargs):
-    return proto_enum_int_field(
-        _replica_catalog_pb2().ReplicaLocationCategory.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ReplicaLocationCategory'],
-        proto_prefix='REPLICA_LOCATION_CATEGORY_', **kwargs)
-
-
-def _replica_persistent_type_field(**kwargs):
-    return proto_enum_int_field(
-        _replica_catalog_pb2().ReplicaPersistentType.DESCRIPTOR,
-        _THRIFT_ENUM_INTS['ReplicaPersistentType'],
-        proto_prefix='REPLICA_PERSISTENT_TYPE_', **kwargs)
-
-
-class DataReplicaLocationSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``DataReplicaLocationModel``."""
-
-    replicaId = serializers.CharField(source='replica_id', allow_blank=True, allow_null=True, required=False)
-    productUri = serializers.CharField(source='product_uri', allow_blank=True, allow_null=True, required=False)
-    replicaName = serializers.CharField(source='replica_name', allow_blank=True, allow_null=True, required=False)
-    replicaDescription = serializers.CharField(source='replica_description', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True)
-    lastModifiedTime = ProtoTimestampField(source='last_modified_time', null_if_zero=True)
-    validUntilTime = ProtoIntOrNoneField(source='valid_until_time')
-    replicaLocationCategory = _replica_location_category_field(source='replica_location_category', required=False, allow_null=True)
-    replicaPersistentType = _replica_persistent_type_field(source='replica_persistent_type', required=False, allow_null=True)
-    storageResourceId = serializers.CharField(source='storage_resource_id', allow_blank=True, allow_null=True, required=False)
-    filePath = serializers.CharField(source='file_path', allow_blank=True, allow_null=True, required=False)
-    replicaMetadata = serializers.DictField(source='replica_metadata', required=False)
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        # The old adapter mapped these optional proto strings to None when empty.
-        for f in ('replicaId', 'productUri', 'replicaName', 'replicaDescription',
-                  'storageResourceId', 'filePath'):
-            if ret.get(f) == '':
-                ret[f] = None
-        return ret
-
-
-class DataProductSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``DataProductModel``."""
-
-    productUri = serializers.CharField(source='product_uri', allow_blank=True, allow_null=True, required=False)
-    gatewayId = serializers.CharField(source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    parentProductUri = serializers.CharField(source='parent_product_uri', allow_blank=True, allow_null=True, required=False)
-    productName = serializers.CharField(source='product_name', allow_blank=True, allow_null=True, required=False)
-    productDescription = serializers.CharField(source='product_description', allow_blank=True, allow_null=True, required=False)
-    ownerName = serializers.CharField(source='owner_name', allow_blank=True, allow_null=True, required=False)
-    dataProductType = _data_product_type_field(source='data_product_type', required=False, allow_null=True)
-    productSize = ProtoIntOrNoneField(source='product_size')
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True)
-    # The serializer declares both modifiedTime and lastModifiedTime; both map to
-    # the proto last_modified_time.
-    modifiedTime = ProtoTimestampField(source='last_modified_time', null_if_zero=True)
-    lastModifiedTime = ProtoTimestampField(source='last_modified_time', null_if_zero=True)
-    productMetadata = serializers.DictField(source='product_metadata', required=False)
-    replicaLocations = DataReplicaLocationSerializer(source='replica_locations', many=True, required=False)
-    downloadURL = serializers.SerializerMethodField()
-    isInputFileUpload = serializers.SerializerMethodField()
-    filesize = serializers.SerializerMethodField()
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        for f in ('parentProductUri', 'productName', 'productDescription',
-                  'ownerName'):
-            if ret.get(f) == '':
-                ret[f] = None
-        return ret
-
-    def get_downloadURL(self, data_product):
-        """Lazy portal URL to the byte-streaming download endpoint.
-
-        Returns None when the data product has no replica. Resolving the bytes
-        is deferred to the endpoint, so this getter makes no backend call.
-        """
-        request = self.context['request']
-        if not data_product.replica_locations:
-            return None
-        base = request.build_absolute_uri(
-            reverse('django_airavata_api:download-file'))
-        return base + '?data-product-uri=' + quote(data_product.product_uri)
-
-    def get_isInputFileUpload(self, data_product):
-        """Return True if this is an uploaded input file.
-
-        Derived from the data product alone (no backend call): an uploaded
-        input file lives directly under the input-staging directory
-        (TMP_INPUT_FILE_UPLOAD_DIR == "tmp"), so the first replica's file path
-        has that directory as its immediate parent.
-        """
-        replicas = data_product.replica_locations
-        if not replicas or not replicas[0].file_path:
-            return False
-        parent = os.path.dirname(replicas[0].file_path)
-        return os.path.basename(parent) == TMP_INPUT_FILE_UPLOAD_DIR
-
-    def get_filesize(self, data_product):
-        # product_size comes from the data product registry; no backend call.
-        return data_product.product_size or 0
-
-    def get_userHasWriteAccess(self, data_product):
-        """Whether the requesting user may write this data product.
-
-        Derived without a backend file-metadata call: the owner always has
-        write access; in a shared directory only gateway admins do; otherwise
-        (a user's own private storage) write is allowed.
-        """
-        request = self.context['request']
-        owner = data_product.owner_name
-        if owner and owner == request.user.username:
-            return True
-        replicas = data_product.replica_locations
-        if replicas and replicas[0].file_path:
-            if view_utils.is_shared_path(replicas[0].file_path):
-                # Only admins can edit files in a shared directory.
-                return request.is_gateway_admin
-        return True
-
-
-# TODO move this into airavata_sdk?
-class FullExperiment:
-    """Experiment with referenced data models."""
-
-    def __init__(self, experimentModel, project=None, outputDataProducts=None,
-                 inputDataProducts=None, applicationModule=None,
-                 computeResource=None, jobDetails=None, outputViews=None):
-        self.experiment = experimentModel
-        self.experimentId = experimentModel.experiment_id
-        self.project = project
-        self.outputDataProducts = outputDataProducts
-        self.inputDataProducts = inputDataProducts
-        self.applicationModule = applicationModule
-        self.computeResource = computeResource
-        self.jobDetails = jobDetails
-        self.outputViews = outputViews
-
-
-class FullExperimentSerializer(serializers.Serializer):
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:full-experiment-detail',
-        lookup_field='experimentId',
-        lookup_url_kwarg='experiment_id')
-    experiment = ExperimentSerializer()
-    experimentId = serializers.CharField(read_only=True)
-    outputDataProducts = DataProductSerializer(many=True, read_only=True)
-    inputDataProducts = DataProductSerializer(many=True, read_only=True)
-    applicationModule = ApplicationModuleSerializer(read_only=True)
-    computeResource = ComputeResourceDescriptionSerializer(read_only=True)
-    project = ProjectSerializer(read_only=True)
-    jobDetails = JobSerializer(many=True, read_only=True)
-    outputViews = serializers.DictField(read_only=True)
-
-    def create(self, validated_data):
-        raise Exception("Not implemented")
-
-    def update(self, instance, validated_data):
-        raise Exception("Not implemented")
-
-
-class BaseExperimentSummarySerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ExperimentSummaryModel`` message.
-
-    Read-only; used directly for the experiment-statistics summary lists (where
-    the timestamps render as ISO strings — see :class:`ExperimentSummarySerializer`
-    for the experiment-search variant's int rendering).
-    """
-
-    experimentId = serializers.CharField(source='experiment_id', read_only=True)
-    projectId = serializers.CharField(source='project_id', read_only=True)
-    gatewayId = serializers.CharField(source='gateway_id', read_only=True)
-    creationTime = ProtoTimestampField(
-        source='creation_time', null_if_zero=True, read_only=True)
-    userName = serializers.CharField(source='user_name', read_only=True)
-    name = serializers.CharField(read_only=True)
-    description = serializers.CharField(read_only=True)
-    executionId = serializers.CharField(source='execution_id', read_only=True)
-    resourceHostId = serializers.CharField(
-        source='resource_host_id', read_only=True)
-    experimentStatus = serializers.CharField(
-        source='experiment_status', read_only=True)
-    statusUpdateTime = ProtoTimestampField(
-        source='status_update_time', null_if_zero=True, read_only=True)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:experiment-detail',
-        lookup_field='experiment_id',
-        lookup_url_kwarg='experiment_id')
-    project = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:project-detail',
-        lookup_field='project_id',
-        lookup_url_kwarg='project_id')
-
-
-class ExperimentSummarySerializer(BaseExperimentSummarySerializer):
-    # The experiment-search list historically rendered these timestamps as raw
-    # epoch-millis ints (the Thrift metaclass regenerated them as IntegerField on
-    # this subclass, shadowing the base's ISO field); preserve that exactly.
-    creationTime = ProtoIntOrNoneField(source='creation_time')
-    statusUpdateTime = ProtoIntOrNoneField(source='status_update_time')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, experiment):
-        return user_has_access(
-            self.context['request'], experiment.experiment_id)
-
-
-def _user_profile_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.user import (
-        user_profile_pb2,
-    )
-    return user_profile_pb2
-
-
-def _user_status_field(**kwargs):
-    return proto_enum_int_field(
-        _user_profile_pb2().Status.DESCRIPTOR, _THRIFT_ENUM_INTS['Status'],
-        proto_prefix='STATUS_', **kwargs)
-
-
-class UserProfileSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``UserProfile`` message.
-
-    Preserves the Thrift serializer's attribute quirks byte-for-byte: ``State``
-    (capitalised), ``orginationAffiliation`` (the Thrift misspelling), and
-    ``labeledURI`` — a Thrift *list* field fed the proto string, so the old
-    auto-generated ListField iterated it character by character; reproduced here.
-    The ``nsfDemographics``/``customDashboard`` structs render null.
-    """
-
-    userModelVersion = serializers.CharField(source='user_model_version', allow_blank=True, allow_null=True, required=False)
-    airavataInternalUserId = serializers.CharField(source='airavata_internal_user_id', allow_blank=True, allow_null=True, required=False)
-    userId = serializers.CharField(source='user_id', allow_blank=True, allow_null=True, required=False)
-    gatewayId = serializers.CharField(source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    emails = serializers.ListField(child=serializers.CharField(), required=False)
-    firstName = serializers.CharField(source='first_name', allow_blank=True, allow_null=True, required=False)
-    lastName = serializers.CharField(source='last_name', allow_blank=True, allow_null=True, required=False)
-    middleName = serializers.CharField(source='middle_name', allow_blank=True, allow_null=True, required=False)
-    namePrefix = serializers.CharField(source='name_prefix', allow_blank=True, allow_null=True, required=False)
-    nameSuffix = serializers.CharField(source='name_suffix', allow_blank=True, allow_null=True, required=False)
-    orcidId = serializers.CharField(source='orcid_id', allow_blank=True, allow_null=True, required=False)
-    phones = serializers.ListField(child=serializers.CharField(), required=False)
-    country = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    nationality = serializers.ListField(child=serializers.CharField(), required=False)
-    homeOrganization = serializers.CharField(source='home_organization', allow_blank=True, allow_null=True, required=False)
-    orginationAffiliation = serializers.CharField(source='origination_affiliation', allow_blank=True, allow_null=True, required=False)
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True)
-    lastAccessTime = ProtoTimestampField(source='last_access_time', null_if_zero=True)
-    validUntil = ProtoIntOrNoneField(source='valid_until')
-    State = _user_status_field(source='state', required=False, allow_null=True)
-    comments = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    labeledURI = serializers.ListField(source='labeled_uri', child=serializers.CharField(), required=False)
-    gpgKey = serializers.CharField(source='gpg_key', allow_blank=True, allow_null=True, required=False)
-    timeZone = serializers.CharField(source='time_zone', allow_blank=True, allow_null=True, required=False)
-    nsfDemographics = serializers.SerializerMethodField()
-    customDashboard = serializers.SerializerMethodField()
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        # The old adapter mapped optional proto strings to None when empty.
-        for f in ('userModelVersion', 'firstName', 'lastName', 'middleName',
-                  'namePrefix', 'nameSuffix', 'orcidId', 'country',
-                  'homeOrganization', 'orginationAffiliation', 'comments',
-                  'gpgKey', 'timeZone'):
-            if ret.get(f) == '':
-                ret[f] = None
-        return ret
-
-    def get_nsfDemographics(self, instance):
-        return None
-
-    def get_customDashboard(self, instance):
-        return None
-
-
-def _grp_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.groupresourceprofile import (  # noqa: E501
-        group_resource_profile_pb2,
-    )
-    return group_resource_profile_pb2
-
-
-def _resource_type_field(**kwargs):
-    return proto_enum_int_field(
-        _grp_pb2().ResourceType.DESCRIPTOR, _THRIFT_ENUM_INTS['ResourceType'],
-        proto_prefix='RESOURCE_TYPE_', **kwargs)
-
-
-class ComputeResourceReservationSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ComputeResourceReservation``."""
-
-    reservationId = serializers.CharField(source='reservation_id', allow_blank=True, allow_null=True, required=False)
-    reservationName = serializers.CharField(source='reservation_name', allow_blank=True, allow_null=True, required=False)
-    queueNames = serializers.ListField(source='queue_names', child=serializers.CharField(), required=False)
-    startTime = ProtoIntOrNoneField(source='start_time')
-    endTime = ProtoIntOrNoneField(source='end_time')
-
-    def create(self, validated_data):
-        d = validated_data
-        return _grp_pb2().ComputeResourceReservation(
-            reservation_id=d.get('reservation_id', '') or '',
-            reservation_name=d.get('reservation_name', '') or '',
-            queue_names=list(d.get('queue_names', []) or []),
-            start_time=d.get('start_time', 0) or 0,
-            end_time=d.get('end_time', 0) or 0,
-        )
-
-
-class _GroupAccountSSHProvisionerConfigSerializer(serializers.Serializer):
-    resourceId = serializers.CharField(source='resource_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    configName = serializers.CharField(source='config_name', allow_blank=True, allow_null=True, required=False)
-    configValue = serializers.CharField(source='config_value', allow_blank=True, allow_null=True, required=False)
-
-    def create(self, validated_data):
-        d = validated_data
-        return _grp_pb2().GroupAccountSSHProvisionerConfig(
-            resource_id=d.get('resource_id', '') or '',
-            group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-            config_name=d.get('config_name', '') or '',
-            config_value=d.get('config_value', '') or '',
-        )
-
-
-class GroupComputeResourcePreferenceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``GroupComputeResourcePreference``.
-
-    The proto ``specific_preferences`` is a oneof of a SLURM or AWS sub-message;
-    the read output flattens ``allocationProjectNumber`` to the top level and
-    wraps the rendered preferences as ``{'slurm': {...}}`` / AWS fields, matching
-    the historical Thrift-union serializer byte-for-byte.
-    """
-
-    computeResourceId = serializers.CharField(source='compute_resource_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    overridebyAiravata = serializers.BooleanField(source='override_by_airavata', required=False, default=False)
-    loginUserName = serializers.CharField(source='login_user_name', allow_blank=True, allow_null=True, required=False)
-    scratchLocation = serializers.CharField(source='scratch_location', allow_blank=True, allow_null=True, required=False)
-    preferredJobSubmissionProtocol = job_submission_protocol_field(source='preferred_job_submission_protocol', required=False, allow_null=True)
-    preferredDataMovementProtocol = data_movement_protocol_field(source='preferred_data_movement_protocol', required=False, allow_null=True)
-    resourceSpecificCredentialStoreToken = serializers.CharField(source='resource_specific_credential_store_token', allow_blank=True, allow_null=True, required=False)
-    resourceType = _resource_type_field(source='resource_type', required=False, allow_null=True)
-    specificPreferences = serializers.SerializerMethodField()
-    reservations = serializers.SerializerMethodField()
-
-    def get_specificPreferences(self, pref):
-        if not pref.HasField('specific_preferences'):
-            return None
-        sp = pref.specific_preferences
-        which = sp.WhichOneof('preferences')
-        if which == 'slurm':
-            # The old union serializer always rendered both members (the inactive
-            # one as null); preserve that.
-            return {'slurm': self._slurm(sp.slurm), 'aws': None}
-        if which == 'aws':
-            return self._aws(sp.aws)
-        return None
-
-    def get_reservations(self, pref):
-        # The top-level reservations field is always empty here: reservations live
-        # inside the SLURM union member (matching the old serializer's behaviour
-        # where the compute-pref object had no top-level ``reservations`` attr).
-        return []
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        # Flatten allocationProjectNumber out of the SLURM union member, as the
-        # old union serializer did.
-        if (instance.HasField('specific_preferences') and
-                instance.specific_preferences.WhichOneof('preferences') == 'slurm'):
-            ret['allocationProjectNumber'] = (
-                instance.specific_preferences.slurm.allocation_project_number)
-        return ret
-
-    def to_internal_value(self, data):
-        # specificPreferences / allocationProjectNumber / reservations are
-        # read-flatten outputs (SerializerMethodField); carry the raw write input
-        # through so create() can rebuild the proto union.
-        ret = super().to_internal_value(data)
-        if isinstance(data, dict):
-            for k in ('specificPreferences', 'allocationProjectNumber',
-                      'reservations'):
-                if k in data:
-                    ret[k] = data[k]
-        return ret
-
-    @staticmethod
-    def _slurm(s):
-        return {
-            'allocationProjectNumber': s.allocation_project_number,
-            'preferredBatchQueue': s.preferred_batch_queue,
-            'qualityOfService': s.quality_of_service,
-            'usageReportingGatewayId': s.usage_reporting_gateway_id,
-            'sshAccountProvisioner': s.ssh_account_provisioner,
-            'groupSSHAccountProvisionerConfigs': [
-                {
-                    'resourceId': c.resource_id,
-                    'groupResourceProfileId': c.group_resource_profile_id,
-                    'configName': c.config_name,
-                    'configValue': c.config_value,
-                }
-                for c in s.group_ssh_account_provisioner_configs],
-            'sshAccountProvisionerAdditionalInfo': s.ssh_account_provisioner_additional_info,
-            'reservations': [
-                {
-                    'reservationId': r.reservation_id,
-                    'reservationName': r.reservation_name,
-                    'queueNames': list(r.queue_names),
-                    'startTime': r.start_time or None,
-                    'endTime': r.end_time or None,
-                }
-                for r in s.reservations],
-        }
-
-    @staticmethod
-    def _aws(a):
-        return {
-            'region': a.region,
-            'preferredAmiId': a.preferred_ami_id,
-            'preferredInstanceType': a.preferred_instance_type,
-        }
-
-    def create(self, validated_data):
-        return _build_group_compute_resource_preference(validated_data)
-
-
-def _build_group_compute_resource_preference(d):
-    """Build a proto ``GroupComputeResourcePreference`` from validated write data.
-
-    The frontend sends ``specificPreferences`` (a ``{'slurm': {...}}`` / AWS
-    dict) plus a flattened ``allocationProjectNumber`` and a ``resourceType``;
-    map them into the proto oneof.
-    """
-    grp = _grp_pb2()
-    sp = d.get('specificPreferences')
-    resource_type = d.get('resource_type', 0) or 0
-    msg = grp.GroupComputeResourcePreference(
-        compute_resource_id=d.get('compute_resource_id', '') or '',
-        group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-        override_by_airavata=bool(d.get('override_by_airavata', False)),
-        login_user_name=d.get('login_user_name', '') or '',
-        scratch_location=d.get('scratch_location', '') or '',
-        preferred_job_submission_protocol=d.get('preferred_job_submission_protocol', 0) or 0,
-        preferred_data_movement_protocol=d.get('preferred_data_movement_protocol', 0) or 0,
-        resource_specific_credential_store_token=d.get('resource_specific_credential_store_token', '') or '',
-        resource_type=resource_type,
-    )
-    # Resolve the union: prefer an explicit specificPreferences, else infer from
-    # the flattened allocationProjectNumber / aws fields.
-    slurm_data = None
-    aws_data = None
-    if isinstance(sp, dict):
-        if 'slurm' in sp and isinstance(sp['slurm'], dict):
-            slurm_data = dict(sp['slurm'])
-        elif 'aws' in sp and isinstance(sp['aws'], dict):
-            aws_data = dict(sp['aws'])
-        elif 'region' in sp or 'preferredAmiId' in sp:
-            aws_data = dict(sp)
-        elif sp:
-            slurm_data = dict(sp)
-    if slurm_data is None and aws_data is None:
-        # Infer from the flattened fields the read output produced.
-        proto_resource_type = grp.ResourceType
-        if resource_type == proto_resource_type.AWS or 'region' in d:
-            aws_data = {}
-        elif 'allocationProjectNumber' in d or resource_type == proto_resource_type.SLURM:
-            slurm_data = {}
-    if 'allocationProjectNumber' in d and slurm_data is not None:
-        slurm_data.setdefault('allocationProjectNumber', d['allocationProjectNumber'])
-    if slurm_data is not None:
-        msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
-            slurm=_build_slurm_pref(slurm_data)))
-    elif aws_data is not None:
-        msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
-            aws=_build_aws_pref(aws_data)))
-    return msg
-
-
-def _build_slurm_pref(s):
-    grp = _grp_pb2()
-    return grp.SlurmComputeResourcePreference(
-        allocation_project_number=s.get('allocationProjectNumber', '') or '',
-        preferred_batch_queue=s.get('preferredBatchQueue', '') or '',
-        quality_of_service=s.get('qualityOfService', '') or '',
-        usage_reporting_gateway_id=s.get('usageReportingGatewayId', '') or '',
-        ssh_account_provisioner=s.get('sshAccountProvisioner', '') or '',
-        group_ssh_account_provisioner_configs=[
-            grp.GroupAccountSSHProvisionerConfig(
-                resource_id=c.get('resourceId', '') or '',
-                group_resource_profile_id=c.get('groupResourceProfileId', '') or '',
-                config_name=c.get('configName', '') or '',
-                config_value=c.get('configValue', '') or '',
-            )
-            for c in (s.get('groupSSHAccountProvisionerConfigs', []) or [])],
-        ssh_account_provisioner_additional_info=s.get('sshAccountProvisionerAdditionalInfo', '') or '',
-        reservations=[
-            grp.ComputeResourceReservation(
-                reservation_id=r.get('reservationId', '') or '',
-                reservation_name=r.get('reservationName', '') or '',
-                queue_names=list(r.get('queueNames', []) or []),
-                start_time=r.get('startTime', 0) or 0,
-                end_time=r.get('endTime', 0) or 0,
-            )
-            for r in (s.get('reservations', []) or [])],
-    )
-
-
-def _build_aws_pref(a):
-    return _grp_pb2().AwsComputeResourcePreference(
-        region=a.get('region', '') or '',
-        preferred_ami_id=a.get('preferredAmiId', '') or '',
-        preferred_instance_type=a.get('preferredInstanceType', '') or '',
-    )
-
-
-class _ComputeResourcePolicySerializer(serializers.Serializer):
-    resourcePolicyId = serializers.CharField(source='resource_policy_id', allow_blank=True, allow_null=True, required=False)
-    computeResourceId = serializers.CharField(source='compute_resource_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    allowedBatchQueues = serializers.ListField(source='allowed_batch_queues', child=serializers.CharField(), required=False)
-
-    def create(self, validated_data):
-        d = validated_data
-        return _grp_pb2().ComputeResourcePolicy(
-            resource_policy_id=d.get('resource_policy_id', '') or '',
-            compute_resource_id=d.get('compute_resource_id', '') or '',
-            group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-            allowed_batch_queues=list(d.get('allowed_batch_queues', []) or []),
-        )
-
-
-class _BatchQueueResourcePolicySerializer(serializers.Serializer):
-    resourcePolicyId = serializers.CharField(source='resource_policy_id', allow_blank=True, allow_null=True, required=False)
-    computeResourceId = serializers.CharField(source='compute_resource_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    queuename = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    maxAllowedNodes = serializers.IntegerField(source='max_allowed_nodes', allow_null=True, required=False)
-    maxAllowedCores = serializers.IntegerField(source='max_allowed_cores', allow_null=True, required=False)
-    maxAllowedWalltime = serializers.IntegerField(source='max_allowed_walltime', allow_null=True, required=False)
-
-    def create(self, validated_data):
-        d = validated_data
-        return _grp_pb2().BatchQueueResourcePolicy(
-            resource_policy_id=d.get('resource_policy_id', '') or '',
-            compute_resource_id=d.get('compute_resource_id', '') or '',
-            group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-            queuename=d.get('queuename', '') or '',
-            max_allowed_nodes=d.get('max_allowed_nodes', 0) or 0,
-            max_allowed_cores=d.get('max_allowed_cores', 0) or 0,
-            max_allowed_walltime=d.get('max_allowed_walltime', 0) or 0,
-        )
-
-
-class GroupResourceProfileSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``GroupResourceProfile`` message."""
-
-    gatewayId = serializers.CharField(source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileId = serializers.CharField(source='group_resource_profile_id', allow_blank=True, allow_null=True, required=False)
-    groupResourceProfileName = serializers.CharField(source='group_resource_profile_name')
-    computePreferences = GroupComputeResourcePreferenceSerializer(source='compute_preferences', many=True, required=False)
-    computeResourcePolicies = _ComputeResourcePolicySerializer(source='compute_resource_policies', many=True, required=False)
-    batchQueueResourcePolicies = _BatchQueueResourcePolicySerializer(source='batch_queue_resource_policies', many=True, required=False)
-    creationTime = ProtoTimestampField(source='creation_time', null_if_zero=True, read_only=True)
-    updatedTime = ProtoTimestampField(source='updated_time', null_if_zero=True, read_only=True)
-    defaultCredentialStoreToken = serializers.CharField(source='default_credential_store_token', allow_blank=True, allow_null=True, required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:group-resource-profile-detail',
-        lookup_field='group_resource_profile_id',
-        lookup_url_kwarg='group_resource_profile_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        if ret.get('defaultCredentialStoreToken') == '':
-            ret['defaultCredentialStoreToken'] = None
-        return ret
-
-    def create(self, validated_data):
-        return _build_group_resource_profile(validated_data)
-
-    def update(self, instance, validated_data):
-        return _build_group_resource_profile(validated_data)
-
-    def get_userHasWriteAccess(self, groupResourceProfile):
-        request = self.context['request']
-        write_access = user_has_access(
-            request, groupResourceProfile.group_resource_profile_id, "WRITE")
-        if not write_access:
-            return False
-        # Check that the user has READ access to all credential tokens.
-        tokens = set([groupResourceProfile.default_credential_store_token] +
-                     [cp.resource_specific_credential_store_token
-                      for cp in groupResourceProfile.compute_preferences])
-
-        def check_token(token):
-            return not token or user_has_access(request, token, "READ")
-
-        return all(map(check_token, tokens))
-
-
-def _build_group_resource_profile(d):
-    grp = _grp_pb2()
-    return grp.GroupResourceProfile(
-        gateway_id=d.get('gateway_id', '') or '',
-        group_resource_profile_id=d.get('group_resource_profile_id', '') or '',
-        group_resource_profile_name=d.get('group_resource_profile_name', '') or '',
-        compute_preferences=[
-            _build_group_compute_resource_preference(p)
-            for p in d.get('compute_preferences', []) or []],
-        compute_resource_policies=[
-            _ComputeResourcePolicySerializer().create(p)
-            for p in d.get('compute_resource_policies', []) or []],
-        batch_queue_resource_policies=[
-            _BatchQueueResourcePolicySerializer().create(p)
-            for p in d.get('batch_queue_resource_policies', []) or []],
-        default_credential_store_token=d.get('default_credential_store_token', '') or '',
-    )
-
-
-class UserPermissionSerializer(serializers.Serializer):
-    user = UserProfileSerializer()
-    permissionType = serializers.IntegerField()
-
-
-class GroupPermissionSerializer(serializers.Serializer):
-    group = GroupSerializer()
-    permissionType = serializers.IntegerField()
-
-
-class SharedEntitySerializer(serializers.Serializer):
-    entityId = serializers.CharField(read_only=True)
-    userPermissions = UserPermissionSerializer(many=True)
-    groupPermissions = GroupPermissionSerializer(many=True)
-    owner = UserProfileSerializer(read_only=True)
-    isOwner = serializers.SerializerMethodField()
-    hasSharingPermission = serializers.SerializerMethodField()
-
-    def create(self, validated_data):
-        raise Exception("Not implemented")
-
-    def update(self, instance, validated_data):
-        # Compute lists of ids to grant/revoke READ/WRITE/MANAGE_SHARING
-        # permission
-        existing_user_permissions = {
-            user['user'].airavata_internal_user_id: user['permissionType']
-            for user in instance['userPermissions']}
-        new_user_permissions = {
-            user['user']['airavata_internal_user_id']:
-                user['permissionType']
-            for user in validated_data['userPermissions']}
-
-        (
-            user_grant_read_permission,
-            user_grant_write_permission,
-            user_grant_manage_sharing_permission,
-            user_revoke_read_permission,
-            user_revoke_write_permission,
-            user_revoke_manage_sharing_permission) = self._compute_all_revokes_and_grants(
-            existing_user_permissions,
-            new_user_permissions)
-
-        existing_group_permissions = {
-            group['group'].id: group['permissionType']
-            for group in instance['groupPermissions']}
-        new_group_permissions = {
-            group['group']['id']: group['permissionType']
-            for group in validated_data['groupPermissions']}
-        UserProfile = _user_profile_pb2().UserProfile
-        GroupModel = _group_manager_pb2().GroupModel
-
-        (
-            group_grant_read_permission,
-            group_grant_write_permission,
-            group_grant_manage_sharing_permission,
-            group_revoke_read_permission,
-            group_revoke_write_permission,
-            group_revoke_manage_sharing_permission) = self._compute_all_revokes_and_grants(
-            existing_group_permissions,
-            new_group_permissions)
-
-        instance['_user_grant_read_permission'] = user_grant_read_permission
-        instance['_user_grant_write_permission'] = user_grant_write_permission
-        instance['_user_grant_manage_sharing_permission'] = user_grant_manage_sharing_permission
-        instance['_user_revoke_read_permission'] = user_revoke_read_permission
-        instance['_user_revoke_write_permission'] = user_revoke_write_permission
-        instance['_user_revoke_manage_sharing_permission'] = user_revoke_manage_sharing_permission
-        instance['_group_grant_read_permission'] = group_grant_read_permission
-        instance['_group_grant_write_permission'] = group_grant_write_permission
-        instance['_group_grant_manage_sharing_permission'] = group_grant_manage_sharing_permission
-        instance['_group_revoke_read_permission'] = group_revoke_read_permission
-        instance['_group_revoke_write_permission'] = group_revoke_write_permission
-        instance['_group_revoke_manage_sharing_permission'] = group_revoke_manage_sharing_permission
-        instance['userPermissions'] = [
-            {'user': UserProfile(**data['user']),
-             'permissionType': data['permissionType']}
-            for data in validated_data.get(
-                'userPermissions', instance['userPermissions'])]
-        instance['groupPermissions'] = [
-            {'group': GroupModel(**data['group']),
-             'permissionType': data['permissionType']}
-            for data in validated_data.get('groupPermissions', instance['groupPermissions'])]
-        return instance
-
-    def _compute_all_revokes_and_grants(self, existing_permissions,
-                                        new_permissions):
-        grant_read_permission = []
-        grant_write_permission = []
-        grant_manage_sharing_permission = []
-        revoke_read_permission = []
-        revoke_write_permission = []
-        revoke_manage_sharing_permission = []
-        # Union the two sets of user/group ids
-        all_ids = existing_permissions.keys() | new_permissions.keys()
-        for id in all_ids:
-            revokes, grants = self._compute_revokes_and_grants(
-                existing_permissions.get(id),
-                new_permissions.get(id)
-            )
-            if ResourcePermissionType.READ in revokes:
-                revoke_read_permission.append(id)
-            if ResourcePermissionType.WRITE in revokes:
-                revoke_write_permission.append(id)
-            if ResourcePermissionType.MANAGE_SHARING in revokes:
-                revoke_manage_sharing_permission.append(id)
-            if ResourcePermissionType.READ in grants:
-                grant_read_permission.append(id)
-            if ResourcePermissionType.WRITE in grants:
-                grant_write_permission.append(id)
-            if ResourcePermissionType.MANAGE_SHARING in grants:
-                grant_manage_sharing_permission.append(id)
-        return (
-            grant_read_permission,
-            grant_write_permission,
-            grant_manage_sharing_permission,
-            revoke_read_permission,
-            revoke_write_permission,
-            revoke_manage_sharing_permission)
-
-    def _compute_revokes_and_grants(self, current_permission=None,
-                                    new_permission=None):
-        read_permissions = set((ResourcePermissionType.READ,))
-        write_permissions = set((ResourcePermissionType.READ,
-                                 ResourcePermissionType.WRITE))
-        manage_share_permissions = set(
-            (ResourcePermissionType.READ,
-             ResourcePermissionType.WRITE,
-             ResourcePermissionType.MANAGE_SHARING))
-        current_permissions_set = set()
-        new_permissions_set = set()
-        if current_permission == ResourcePermissionType.READ:
-            current_permissions_set = read_permissions
-        elif current_permission == ResourcePermissionType.WRITE:
-            current_permissions_set = write_permissions
-        elif current_permission == ResourcePermissionType.MANAGE_SHARING:
-            current_permissions_set = manage_share_permissions
-        if new_permission == ResourcePermissionType.READ:
-            new_permissions_set = read_permissions
-        elif new_permission == ResourcePermissionType.WRITE:
-            new_permissions_set = write_permissions
-        elif new_permission == ResourcePermissionType.MANAGE_SHARING:
-            new_permissions_set = manage_share_permissions
-
-        # return tuple: permissions to revoke and permissions to grant
-        return (current_permissions_set - new_permissions_set,
-                new_permissions_set - current_permissions_set)
-
-    def get_isOwner(self, shared_entity):
-        request = self.context['request']
-        return shared_entity['owner'].user_id == request.user.username
-
-    def get_hasSharingPermission(self, shared_entity):
-        request = self.context['request']
-        return user_has_access(
-            request, shared_entity['entityId'], "MANAGE_SHARING")
-
-
-def _credential_store_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.credential.store import (
-        credential_store_pb2,
+def _credential_store_pb2():
+    from airavata_sdk.generated.org.apache.airavata.model.credential.store import (
+        credential_store_pb2,
     )
     return credential_store_pb2
 
 
-class CredentialSummarySerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``CredentialSummary`` message."""
-
-    type = proto_enum_name_field(
-        _credential_store_pb2().SummaryType.DESCRIPTOR, read_only=True)
-    gatewayId = serializers.CharField(source='gateway_id', read_only=True)
-    username = serializers.CharField(read_only=True)
-    publicKey = serializers.CharField(source='public_key', read_only=True)
-    persistedTime = ProtoTimestampField(
-        source='persisted_time', read_only=True)
-    token = serializers.CharField(read_only=True)
-    description = serializers.CharField(read_only=True)
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, credential_summary):
-        return user_has_access(
-            self.context['request'], credential_summary.token)
-
-
-def _gateway_profile_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.gatewayprofile import (  # noqa: E501
-        gateway_profile_pb2,
-    )
-    return gateway_profile_pb2
-
-
-class StoragePreferenceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``StoragePreference`` message."""
-
-    storageResourceId = serializers.CharField(
-        source='storage_resource_id', allow_blank=True, allow_null=True,
-        required=False)
-    loginUserName = serializers.CharField(
-        source='login_user_name', allow_blank=True, allow_null=True,
-        required=False)
-    fileSystemRootLocation = serializers.CharField(
-        source='file_system_root_location', allow_blank=True, allow_null=True,
-        required=False)
-    resourceSpecificCredentialStoreToken = serializers.CharField(
-        source='resource_specific_credential_store_token', allow_blank=True,
-        allow_null=True, required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:storage-preference-detail',
-        lookup_field='storage_resource_id',
-        lookup_url_kwarg='storage_resource_id')
-
-    def to_representation(self, instance):
-        ret = super().to_representation(instance)
-        # Convert empty string to null (preserves the old serializer's behavior)
-        if ret['resourceSpecificCredentialStoreToken'] == '':
-            ret['resourceSpecificCredentialStoreToken'] = None
-        return ret
-
-    def create(self, validated_data):
-        gp = _gateway_profile_pb2()
-        return gp.StoragePreference(
-            storage_resource_id=validated_data.get(
-                'storage_resource_id', '') or '',
-            login_user_name=validated_data.get('login_user_name', '') or '',
-            file_system_root_location=validated_data.get(
-                'file_system_root_location', '') or '',
-            resource_specific_credential_store_token=validated_data.get(
-                'resource_specific_credential_store_token', '') or '',
-        )
-
-    def update(self, instance, validated_data):
-        return self.create(validated_data)
-
-
-class ComputeResourcePreferenceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ComputeResourcePreference`` message."""
-
-    computeResourceId = serializers.CharField(
-        source='compute_resource_id', allow_blank=True, allow_null=True,
-        required=False)
-    overridebyAiravata = serializers.BooleanField(
-        source='override_by_airavata', required=False, default=False)
-    loginUserName = serializers.CharField(
-        source='login_user_name', allow_blank=True, allow_null=True,
-        required=False)
-    preferredJobSubmissionProtocol = job_submission_protocol_field(
-        source='preferred_job_submission_protocol', required=False,
-        allow_null=True)
-    preferredDataMovementProtocol = data_movement_protocol_field(
-        source='preferred_data_movement_protocol', required=False,
-        allow_null=True)
-    preferredBatchQueue = serializers.CharField(
-        source='preferred_batch_queue', allow_blank=True, allow_null=True,
-        required=False)
-    scratchLocation = serializers.CharField(
-        source='scratch_location', allow_blank=True, allow_null=True,
-        required=False)
-    allocationProjectNumber = serializers.CharField(
-        source='allocation_project_number', allow_blank=True, allow_null=True,
-        required=False)
-    resourceSpecificCredentialStoreToken = serializers.CharField(
-        source='resource_specific_credential_store_token', allow_blank=True,
-        allow_null=True, required=False)
-    usageReportingGatewayId = serializers.CharField(
-        source='usage_reporting_gateway_id', allow_blank=True, allow_null=True,
-        required=False)
-    qualityOfService = serializers.CharField(
-        source='quality_of_service', allow_blank=True, allow_null=True,
-        required=False)
-    reservation = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    reservationStartTime = ProtoIntOrNoneField(source='reservation_start_time')
-    reservationEndTime = ProtoIntOrNoneField(source='reservation_end_time')
-    sshAccountProvisioner = serializers.CharField(
-        source='ssh_account_provisioner', allow_blank=True, allow_null=True,
-        required=False)
-    sshAccountProvisionerConfig = serializers.DictField(
-        source='ssh_account_provisioner_config', required=False)
-    sshAccountProvisionerAdditionalInfo = serializers.CharField(
-        source='ssh_account_provisioner_additional_info', allow_blank=True,
-        allow_null=True, required=False)
-
-    def create(self, validated_data):
-        gp = _gateway_profile_pb2()
-        return gp.ComputeResourcePreference(
-            compute_resource_id=validated_data.get(
-                'compute_resource_id', '') or '',
-            override_by_airavata=bool(
-                validated_data.get('override_by_airavata', False)),
-            login_user_name=validated_data.get('login_user_name', '') or '',
-            preferred_job_submission_protocol=validated_data.get(
-                'preferred_job_submission_protocol', 0) or 0,
-            preferred_data_movement_protocol=validated_data.get(
-                'preferred_data_movement_protocol', 0) or 0,
-            preferred_batch_queue=validated_data.get(
-                'preferred_batch_queue', '') or '',
-            scratch_location=validated_data.get('scratch_location', '') or '',
-            allocation_project_number=validated_data.get(
-                'allocation_project_number', '') or '',
-            resource_specific_credential_store_token=validated_data.get(
-                'resource_specific_credential_store_token', '') or '',
-            usage_reporting_gateway_id=validated_data.get(
-                'usage_reporting_gateway_id', '') or '',
-            quality_of_service=validated_data.get('quality_of_service', '') or '',
-            reservation=validated_data.get('reservation', '') or '',
-            reservation_start_time=validated_data.get(
-                'reservation_start_time', 0) or 0,
-            reservation_end_time=validated_data.get(
-                'reservation_end_time', 0) or 0,
-            ssh_account_provisioner=validated_data.get(
-                'ssh_account_provisioner', '') or '',
-            ssh_account_provisioner_config=dict(
-                validated_data.get('ssh_account_provisioner_config', {}) or {}),
-            ssh_account_provisioner_additional_info=validated_data.get(
-                'ssh_account_provisioner_additional_info', '') or '',
-        )
-
-    def update(self, instance, validated_data):
-        return self.create(validated_data)
-
-
-class GatewayResourceProfileSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``GatewayResourceProfile`` message."""
-
-    gatewayID = serializers.CharField(
-        source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    credentialStoreToken = serializers.CharField(
-        source='credential_store_token', allow_blank=True, allow_null=True,
-        required=False)
-    computeResourcePreferences = ComputeResourcePreferenceSerializer(
-        source='compute_resource_preferences', many=True, required=False)
-    storagePreferences = StoragePreferenceSerializer(
-        source='storage_preferences', many=True, required=False)
-    identityServerTenant = serializers.CharField(
-        source='identity_server_tenant', allow_blank=True, allow_null=True,
-        required=False)
-    identityServerPwdCredToken = serializers.CharField(
-        source='identity_server_pwd_cred_token', allow_blank=True,
-        allow_null=True, required=False)
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, gatewayResourceProfile):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-    def create(self, validated_data):
-        gp = _gateway_profile_pb2()
-        return gp.GatewayResourceProfile(
-            gateway_id=validated_data.get('gateway_id', '') or '',
-            credential_store_token=validated_data.get(
-                'credential_store_token', '') or '',
-            compute_resource_preferences=[
-                ComputeResourcePreferenceSerializer().create(p)
-                for p in validated_data.get(
-                    'compute_resource_preferences', []) or []],
-            storage_preferences=[
-                StoragePreferenceSerializer().create(p)
-                for p in validated_data.get('storage_preferences', []) or []],
-            identity_server_tenant=validated_data.get(
-                'identity_server_tenant', '') or '',
-            identity_server_pwd_cred_token=validated_data.get(
-                'identity_server_pwd_cred_token', '') or '',
-        )
-
-    def update(self, instance, validated_data):
-        return self.create(validated_data)
-
-
-class StorageResourceSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``StorageResourceDescription`` message."""
-
-    storageResourceId = serializers.CharField(
-        source='storage_resource_id', allow_blank=True, allow_null=True,
-        required=False)
-    hostName = serializers.CharField(
-        source='host_name', allow_blank=True, allow_null=True, required=False)
-    storageResourceDescription = serializers.CharField(
-        source='storage_resource_description', allow_blank=True, allow_null=True,
-        required=False)
-    enabled = serializers.BooleanField(required=False, default=False)
-    dataMovementInterfaces = DataMovementInterfaceSerializer(
-        source='data_movement_interfaces', many=True, required=False)
-    # top-level creation/update render as ISO (non-nullable UTC fields).
-    creationTime = ProtoTimestampField(source='creation_time', required=False)
-    updateTime = ProtoTimestampField(source='update_time', required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:storage-resource-detail',
-        lookup_field='storage_resource_id',
-        lookup_url_kwarg='storage_resource_id')
-
-
-def _parser_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.appcatalog.parser import (
-        parser_pb2,
-    )
-    return parser_pb2
-
-
-class ParserInputSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ParserInput`` message."""
-
-    id = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    name = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    requiredInput = serializers.BooleanField(
-        source='required_input', required=False, default=False)
-    parserId = serializers.CharField(
-        source='parser_id', allow_blank=True, allow_null=True, required=False)
-    # IOType renders as the Thrift integer (proto FILE=1 -> Thrift FILE=0).
-    type = proto_enum_int_field(
-        _parser_pb2().IOType.DESCRIPTOR, _THRIFT_ENUM_INTS['IOType'], 'IO_TYPE_',
-        required=False, allow_null=True)
-
-
-class ParserOutputSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ParserOutput`` message."""
-
-    id = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    name = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    requiredOutput = serializers.BooleanField(
-        source='required_output', required=False, default=False)
-    parserId = serializers.CharField(
-        source='parser_id', allow_blank=True, allow_null=True, required=False)
-    type = proto_enum_int_field(
-        _parser_pb2().IOType.DESCRIPTOR, _THRIFT_ENUM_INTS['IOType'], 'IO_TYPE_',
-        required=False, allow_null=True)
-
-
-class ParserSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``Parser`` message."""
-
-    id = serializers.CharField(allow_blank=True, allow_null=True, required=False)
-    imageName = serializers.CharField(
-        source='image_name', allow_blank=True, allow_null=True, required=False)
-    outputDirPath = serializers.CharField(
-        source='output_dir_path', allow_blank=True, allow_null=True,
-        required=False)
-    inputDirPath = serializers.CharField(
-        source='input_dir_path', allow_blank=True, allow_null=True,
-        required=False)
-    executionCommand = serializers.CharField(
-        source='execution_command', allow_blank=True, allow_null=True,
-        required=False)
-    inputFiles = ParserInputSerializer(
-        source='input_files', many=True, required=False)
-    outputFiles = ParserOutputSerializer(
-        source='output_files', many=True, required=False)
-    gatewayId = serializers.CharField(
-        source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:parser-detail',
-        lookup_field='id',
-        lookup_url_kwarg='parser_id')
-
-    def create(self, validated_data):
-        pp = _parser_pb2()
-        return pp.Parser(
-            id=validated_data.get('id', '') or '',
-            image_name=validated_data.get('image_name', '') or '',
-            output_dir_path=validated_data.get('output_dir_path', '') or '',
-            input_dir_path=validated_data.get('input_dir_path', '') or '',
-            execution_command=validated_data.get('execution_command', '') or '',
-            gateway_id=validated_data.get('gateway_id', '') or '',
-            input_files=[
-                pp.ParserInput(
-                    id=i.get('id', '') or '',
-                    name=i.get('name', '') or '',
-                    required_input=bool(i.get('required_input', False)),
-                    parser_id=i.get('parser_id', '') or '',
-                    type=i.get('type', 0) or 0,
-                ) for i in validated_data.get('input_files', []) or []],
-            output_files=[
-                pp.ParserOutput(
-                    id=o.get('id', '') or '',
-                    name=o.get('name', '') or '',
-                    required_output=bool(o.get('required_output', False)),
-                    parser_id=o.get('parser_id', '') or '',
-                    type=o.get('type', 0) or 0,
-                ) for o in validated_data.get('output_files', []) or []],
-        )
-
-    def update(self, instance, validated_data):
-        return self.create(validated_data)
-
-
-class UserHasWriteAccessToPathSerializer(serializers.Serializer):
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, instance):
-        request = self.context['request']
-        if "userHasWriteAccess" in instance:
-            return instance["userHasWriteAccess"]
-        is_shared_path = view_utils.is_shared_path(instance["path"])
-        if is_shared_path:
-            return request.is_gateway_admin
-        else:
-            return True
-
-
-class UserStorageFileSerializer(UserHasWriteAccessToPathSerializer):
-    name = serializers.CharField()
-    downloadURL = serializers.SerializerMethodField()
-    dataProductURI = serializers.CharField(source='data-product-uri')
-    createdTime = serializers.DateTimeField(source='created_time')
-    modifiedTime = serializers.DateTimeField(source='modified_time')
-    mimeType = serializers.CharField(source='mime_type')
-    size = serializers.IntegerField()
-    hidden = serializers.BooleanField()
-
-    def get_downloadURL(self, file):
-        """Lazy portal URL to the byte-streaming download endpoint for this file.
-
-        Returns None when the file has no data product URI; resolving the bytes is
-        deferred to the endpoint, so this getter makes no backend call.
-        """
-        request = self.context['request']
-        data_product_uri = file.get('data-product-uri')
-        if not data_product_uri:
-            return None
-        base = request.build_absolute_uri(
-            reverse('django_airavata_api:download-file'))
-        return base + '?data-product-uri=' + quote(data_product_uri)
-
-
-class UserStorageDirectorySerializer(UserHasWriteAccessToPathSerializer):
-    name = serializers.CharField()
-    path = serializers.CharField()
-    createdTime = serializers.DateTimeField(source='created_time')
-    modifiedTime = serializers.DateTimeField(source='modified_time')
-    size = serializers.IntegerField()
-    hidden = serializers.BooleanField()
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:user-storage-items',
-        lookup_field='path',
-        lookup_url_kwarg='path')
-    isSharedDir = serializers.SerializerMethodField()
-
-    def get_isSharedDir(self, directory):
-        if "isSharedDir" in directory:
-            return directory["isSharedDir"]
-        return view_utils.is_shared_dir(directory["path"])
-
-
-class UserStoragePathSerializer(UserHasWriteAccessToPathSerializer):
-    isDir = serializers.BooleanField()
-    directories = UserStorageDirectorySerializer(many=True)
-    files = UserStorageFileSerializer(many=True)
-    parts = serializers.ListField(child=serializers.CharField())
-    path = serializers.CharField(required=False)
-    # uploaded is populated after a file upload
-    uploaded = DataProductSerializer(read_only=True)
-
-
-# Fields for ExperimentStorageFileSerializer are the same as UserStorageFileSerializer
-ExperimentStorageFileSerializer = UserStorageFileSerializer
-
-
-class ExperimentStorageDirectorySerializer(serializers.Serializer):
-    name = serializers.CharField()
-    path = serializers.CharField()
-    createdTime = serializers.DateTimeField(source='created_time')
-    modifiedTime = serializers.DateTimeField(source='modified_time')
-    size = serializers.IntegerField()
-    url = serializers.SerializerMethodField()
-
-    def get_url(self, dir):
-
-        request = self.context['request']
-        return request.build_absolute_uri(
-            reverse("django_airavata_api:experiment-storage-items", kwargs={
-                "experiment_id": dir['experiment_id'],
-                "path": dir['path']
-            }))
-
-
-class ExperimentStoragePathSerializer(serializers.Serializer):
-    isDir = serializers.BooleanField()
-    directories = ExperimentStorageDirectorySerializer(many=True)
-    files = ExperimentStorageFileSerializer(many=True)
-    parts = serializers.ListField(child=serializers.CharField())
-
-
-# ModelSerializers
-class ApplicationPreferencesSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = models.ApplicationPreferences
-        exclude = ('id', 'username', 'workspace_preferences')
-
-
-class WorkspacePreferencesSerializer(serializers.ModelSerializer):
-    application_preferences = ApplicationPreferencesSerializer(
-        source="applicationpreferences_set", many=True)
-
-    class Meta:
-        model = models.WorkspacePreferences
-        exclude = ('username',)
-
-
-class IAMUserProfile(serializers.Serializer):
-    airavataInternalUserId = serializers.CharField()
-    userId = serializers.CharField()
-    gatewayId = serializers.CharField()
-    email = serializers.CharField()
-    firstName = serializers.CharField()
-    lastName = serializers.CharField()
-    enabled = serializers.BooleanField()
-    emailVerified = serializers.BooleanField()
-    airavataUserProfileExists = serializers.BooleanField()
-    creationTime = UTCPosixTimestampDateTimeField()
-    groups = GroupSerializer(many=True)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:iam-user-profile-detail',
-        lookup_field='userId',
-        lookup_url_kwarg='user_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-    newUsername = serializers.CharField(write_only=True, required=False)
-    externalIDPUserInfo = serializers.SerializerMethodField()
-    userProfileInvalidFields = serializers.SerializerMethodField()
-
-    def update(self, instance, validated_data):
-        existing_group_ids = [group.id for group in instance['groups']]
-        new_group_ids = [group['id'] for group in validated_data['groups']]
-        instance['_added_group_ids'] = list(
-            set(new_group_ids) - set(existing_group_ids))
-        instance['_removed_group_ids'] = list(
-            set(existing_group_ids) - set(new_group_ids))
-        return instance
-
-    def get_userHasWriteAccess(self, userProfile):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-    def get_externalIDPUserInfo(self, userProfile):
-        result = {}
-        try:
-            if get_user_model().objects.filter(username=userProfile['userId']).exists():
-                django_user = get_user_model().objects.get(username=userProfile['userId'])
-                claims = django_user.user_profile.idp_userinfo.all()
-                if claims.exists():
-                    result['idp_alias'] = claims.first().idp_alias
-                    result['userinfo'] = {}
-                for claim in claims:
-                    result['userinfo'][claim.claim] = claim.value
-        except Exception as e:
-            log.warning(f"Failed to load idp_userinfo for {userProfile['userId']}", exc_info=e)
-        return result
-
-    def get_userProfileInvalidFields(self, userProfile):
-        try:
-            User = get_user_model()
-            if User.objects.filter(username=userProfile['userId']).exists():
-                django_user = User.objects.get(username=userProfile['userId'])
-                if hasattr(django_user, 'user_profile'):
-                    return django_user.user_profile.invalid_fields
-                else:
-                    # For backwards compatibility, return True if no user_profile
-                    return []
-        except Exception as e:
-            log.warning(f"Failed to get user_profile.invalid_fields for {userProfile['userId']}", exc_info=e)
-        return []
-
-
-class AckNotificationSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = models.User_Notifications
-
-
-def _notification_workspace_pb2():
-    from airavata_sdk.generated.org.apache.airavata.model.workspace import (
-        workspace_pb2,
-    )
-    return workspace_pb2
-
-
-class NotificationSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``Notification`` message."""
-
-    notificationId = serializers.CharField(
-        source='notification_id', read_only=True)
-    gatewayId = serializers.CharField(
-        source='gateway_id', allow_blank=True, allow_null=True, required=False)
-    title = serializers.CharField(
-        allow_blank=True, allow_null=True, required=False)
-    notificationMessage = serializers.CharField(
-        source='notification_message', allow_blank=True, allow_null=True,
-        required=False)
-    creationTime = ProtoTimestampField(
-        source='creation_time', null_if_zero=True, required=False)
-    publishedTime = ProtoTimestampField(source='published_time', required=False)
-    expirationTime = ProtoTimestampField(source='expiration_time', required=False)
-    # priority renders as the member NAME (proto LOW/NORMAL/HIGH == Thrift names).
-    priority = proto_enum_name_field(
-        _notification_workspace_pb2().NotificationPriority.DESCRIPTOR,
-        proto_prefix='NOTIFICATION_PRIORITY_', required=False)
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:manage-notifications-detail',
-        lookup_field='notification_id',
-        lookup_url_kwarg='notification_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-    showInDashboard = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, notification):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-    def get_showInDashboard(self, notification):
-        extensions = models.NotificationExtension.objects.filter(
-            notification_id=notification.notification_id)
-        return bool(extensions) and extensions[0].showInDashboard
-
-    def validate(self, attrs):
-        attrs.pop("showInDashboard", None)
-        return attrs
-
-    def create(self, validated_data):
-        w = _notification_workspace_pb2()
-        return w.Notification(
-            gateway_id=validated_data.get('gateway_id', '') or '',
-            title=validated_data.get('title', '') or '',
-            notification_message=validated_data.get(
-                'notification_message', '') or '',
-            creation_time=validated_data.get('creation_time', 0) or 0,
-            published_time=validated_data.get('published_time', 0) or 0,
-            expiration_time=validated_data.get('expiration_time', 0) or 0,
-            priority=validated_data.get('priority', 0) or 0,
-        )
-
-    def update(self, instance, validated_data):
-        for proto_field in ('gateway_id', 'title', 'notification_message',
-                            'creation_time', 'published_time', 'expiration_time',
-                            'priority'):
-            if proto_field in validated_data:
-                value = validated_data[proto_field]
-                if proto_field.endswith('_time') or proto_field == 'priority':
-                    value = value or 0
-                else:
-                    value = value or ''
-                setattr(instance, proto_field, value)
-        return instance
-
-    def update_notification_extension(self, request, notification):
-        if "showInDashboard" in request.data:
-            existing_entries = models.NotificationExtension.objects.filter(
-                notification_id=notification.notification_id)
-
-            if len(existing_entries) > 0:
-                existing_entries.update(
-                    showInDashboard=request.data["showInDashboard"]
-                )
-            else:
-                models.NotificationExtension.objects.create(
-                    notification_id=notification.notification_id,
-                    showInDashboard=request.data["showInDashboard"]
-                )
-
-
-class ExperimentStatisticsSerializer(serializers.Serializer):
-    """Proto-native serializer for the gRPC ``ExperimentStatistics`` message."""
-
-    allExperimentCount = serializers.IntegerField(
-        source='all_experiment_count', read_only=True)
-    completedExperimentCount = serializers.IntegerField(
-        source='completed_experiment_count', read_only=True)
-    cancelledExperimentCount = serializers.IntegerField(
-        source='cancelled_experiment_count', read_only=True)
-    failedExperimentCount = serializers.IntegerField(
-        source='failed_experiment_count', read_only=True)
-    createdExperimentCount = serializers.IntegerField(
-        source='created_experiment_count', read_only=True)
-    runningExperimentCount = serializers.IntegerField(
-        source='running_experiment_count', read_only=True)
-    allExperiments = BaseExperimentSummarySerializer(
-        source='all_experiments', many=True, read_only=True)
-    completedExperiments = BaseExperimentSummarySerializer(
-        source='completed_experiments', many=True, read_only=True)
-    failedExperiments = BaseExperimentSummarySerializer(
-        source='failed_experiments', many=True, read_only=True)
-    cancelledExperiments = BaseExperimentSummarySerializer(
-        source='cancelled_experiments', many=True, read_only=True)
-    createdExperiments = BaseExperimentSummarySerializer(
-        source='created_experiments', many=True, read_only=True)
-    runningExperiments = BaseExperimentSummarySerializer(
-        source='running_experiments', many=True, read_only=True)
-
-
-class UnverifiedEmailUserProfile(serializers.Serializer):
-    userId = serializers.CharField()
-    gatewayId = serializers.CharField()
-    email = serializers.CharField()
-    firstName = serializers.CharField()
-    lastName = serializers.CharField()
-    enabled = serializers.BooleanField()
-    emailVerified = serializers.BooleanField()
-    creationTime = UTCPosixTimestampDateTimeField()
-    url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:unverified-email-user-profile-detail',
-        lookup_field='userId',
-        lookup_url_kwarg='user_id')
-    userHasWriteAccess = serializers.SerializerMethodField()
-
-    def get_userHasWriteAccess(self, userProfile):
-        request = self.context['request']
-        return request.is_gateway_admin
-
-
-class LogRecordSerializer(serializers.Serializer):
-    level = serializers.CharField()
-    message = serializers.CharField()
-    details = StoredJSONField()
-    stacktrace = serializers.ListField(child=serializers.CharField())
-
-
-class SettingsSerializer(serializers.Serializer):
-    fileUploadMaxFileSize = serializers.IntegerField()
-    tusEndpoint = serializers.CharField()
-    pgaUrl = serializers.CharField()
-
-
-class QueueSettingsCalculatorSerializer(serializers.Serializer):
-    id = serializers.CharField()
-    name = serializers.CharField()
+def application_preferences_data(application_preferences):
+    # Same field set the old ModelSerializer emitted (exclude id/username/fk).
+    return {
+        'application_id': application_preferences.application_id,
+        'favorite': application_preferences.favorite,
+    }
+
+
+def workspace_preferences_data(workspace_preferences):
+    # Read-only; matches the old ModelSerializer (exclude username, nest the
+    # application_preferences child list).
+    return {
+        'most_recent_project_id':
+            workspace_preferences.most_recent_project_id,
+        'most_recent_group_resource_profile_id':
+            workspace_preferences.most_recent_group_resource_profile_id,
+        'most_recent_compute_resource_id':
+            workspace_preferences.most_recent_compute_resource_id,
+        'application_preferences': [
+            application_preferences_data(ap)
+            for ap in workspace_preferences.applicationpreferences_set.all()],
+    }
+
+
+# IAM user write path (read/output is the SDK pydantic IAMUser). The old
+# camelCase IAMUserProfile body is validated for its required field set; the
+# group-membership diff drives the add/remove the view applies.
+_IAM_USER_REQUIRED_FIELDS = (
+    'airavataInternalUserId', 'userId', 'gatewayId', 'email', 'firstName',
+    'lastName', 'enabled', 'emailVerified', 'airavataUserProfileExists',
+    'creationTime', 'groups',
+)
+
+
+def validate_iam_user_body(data):
+    """Validate the camelCase IAM user body, reproducing the old serializer's
+    required-field 400 shape (``{field: ['This field is required.']}``)."""
+    if not isinstance(data, dict):
+        raise ValidationError("Invalid request body.")
+    errors = {}
+    for field in _IAM_USER_REQUIRED_FIELDS:
+        if data.get(field) is None:
+            errors[field] = ['This field is required.']
+    if errors:
+        raise ValidationError(errors)
+    return data
+
+
+def iam_user_group_diff(existing_groups, data):
+    """Compute the added/removed group-id diff the view applies.
+
+    ``existing_groups`` are the proto ``GroupModel`` objects the user belongs to;
+    the new ids are read from the request ``groups`` dicts by ``id`` (the field
+    the frontend sends). Returns ``(added_group_ids, removed_group_ids)``.
+    """
+    existing_group_ids = [group.id for group in existing_groups]
+    new_group_ids = [group['id'] for group in data.get('groups', [])]
+    added_group_ids = list(set(new_group_ids) - set(existing_group_ids))
+    removed_group_ids = list(set(existing_group_ids) - set(new_group_ids))
+    return added_group_ids, removed_group_ids
+
+
+def parse_update_username(data):
+    """Validate an update-username body (the full profile plus ``newUsername``),
+    returning ``(user_id, new_username)``."""
+    validated = validate_iam_user_body(data)
+    new_username = validated.get('newUsername')
+    if not new_username:
+        raise ValidationError({'newUsername': ['This field is required.']})
+    return validated['userId'], new_username
+
+
+class ValidationError(Exception):
+    """Carries a DRF-style error mapping (``{field: [messages]}`` or a string)
+    so plain validators can produce the 400 body the frontend expects."""
+
+    def __init__(self, detail):
+        self.detail = detail
+        super().__init__(detail)
+
+
+def settings_data(file_upload_max_file_size, tus_endpoint, pga_url):
+    # camelCase keys preserved verbatim (frontend Settings.js contract).
+    return {
+        'fileUploadMaxFileSize': file_upload_max_file_size,
+        'tusEndpoint': tus_endpoint,
+        'pgaUrl': pga_url,
+    }
+
+
+def parse_log_record(data):
+    """Validate a posted frontend log record, preserving the legacy
+    ``LogRecordSerializer`` shape. ``details`` is stored as a JSON string (the
+    old ``StoredJSONField.to_internal_value``) for logging; ``render_log_record``
+    decodes it back for the echo response.
+    """
+    errors = {}
+    if not isinstance(data, dict):
+        raise ValidationError("Invalid log record.")
+    level = data.get('level')
+    message = data.get('message')
+    stacktrace = data.get('stacktrace')
+    if not level:
+        errors['level'] = ['This field is required.']
+    if not message:
+        errors['message'] = ['This field is required.']
+    if 'details' not in data:
+        errors['details'] = ['This field is required.']
+    if stacktrace is None:
+        errors['stacktrace'] = ['This field is required.']
+    elif not isinstance(stacktrace, list):
+        errors['stacktrace'] = ['Expected a list of items.']
+    if errors:
+        raise ValidationError(errors)
+    try:
+        details = json.dumps(data['details'])
+    except (TypeError, ValueError):
+        raise ValidationError({'details': ['Value must be valid JSON.']})
+    return {
+        'level': level,
+        'message': message,
+        'details': details,
+        'stacktrace': [str(s) for s in stacktrace],
+    }
+
+
+def render_log_record(validated):
+    # Mirror the old serializer's read path: details JSON-decoded, rest verbatim.
+    try:
+        details = json.loads(validated['details']) if validated['details'] else validated['details']
+    except Exception:
+        details = validated['details']
+    return {
+        'level': validated['level'],
+        'message': validated['message'],
+        'details': details,
+        'stacktrace': validated['stacktrace'],
+    }
+
+
+def queue_settings_calculator_data(calculator):
+    return {'id': calculator.id, 'name': calculator.name}
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js
index 87561c555..6ae994281 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js
@@ -4,56 +4,57 @@ import CommandObject from "./CommandObject";
 import SetEnvPaths from "./SetEnvPaths";
 
 const FIELDS = [
-  "appDeploymentId",
-  "appModuleId",
-  "computeHostId",
-  "executablePath",
+  "app_deployment_id",
+  "app_module_id",
+  "compute_host_id",
+  "executable_path",
   {
     name: "parallelism",
     type: ParallelismType,
     default: ParallelismType.SERIAL,
   },
-  "appDeploymentDescription",
+  "app_deployment_description",
   {
-    name: "moduleLoadCmds",
+    name: "module_load_cmds",
     type: CommandObject,
     list: true,
   },
   {
-    name: "libPrependPaths",
+    name: "lib_prepend_paths",
     type: SetEnvPaths,
     list: true,
   },
   {
-    name: "libAppendPaths",
+    name: "lib_append_paths",
     type: SetEnvPaths,
     list: true,
   },
   {
-    name: "setEnvironment",
+    name: "set_environment",
     type: SetEnvPaths,
     list: true,
   },
   {
-    name: "preJobCommands",
+    name: "pre_job_commands",
     type: CommandObject,
     list: true,
   },
   {
-    name: "postJobCommands",
+    name: "post_job_commands",
     type: CommandObject,
     list: true,
   },
-  "defaultQueueName",
-  "defaultNodeCount",
-  "defaultCPUCount",
-  "defaultWalltime",
+  "default_queue_name",
+  "default_node_count",
+  "default_cpu_count",
+  "default_walltime",
   {
-    name: "editableByUser",
+    name: "editable_by_user",
     type: "boolean",
     default: false,
   },
-  "userHasWriteAccess",
+  // WithAccess-merged scalar (not on the proto).
+  "user_has_write_access",
 ];
 
 export default class ApplicationDeploymentDescription extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
index d75164cb0..0d9af0091 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
@@ -5,47 +5,51 @@ import DataType from "./DataType";
 import Experiment from "./Experiment";
 
 const FIELDS = [
-  "applicationInterfaceId",
-  "applicationName",
-  "applicationDescription",
+  "application_interface_id",
+  "application_name",
+  "application_description",
   {
-    name: "applicationModules",
+    name: "application_modules",
     type: "string",
     list: true,
   },
-  // When saving/updating, the order of the inputs in the applicationInputs
-  // array determines the 'inputOrder' that will be applied to each input on the
-  // backend. Updating 'inputOrder' will have no effect.
+  // When saving/updating, the order of the inputs in the application_inputs
+  // array determines the 'input_order' that will be applied to each input on
+  // the backend. Updating 'input_order' will have no effect.
   {
-    name: "applicationInputs",
+    name: "application_inputs",
     type: InputDataObjectType,
     list: true,
     default: BaseModel.defaultNewInstance(Array),
   },
   {
-    name: "applicationOutputs",
+    name: "application_outputs",
     type: OutputDataObjectType,
     list: true,
     default: BaseModel.defaultNewInstance(Array),
   },
   {
-    name: "archiveWorkingDirectory",
+    name: "archive_working_directory",
     type: "boolean",
     default: false,
   },
   {
-    name: "hasOptionalFileInputs",
+    name: "has_optional_file_inputs",
     type: "boolean",
     default: false,
   },
-  "userHasWriteAccess",
+  // WithAccess-merged scalar (not on the proto).
+  "user_has_write_access",
+  // Portal-only queue-settings overrides: not on the proto and not merged into
+  // the read response; the backend persists them from the write request body
+  // (snake_case keys) into the ApplicationSettings model.
   {
-    name: "showQueueSettings",
+    name: "show_queue_settings",
     type: "boolean",
     default: true,
   },
   {
-    name: "queueSettingsCalculatorId",
+    name: "queue_settings_calculator_id",
     type: "string",
     default: null,
   },
@@ -77,26 +81,26 @@ export default class ApplicationInterfaceDefinition extends BaseModel {
         },
       },
     });
-    if (!this.applicationOutputs) {
-      this.applicationOutputs = [];
+    if (!this.application_outputs) {
+      this.application_outputs = [];
     }
-    this.applicationOutputs.push(stdout, stderr);
+    this.application_outputs.push(stdout, stderr);
   }
 
   createExperiment() {
     const experiment = new Experiment();
     experiment.populateInputsOutputsFromApplicationInterface(this);
-    experiment.executionId = this.applicationInterfaceId;
+    experiment.execution_id = this.application_interface_id;
     return experiment;
   }
 
   get applicationModuleId() {
-    if (!this.applicationModules || this.applicationModules.length > 1) {
+    if (!this.application_modules || this.application_modules.length > 1) {
       throw new Error(
         `No unique application module exists for interface
-        ${this.applicationName}: modules=${this.applicationModules}`
+        ${this.application_name}: modules=${this.application_modules}`
       );
     }
-    return this.applicationModules[0];
+    return this.application_modules[0];
   }
 }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationModule.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationModule.js
index b7fe38c12..f60ce9b13 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationModule.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationModule.js
@@ -1,11 +1,12 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "appModuleId",
-  "appModuleName",
-  "appModuleVersion",
-  "appModuleDescription",
-  "userHasWriteAccess",
+  "app_module_id",
+  "app_module_name",
+  "app_module_version",
+  "app_module_description",
+  // WithAccess-merged scalar (not on the proto).
+  "user_has_write_access",
 ];
 
 export default class ApplicationModule extends BaseModel {
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
index 8ba728871..2cb7e8159 100644
--- 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
@@ -2,8 +2,8 @@ import BaseModel from "./BaseModel";
 
 const FIELDS = [
   "region",
-  "preferredAmiId",
-  "preferredInstanceType",
+  "preferred_ami_id",
+  "preferred_instance_type",
 ];
 
 export default class AwsComputeResourcePreference extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
index b48822d21..4838911ba 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseEnum.js
@@ -1,5 +1,4 @@
 export default class BaseEnum {
-  // TODO: add parameter
   constructor(name, value, writeName = false) {
     this.name = name;
     this.value = value;
@@ -10,21 +9,38 @@ export default class BaseEnum {
   toJSON() {
     return this.writeName ? this.name : this.value;
   }
+  // Wire enum values are full proto member names ("EXPERIMENT_STATE_EXECUTING");
+  // byName also accepts the stripped short alias ("EXECUTING") so component-level
+  // constant lookups keep resolving.
   static byName(name) {
-    return this.values.find((x) => x.name === name);
+    return (
+      this.values.find((x) => x.name === name) ||
+      this.values.find((x) => this.shortAlias(x.name) === name)
+    );
   }
   static byValue(value) {
     return this.values.find((x) => x.value === value);
   }
-  // This must be called to initialize static methods on the Enum subclass
+
+  // The 0-sentinel is the only member that always carries the full
+  // SCREAMING_SNAKE(className) prefix; non-zero members carry it inconsistently
+  // (ExperimentState.EXPERIMENT_STATE_CREATED, but DataType.STRING). Strip the
+  // prefix when present so `ExperimentState.EXECUTING` resolves either way.
+  static shortAlias(name) {
+    const prefix = this.name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase() + "_";
+    return name.startsWith(prefix) ? name.slice(prefix.length) : name;
+  }
+
+  // names: full proto member names in proto order. Each value is registered
+  // under its full name AND aliased to its stripped short name on the class, so
+  // both byName(fullName)/this[fullName] and this[shortName] resolve.
   static init(names, writeName = false) {
     const enums = names.map((name, index) => new this(name, index, writeName));
     Object.freeze(enums);
-    Object.defineProperty(this, "values", {
-      get: function () {
-        return enums;
-      },
+    Object.defineProperty(this, "values", { get: () => enums });
+    this.values.forEach((v) => {
+      this[v.name] = v;
+      this[this.shortAlias(v.name)] = v;
     });
-    this.values.forEach((v) => (this[v.name] = v));
   }
 }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
index 571a2afd6..0d00c166c 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BaseModel.js
@@ -71,9 +71,12 @@ export default class BaseModel {
   }
 
   convertDateField(fieldValue, fieldDefault) {
-    return typeof fieldValue !== "undefined"
-      ? new Date(fieldValue)
-      : fieldDefault;
+    // Wire timestamps are epoch-millis strings ("1705320000000", "0" for unset);
+    // new Date(millisString) would parse it as a date string and yield Invalid Date.
+    if (fieldValue === null || typeof fieldValue === "undefined") {
+      return fieldDefault;
+    }
+    return new Date(Number(fieldValue));
   }
 
   convertModelField(modelClass, fieldValue, fieldDefault) {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js
index 0f31b6a0c..a7d0209cf 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js
@@ -1,19 +1,19 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "queueName",
-  "queueDescription",
-  "maxRunTime",
-  "maxNodes",
-  "maxProcessors",
-  "maxJobsInQueue",
-  "maxMemory",
-  "cpuPerNode",
-  "defaultNodeCount",
-  "defaultCPUCount",
-  "defaultWalltime",
-  "queueSpecificMacros",
-  "isDefaultQueue",
+  "queue_name",
+  "queue_description",
+  "max_run_time",
+  "max_nodes",
+  "max_processors",
+  "max_jobs_in_queue",
+  "max_memory",
+  "cpu_per_node",
+  "default_node_count",
+  "default_cpu_count",
+  "default_walltime",
+  "queue_specific_macros",
+  "is_default_queue",
 ];
 
 export default class BatchQueue extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueueResourcePolicy.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueueResourcePolicy.js
index 3cf8f262d..f6496176d 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueueResourcePolicy.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueueResourcePolicy.js
@@ -1,13 +1,13 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "resourcePolicyId",
-  "computeResourceId",
-  "groupResourceProfileId",
+  "resource_policy_id",
+  "compute_resource_id",
+  "group_resource_profile_id",
   "queuename",
-  "maxAllowedNodes",
-  "maxAllowedCores",
-  "maxAllowedWalltime",
+  "max_allowed_nodes",
+  "max_allowed_cores",
+  "max_allowed_walltime",
 ];
 
 export default class BatchQueueResourcePolicy extends BaseModel {
@@ -17,26 +17,26 @@ export default class BatchQueueResourcePolicy extends BaseModel {
 
   validate(batchQueue) {
     let validationResults = {};
-    if (this.maxAllowedNodes && this.maxAllowedNodes < 1) {
-      validationResults["maxAllowedNodes"] = "Must be at least 1.";
-    } else if (this.maxAllowedNodes > batchQueue.maxNodes) {
+    if (this.max_allowed_nodes && this.max_allowed_nodes < 1) {
+      validationResults["max_allowed_nodes"] = "Must be at least 1.";
+    } else if (this.max_allowed_nodes > batchQueue.max_nodes) {
       validationResults[
-        "maxAllowedNodes"
-      ] = `Must be at most ${batchQueue.maxNodes}.`;
+        "max_allowed_nodes"
+      ] = `Must be at most ${batchQueue.max_nodes}.`;
     }
-    if (this.maxAllowedCores && this.maxAllowedCores < 1) {
-      validationResults["maxAllowedCores"] = "Must be at least 1.";
-    } else if (this.maxAllowedCores > batchQueue.maxProcessors) {
+    if (this.max_allowed_cores && this.max_allowed_cores < 1) {
+      validationResults["max_allowed_cores"] = "Must be at least 1.";
+    } else if (this.max_allowed_cores > batchQueue.max_processors) {
       validationResults[
-        "maxAllowedCores"
-      ] = `Must be at most ${batchQueue.maxProcessors}.`;
+        "max_allowed_cores"
+      ] = `Must be at most ${batchQueue.max_processors}.`;
     }
-    if (this.maxAllowedWalltime && this.maxAllowedWalltime < 1) {
-      validationResults["maxAllowedWalltime"] = "Must be at least 1.";
-    } else if (this.maxAllowedWalltime > batchQueue.maxRunTime) {
+    if (this.max_allowed_walltime && this.max_allowed_walltime < 1) {
+      validationResults["max_allowed_walltime"] = "Must be at least 1.";
+    } else if (this.max_allowed_walltime > batchQueue.max_run_time) {
       validationResults[
-        "maxAllowedWalltime"
-      ] = `Must be at most ${batchQueue.maxRunTime}.`;
+        "max_allowed_walltime"
+      ] = `Must be at most ${batchQueue.max_run_time}.`;
     }
     return validationResults;
   }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js
index fbd43fb75..9ef8163ad 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js
@@ -1,7 +1,7 @@
 import BaseModel from "./BaseModel";
 import uuidv4 from "uuid/v4";
 
-const FIELDS = ["command", "commandOrder"];
+const FIELDS = ["command", "command_order"];
 
 export default class CommandObject extends BaseModel {
   constructor(data = {}) {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputationalResourceSchedulingModel.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputationalResourceSchedulingModel.js
index 5df9bc237..2a88cc8fe 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputationalResourceSchedulingModel.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputationalResourceSchedulingModel.js
@@ -1,18 +1,19 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "resourceHostId",
-  "totalCPUCount",
-  "nodeCount",
-  "numberOfThreads",
-  "queueName",
-  "wallTimeLimit",
-  "totalPhysicalMemory",
-  "chessisNumber",
-  "staticWorkingDir",
-  "overrideLoginUserName",
-  "overrideScratchLocation",
-  "overrideAllocationProjectNumber",
+  "resource_host_id",
+  "total_cpu_count",
+  "node_count",
+  "number_of_threads",
+  "queue_name",
+  "wall_time_limit",
+  "total_physical_memory",
+  "chessis_number",
+  "static_working_dir",
+  "override_login_user_name",
+  "override_scratch_location",
+  "override_allocation_project_number",
+  "m_group_count",
 ];
 
 export default class ComputationalResourceSchedulingModel extends BaseModel {
@@ -22,78 +23,80 @@ export default class ComputationalResourceSchedulingModel extends BaseModel {
 
   validate(queueInfo = null, batchQueueResourcePolicy = null) {
     const validationResults = {};
-    if (this.isEmpty(this.resourceHostId)) {
-      validationResults["resourceHostId"] = "Please select a compute resource.";
+    if (this.isEmpty(this.resource_host_id)) {
+      validationResults["resource_host_id"] =
+        "Please select a compute resource.";
     }
-    if (this.isEmpty(this.queueName)) {
-      validationResults["queueName"] = "Please select a queue.";
+    if (this.isEmpty(this.queue_name)) {
+      validationResults["queue_name"] = "Please select a queue.";
     }
-    if (!(this.nodeCount > 0)) {
-      validationResults["nodeCount"] = "Enter a node count greater than 0.";
+    if (!(this.node_count > 0)) {
+      validationResults["node_count"] = "Enter a node count greater than 0.";
     } else if (
       batchQueueResourcePolicy &&
-      this.nodeCount > batchQueueResourcePolicy.maxAllowedNodes
+      this.node_count > batchQueueResourcePolicy.max_allowed_nodes
     ) {
       validationResults[
-        "nodeCount"
-      ] = `Enter a node count no greater than ${batchQueueResourcePolicy.maxAllowedNodes}.`;
+        "node_count"
+      ] = `Enter a node count no greater than ${batchQueueResourcePolicy.max_allowed_nodes}.`;
     } else if (
       queueInfo &&
-      queueInfo.maxNodes &&
-      this.nodeCount > queueInfo.maxNodes
+      queueInfo.max_nodes &&
+      this.node_count > queueInfo.max_nodes
     ) {
       validationResults[
-        "nodeCount"
-      ] = `Enter a node count no greater than ${queueInfo.maxNodes}.`;
+        "node_count"
+      ] = `Enter a node count no greater than ${queueInfo.max_nodes}.`;
     }
-    if (!(this.totalCPUCount > 0)) {
-      validationResults["totalCPUCount"] = "Enter a core count greater than 0.";
+    if (!(this.total_cpu_count > 0)) {
+      validationResults["total_cpu_count"] =
+        "Enter a core count greater than 0.";
     } else if (
       batchQueueResourcePolicy &&
-      this.totalCPUCount > batchQueueResourcePolicy.maxAllowedCores
+      this.total_cpu_count > batchQueueResourcePolicy.max_allowed_cores
     ) {
       validationResults[
-        "totalCPUCount"
-      ] = `Enter a core count no greater than ${batchQueueResourcePolicy.maxAllowedCores}.`;
+        "total_cpu_count"
+      ] = `Enter a core count no greater than ${batchQueueResourcePolicy.max_allowed_cores}.`;
     } else if (
       queueInfo &&
-      queueInfo.maxProcessors &&
-      this.totalCPUCount > queueInfo.maxProcessors
+      queueInfo.max_processors &&
+      this.total_cpu_count > queueInfo.max_processors
     ) {
       validationResults[
-        "totalCPUCount"
-      ] = `Enter a core count no greater than ${queueInfo.maxProcessors}.`;
+        "total_cpu_count"
+      ] = `Enter a core count no greater than ${queueInfo.max_processors}.`;
     }
-    if (!(this.wallTimeLimit > 0)) {
-      validationResults["wallTimeLimit"] =
+    if (!(this.wall_time_limit > 0)) {
+      validationResults["wall_time_limit"] =
         "Enter a wall time limit greater than 0.";
     } else if (
       batchQueueResourcePolicy &&
-      this.wallTimeLimit > batchQueueResourcePolicy.maxAllowedWalltime
+      this.wall_time_limit > batchQueueResourcePolicy.max_allowed_walltime
     ) {
       validationResults[
-        "wallTimeLimit"
-      ] = `Enter a wall time limit no greater than ${batchQueueResourcePolicy.maxAllowedWalltime}.`;
+        "wall_time_limit"
+      ] = `Enter a wall time limit no greater than ${batchQueueResourcePolicy.max_allowed_walltime}.`;
     } else if (
       queueInfo &&
-      queueInfo.maxRunTime &&
-      this.wallTimeLimit > queueInfo.maxRunTime
+      queueInfo.max_run_time &&
+      this.wall_time_limit > queueInfo.max_run_time
     ) {
       validationResults[
-        "wallTimeLimit"
-      ] = `Enter a wall time limit no greater than ${queueInfo.maxRunTime}.`;
+        "wall_time_limit"
+      ] = `Enter a wall time limit no greater than ${queueInfo.max_run_time}.`;
     }
-    if (!(this.totalPhysicalMemory >= 0)) {
-      validationResults["totalPhysicalMemory"] =
+    if (!(this.total_physical_memory >= 0)) {
+      validationResults["total_physical_memory"] =
         "Enter a total physical memory greater than or equal to 0.";
     } else if (
       queueInfo &&
-      queueInfo.maxMemory &&
-      this.totalPhysicalMemory > queueInfo.maxMemory
+      queueInfo.max_memory &&
+      this.total_physical_memory > queueInfo.max_memory
     ) {
       validationResults[
-        "totalPhysicalMemory"
-      ] = `Enter a total physical memory no greater than ${queueInfo.maxMemory}.`;
+        "total_physical_memory"
+      ] = `Enter a total physical memory no greater than ${queueInfo.max_memory}.`;
     }
     return validationResults;
   }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceDescription.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceDescription.js
index 81b751cd6..70b0a58ab 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceDescription.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceDescription.js
@@ -2,37 +2,37 @@ import BaseModel from "./BaseModel";
 import BatchQueue from "./BatchQueue";
 
 const FIELDS = [
-  "computeResourceId",
-  "hostName",
+  "compute_resource_id",
+  "host_name",
   {
-    name: "hostAliases",
+    name: "host_aliases",
     type: "string",
     list: true,
   },
   {
-    name: "ipAddresses",
+    name: "ip_addresses",
     type: "string",
     list: true,
   },
-  "resourceDescription",
+  "resource_description",
   "enabled",
   {
-    name: "batchQueues",
+    name: "batch_queues",
     type: BatchQueue,
     list: true,
   },
   // TODO: map these
-  // 'fileSystems',
-  // 'jobSubmissionInterfaces',
-  // 'dataMovementInterfaces',
-  "maxMemoryPerNode",
-  "gatewayUsageReporting",
-  "gatewayUsageModuleLoadCommand",
-  "gatewayUsageExecutable",
-  "cpusPerNode",
-  "defaultNodeCount",
-  "defaultCPUCount",
-  "defaultWalltime",
+  // 'file_systems',
+  // 'job_submission_interfaces',
+  // 'data_movement_interfaces',
+  "max_memory_per_node",
+  "gateway_usage_reporting",
+  "gateway_usage_module_load_command",
+  "gateway_usage_executable",
+  "cpus_per_node",
+  "default_node_count",
+  "default_cpu_count",
+  "default_walltime",
 ];
 
 export default class ComputeResourceDescription extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourcePolicy.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourcePolicy.js
index b40d6a0e3..a1d34cd47 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourcePolicy.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourcePolicy.js
@@ -1,11 +1,11 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "resourcePolicyId",
-  "computeResourceId",
-  "groupResourceProfileId",
+  "resource_policy_id",
+  "compute_resource_id",
+  "group_resource_profile_id",
   {
-    name: "allowedBatchQueues",
+    name: "allowed_batch_queues",
     type: "string",
     list: true,
   },
@@ -18,15 +18,15 @@ export default class ComputeResourcePolicy extends BaseModel {
 
   populateParentIdsOnBatchQueueResourcePolicy(batchQueueResourcePolicy) {
     // For new BatchQueueResourcePolicy instances, set the parent ids
-    batchQueueResourcePolicy.groupResourceProfileId = this.groupResourceProfileId;
-    batchQueueResourcePolicy.computeResourceId = this.computeResourceId;
+    batchQueueResourcePolicy.group_resource_profile_id = this.group_resource_profile_id;
+    batchQueueResourcePolicy.compute_resource_id = this.compute_resource_id;
     return batchQueueResourcePolicy;
   }
 
   validate() {
     let validationResults = {};
-    if (!this.allowedBatchQueues || this.allowedBatchQueues.length === 0) {
-      validationResults["allowedBatchQueues"] =
+    if (!this.allowed_batch_queues || this.allowed_batch_queues.length === 0) {
+      validationResults["allowed_batch_queues"] =
         "Must select at least one queue.";
     }
     return validationResults;
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceReservation.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceReservation.js
index fbd7c5d82..613bc6579 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceReservation.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ComputeResourceReservation.js
@@ -9,21 +9,21 @@ function currentTimeTopOfHour() {
   return d;
 }
 const FIELDS = [
-  "reservationId",
-  "reservationName",
+  "reservation_id",
+  "reservation_name",
   {
-    name: "queueNames",
+    name: "queue_names",
     type: "string",
     list: true,
   },
   {
-    name: "startTime",
-    type: Date,
+    name: "start_time",
+    type: "date",
     default: () => currentTimeTopOfHour(),
   },
   {
-    name: "endTime",
-    type: Date,
+    name: "end_time",
+    type: "date",
     default: () => currentTimeTopOfHour(),
   },
 ];
@@ -38,28 +38,28 @@ export default class ComputeResourceReservation extends BaseModel {
   }
   validate() {
     let validationResults = {};
-    if (this.isEmpty(this.reservationName)) {
-      validationResults["reservationName"] =
+    if (this.isEmpty(this.reservation_name)) {
+      validationResults["reservation_name"] =
         "Please provide the name of this reservation.";
     }
-    if (this.startTime > this.endTime) {
-      validationResults["endTime"] = "End time must be later than start time.";
+    if (this.start_time > this.end_time) {
+      validationResults["end_time"] = "End time must be later than start time.";
     }
-    if (this.isEmpty(this.queueNames)) {
-      validationResults["queueNames"] = "Please select at least one queue.";
+    if (this.isEmpty(this.queue_names)) {
+      validationResults["queue_names"] = "Please select at least one queue.";
     }
     return validationResults;
   }
   get isExpired() {
     const now = new Date();
-    return now > this.endTime;
+    return now > this.end_time;
   }
   get isActive() {
     const now = new Date();
-    return this.startTime < now && now < this.endTime;
+    return this.start_time < now && now < this.end_time;
   }
   get isUpcoming() {
     const now = new Date();
-    return now < this.startTime;
+    return now < this.start_time;
   }
 }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
index 83d38a988..aaa549d8e 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/CredentialSummary.js
@@ -6,16 +6,17 @@ const FIELDS = [
     name: "type",
     type: SummaryType,
   },
-  "gatewayId",
+  "gateway_id",
   "username",
-  "publicKey",
+  "public_key",
   {
-    name: "persistedTime",
-    type: Date,
+    name: "persisted_time",
+    type: "date",
   },
   "token",
   "description",
-  "userHasWriteAccess",
+  // merged onto the proto server-side by the WithAccess envelope.
+  "user_has_write_access",
 ];
 
 export default class CredentialSummary extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataProduct.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataProduct.js
index 507d5bdb8..bbef3076a 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataProduct.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataProduct.js
@@ -4,32 +4,37 @@ import DataReplicaLocation from "./DataReplicaLocation";
 import URL from "url-parse";
 
 const FIELDS = [
-  "productUri",
-  "gatewayId",
-  "parentProductUri",
-  "productName",
-  "productDescription",
-  "ownerName",
-  "dataProductType",
-  "productSize",
+  "product_uri",
+  "gateway_id",
+  "parent_product_uri",
+  "product_name",
+  "product_description",
+  "owner_name",
+  // wire enum NAME ("FILE" / "COLLECTION"); 0-sentinel "DATA_PRODUCT_TYPE_UNKNOWN".
+  "data_product_type",
+  "product_size",
   {
-    name: "creationTime",
+    name: "creation_time",
     type: "date",
   },
   {
-    name: "lastModifiedTime",
+    name: "last_modified_time",
     type: "date",
   },
-  "productMetadata",
+  // proto map<string,string>, arrives as a JSON object (e.g. {"mime-type": ...}).
+  "product_metadata",
   {
-    name: "replicaLocations",
+    name: "replica_locations",
     type: DataReplicaLocation,
     list: true,
   },
-  "downloadURL",
-  "isInputFileUpload",
-  "filesize",
-  "userHasWriteAccess",
+  // merged onto the proto server-side by the WithAccess envelope.
+  "is_owner",
+  {
+    name: "user_has_write_access",
+    type: "boolean",
+    default: true,
+  },
 ];
 
 const FILENAME_REGEX = /[^/]+$/;
@@ -42,9 +47,9 @@ export default class DataProduct extends BaseModel {
   }
 
   get filename() {
-    if (this.replicaLocations && this.replicaLocations.length > 0) {
-      const firstReplicaLocation = this.replicaLocations[0];
-      const fileURL = new URL(firstReplicaLocation.filePath);
+    if (this.replica_locations && this.replica_locations.length > 0) {
+      const firstReplicaLocation = this.replica_locations[0];
+      const fileURL = new URL(firstReplicaLocation.file_path);
       const filenameMatch = FILENAME_REGEX.exec(fileURL.pathname);
       if (filenameMatch) {
         return filenameMatch[0];
@@ -62,8 +67,8 @@ export default class DataProduct extends BaseModel {
   }
 
   get mimeType() {
-    return this.productMetadata && this.productMetadata["mime-type"]
-      ? this.productMetadata["mime-type"]
+    return this.product_metadata && this.product_metadata["mime-type"]
+      ? this.product_metadata["mime-type"]
       : null;
   }
 }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataReplicaLocation.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataReplicaLocation.js
index 064fead2a..c3ed6c078 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataReplicaLocation.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataReplicaLocation.js
@@ -1,27 +1,30 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "replicaId",
-  "productUri",
-  "replicaName",
-  "replicaDescription",
+  "replica_id",
+  "product_uri",
+  "replica_name",
+  "replica_description",
   {
-    name: "creationTime",
+    name: "creation_time",
     type: "date",
   },
   {
-    name: "lastModifiedTime",
+    name: "last_modified_time",
     type: "date",
   },
   {
-    name: "validUntilTime",
+    name: "valid_until_time",
     type: "date",
   },
-  "replicaLocationCategory",
-  "replicaPersistentType",
-  "storageResourceId",
-  "filePath",
-  "replicaMetadata",
+  // wire enum NAMEs ("GATEWAY_DATA_STORE" / "TRANSIENT"); 0-sentinels are
+  // "REPLICA_LOCATION_CATEGORY_UNKNOWN" / "REPLICA_PERSISTENT_TYPE_UNKNOWN".
+  "replica_location_category",
+  "replica_persistent_type",
+  "storage_resource_id",
+  "file_path",
+  // proto map<string,string>, arrives as a JSON object.
+  "replica_metadata",
 ];
 
 export default class DataReplicaLocation extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataType.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataType.js
index 78e425f37..80d70ade0 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataType.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/DataType.js
@@ -18,6 +18,7 @@ export default class DataType extends BaseEnum {
   }
 }
 DataType.init([
+  "DATA_TYPE_UNKNOWN",
   "STRING",
   "INTEGER",
   "FLOAT",
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ErrorModel.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ErrorModel.js
index 7b3d7765a..50fdb5714 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ErrorModel.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ErrorModel.js
@@ -1,16 +1,16 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "errorId",
+  "error_id",
   {
-    name: "creationTime",
+    name: "creation_time",
     type: "date",
   },
-  "actualErrorMessage",
-  "userFriendlyMessage",
-  "transientOrPersistent",
+  "actual_error_message",
+  "user_friendly_message",
+  "transient_or_persistent",
   {
-    name: "rootCauseErrorIdList",
+    name: "root_cause_error_id_list",
     type: "string",
     list: true,
   },
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js
index 71696876a..a6b674438 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js
@@ -8,50 +8,51 @@ import ProcessModel from "./ProcessModel";
 import UserConfigurationData from "./UserConfigurationData";
 
 const FIELDS = [
-  "experimentId",
-  "projectId",
-  "gatewayId",
+  "experiment_id",
+  "project_id",
+  "gateway_id",
+  // wire enum NAME ("SINGLE_APPLICATION"); 0-sentinel "EXPERIMENT_TYPE_UNKNOWN".
   {
-    name: "experimentType",
-    type: "number",
-    default: 0,
+    name: "experiment_type",
+    type: "string",
+    default: "EXPERIMENT_TYPE_UNKNOWN",
   },
-  "userName",
-  "experimentName",
+  "user_name",
+  "experiment_name",
   {
-    name: "creationTime",
+    name: "creation_time",
     type: "date",
   },
   "description",
-  "executionId",
+  "execution_id",
   {
-    name: "enableEmailNotification",
+    name: "enable_email_notification",
     type: "boolean",
     default: false,
   },
   {
-    name: "emailAddresses",
+    name: "email_addresses",
     type: "string",
     list: true,
   },
   {
-    name: "userConfigurationData",
+    name: "user_configuration_data",
     type: UserConfigurationData,
     default: BaseModel.defaultNewInstance(UserConfigurationData),
   },
   {
-    name: "experimentInputs",
+    name: "experiment_inputs",
     type: InputDataObjectType,
     list: true,
     default: BaseModel.defaultNewInstance(Array),
   },
   {
-    name: "experimentOutputs",
+    name: "experiment_outputs",
     type: OutputDataObjectType,
     list: true,
   },
   {
-    name: "experimentStatus",
+    name: "experiment_status",
     type: ExperimentStatus,
     list: true,
   },
@@ -66,8 +67,9 @@ const FIELDS = [
     list: true,
   },
   "workflow",
+  // merged onto the proto server-side by the WithAccess envelope.
   {
-    name: "userHasWriteAccess",
+    name: "user_has_write_access",
     type: "boolean",
     default: true,
   },
@@ -81,19 +83,19 @@ export default class Experiment extends BaseModel {
 
   validate() {
     let validationResults = {};
-    if (this.isEmpty(this.experimentName)) {
-      validationResults["experimentName"] =
+    if (this.isEmpty(this.experiment_name)) {
+      validationResults["experiment_name"] =
         "Please provide a name for this experiment.";
     }
-    if (this.isEmpty(this.projectId)) {
-      validationResults["projectId"] = "Please select a project.";
+    if (this.isEmpty(this.project_id)) {
+      validationResults["project_id"] = "Please select a project.";
     }
     return validationResults;
   }
 
   get latestStatus() {
-    if (this.experimentStatus && this.experimentStatus.length > 0) {
-      return this.experimentStatus[this.experimentStatus.length - 1];
+    if (this.experiment_status && this.experiment_status.length > 0) {
+      return this.experiment_status[this.experiment_status.length - 1];
     } else {
       return null;
     }
@@ -127,7 +129,7 @@ export default class Experiment extends BaseModel {
     return (
       (!this.latestStatus ||
         this.latestStatus.state === ExperimentState.CREATED) &&
-      this.userHasWriteAccess
+      this.user_has_write_access
     );
   }
 
@@ -144,40 +146,39 @@ export default class Experiment extends BaseModel {
   }
 
   get resourceHostId() {
-    return this.userConfigurationData &&
-      this.userConfigurationData.computationalResourceScheduling
-      ? this.userConfigurationData.computationalResourceScheduling
-          .resourceHostId
+    return this.user_configuration_data &&
+      this.user_configuration_data.computational_resource_scheduling
+      ? this.user_configuration_data.computational_resource_scheduling
+          .resource_host_id
       : null;
   }
 
   populateInputsOutputsFromApplicationInterface(applicationInterface) {
-    // Copy application inputs and outputs to the experiment
-    this.experimentInputs = applicationInterface.applicationInputs.map(
+    this.experiment_inputs = applicationInterface.application_inputs.map(
       (input) => input.clone()
     );
     this.evaluateInputDependencies();
-    this.experimentOutputs = applicationInterface.applicationOutputs.slice();
+    this.experiment_outputs = applicationInterface.application_outputs.slice();
   }
 
   evaluateInputDependencies() {
-    const inputValues = this._collectInputValues(this.experimentInputs);
-    for (const input of this.experimentInputs) {
+    const inputValues = this._collectInputValues(this.experiment_inputs);
+    for (const input of this.experiment_inputs) {
       input.evaluateDependencies(inputValues);
     }
   }
 
   getExperimentInput(inputName) {
-    return this.experimentInputs.find(inp => inp.name === inputName);
+    return this.experiment_inputs.find(inp => inp.name === inputName);
   }
 
   getExperimentOutput(outputName) {
-    return this.experimentOutputs.find(out => out.name === outputName);
+    return this.experiment_outputs.find(out => out.name === outputName);
   }
 
   _collectInputValues() {
     const result = {};
-    this.experimentInputs.forEach((inp) => {
+    this.experiment_inputs.forEach((inp) => {
       result[inp.name] = inp.value;
     });
     return result;
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSearchFields.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSearchFields.js
index 95b6b57c1..b4cb61147 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSearchFields.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSearchFields.js
@@ -2,6 +2,7 @@ import BaseEnum from "./BaseEnum";
 
 export default class ExperimentSearchFields extends BaseEnum {}
 ExperimentSearchFields.init([
+  "EXPERIMENT_SEARCH_FIELDS_UNKNOWN",
   "EXPERIMENT_NAME",
   "EXPERIMENT_DESC",
   "APPLICATION_ID",
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentState.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentState.js
index 6ac0150e4..d8bec5ba1 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentState.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentState.js
@@ -20,13 +20,14 @@ export default class ExperimentState extends BaseEnum {
   }
 }
 ExperimentState.init([
-  "CREATED",
-  "VALIDATED",
-  "SCHEDULED",
-  "LAUNCHED",
-  "EXECUTING",
-  "CANCELING",
-  "CANCELED",
-  "COMPLETED",
-  "FAILED",
+  "EXPERIMENT_STATE_UNKNOWN",
+  "EXPERIMENT_STATE_CREATED",
+  "EXPERIMENT_STATE_VALIDATED",
+  "EXPERIMENT_STATE_SCHEDULED",
+  "EXPERIMENT_STATE_LAUNCHED",
+  "EXPERIMENT_STATE_EXECUTING",
+  "EXPERIMENT_STATE_CANCELING",
+  "EXPERIMENT_STATE_CANCELED",
+  "EXPERIMENT_STATE_COMPLETED",
+  "EXPERIMENT_STATE_FAILED",
 ]);
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatistics.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatistics.js
index 08bd5dd6a..00b758e18 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatistics.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatistics.js
@@ -2,38 +2,38 @@ import BaseModel from "./BaseModel";
 import ExperimentSummary from "./ExperimentSummary";
 
 const FIELDS = [
-  "allExperimentCount",
-  "completedExperimentCount",
-  "cancelledExperimentCount",
-  "failedExperimentCount",
-  "createdExperimentCount",
-  "runningExperimentCount",
+  "all_experiment_count",
+  "completed_experiment_count",
+  "cancelled_experiment_count",
+  "failed_experiment_count",
+  "created_experiment_count",
+  "running_experiment_count",
   {
-    name: "allExperiments",
+    name: "all_experiments",
     type: ExperimentSummary,
     list: true,
   },
   {
-    name: "completedExperiments",
+    name: "completed_experiments",
     type: ExperimentSummary,
     list: true,
   },
   {
-    name: "failedExperiments",
+    name: "failed_experiments",
     type: ExperimentSummary,
     list: true,
   },
   {
-    name: "cancelledExperiments",
+    name: "cancelled_experiments",
     type: ExperimentSummary,
     list: true,
   },
   {
-    name: "createdExperiments",
+    name: "created_experiments",
     type: ExperimentSummary,
     list: true,
   },
-  { name: "runningExperiments", type: ExperimentSummary, list: true },
+  { name: "running_experiments", type: ExperimentSummary, list: true },
 ];
 
 export default class ExperimentStatistics extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatus.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatus.js
index a18761836..291110d09 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatus.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentStatus.js
@@ -7,11 +7,11 @@ const FIELDS = [
     type: ExperimentState,
   },
   {
-    name: "timeOfStateChange",
+    name: "time_of_state_change",
     type: "date",
   },
   "reason",
-  "statusId",
+  "status_id",
 ];
 
 export default class ExperimentStatus extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSummary.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSummary.js
index 68d56a587..286e2feb8 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSummary.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/ExperimentSummary.js
@@ -4,28 +4,29 @@ import ExperimentStatus from "./ExperimentStatus";
 import ExperimentState from "./ExperimentState";
 
 const FIELDS = [
-  "experimentId",
-  "projectId",
-  "gatewayId",
+  "experiment_id",
+  "project_id",
+  "gateway_id",
   {
-    name: "creationTime",
+    name: "creation_time",
     type: "date",
   },
-  "userName",
+  "user_name",
   "name",
   "description",
-  "executionId",
-  "resourceHostId",
+  "execution_id",
+  "resource_host_id",
   {
-    name: "experimentStatus",
+    name: "experiment_status",
     type: ExperimentState,
   },
   {
-    name: "statusUpdateTime",
+    name: "status_update_time",
     type: "date",
   },
+  // merged onto the proto server-side by the WithAccess envelope.
   {
-    name: "userHasWriteAccess",
+    name: "user_has_write_access",
     type: "boolean",
     default: false,
   },
@@ -42,16 +43,16 @@ export default class ExperimentSummary extends BaseModel {
 
   convertToExperiment() {
     // Purpose of this is to be able to access computed properties on
-    // Experiment.js
+    // Experiment.js. The summary carries a single experiment_status enum +
+    // status_update_time; Experiment expects an experiment_status LIST of
+    // ExperimentStatus, so reshape those two while passing the rest through.
     return new Experiment(
       Object.assign({}, this, {
-        // Most properties are named the same as on Experiment, but the
-        // following require some conversion
-        experimentName: this.name,
-        experimentStatus: [
+        experiment_name: this.name,
+        experiment_status: [
           new ExperimentStatus({
-            state: this.experimentStatus,
-            timeOfStateChange: this.statusUpdateTime,
+            state: this.experiment_status,
+            time_of_state_change: this.status_update_time,
           }),
         ],
       })
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/FullExperiment.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/FullExperiment.js
index 03aa10e38..34fff3c48 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/FullExperiment.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/FullExperiment.js
@@ -7,7 +7,7 @@ import Job from "./Job";
 import Project from "./Project";
 
 const FIELDS = [
-  "experimentId",
+  "experiment_id",
   {
     name: "experiment",
     type: Experiment,
@@ -17,30 +17,30 @@ const FIELDS = [
     type: Project,
   },
   {
-    name: "applicationModule",
+    name: "application_module",
     type: ApplicationModule,
   },
   {
-    name: "computeResource",
+    name: "compute_resource",
     type: ComputeResourceDescription,
   },
   {
-    name: "outputDataProducts",
+    name: "output_data_products",
     type: DataProduct,
     list: true,
   },
   {
-    name: "inputDataProducts",
+    name: "input_data_products",
     type: DataProduct,
     list: true,
   },
   {
-    name: "jobDetails",
+    name: "job_details",
     type: Job,
     list: true,
   },
   {
-    name: "outputViews",
+    name: "output_views",
     type: Object,
   },
 ];
@@ -55,11 +55,13 @@ export default class FullExperiment extends BaseModel {
   }
 
   get applicationName() {
-    return this.applicationModule ? this.applicationModule.appModuleName : null;
+    return this.application_module
+      ? this.application_module.app_module_name
+      : null;
   }
 
   get computeHostName() {
-    return this.computeResource ? this.computeResource.hostName : null;
+    return this.compute_resource ? this.compute_resource.host_name : null;
   }
 
   get resourceHostId() {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GatewayResourceProfile.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GatewayResourceProfile.js
index 35e881ffa..4e698efa6 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GatewayResourceProfile.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GatewayResourceProfile.js
@@ -2,17 +2,18 @@ import BaseModel from "./BaseModel";
 import StoragePreference from "./StoragePreference";
 
 const FIELDS = [
-  "gatewayID",
-  "credentialStoreToken",
-  "computeResourcePreferences",
+  "gateway_id",
+  "credential_store_token",
+  "compute_resource_preferences",
   {
-    name: "storagePreferences",
+    name: "storage_preferences",
     type: StoragePreference,
     list: true,
   },
-  "identityServerTenant",
-  "identityServerPwdCredToken",
-  "userHasWriteAccess",
+  "identity_server_tenant",
+  "identity_server_pwd_cred_token",
+  // merged onto the proto server-side by the WithAccess envelope.
+  "user_has_write_access",
 ];
 
 export default class GatewayResourceProfile extends BaseModel {
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Group.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Group.js
index 533745a5e..3f6d44da6 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Group.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/Group.js
@@ -3,7 +3,7 @@ import BaseModel from "./BaseModel";
 const FIELDS = [
   "id",
   "name",
-  "ownerId",
+  "owner_id",
   "description",
   {
     name: "members",
@@ -17,24 +17,26 @@ const FIELDS = [
     list: true,
     default: BaseModel.defaultNewInstance(Array),
   },
+  // The six access flags are merged onto the GroupModel proto server-side by the
+  // WithGroupAccess envelope (not on the proto itself).
   {
-    name: "isOwner",
+    name: "is_owner",
     type: "boolean",
     default: true,
   },
   {
-    name: "isAdmin",
+    name: "is_admin",
     type: "boolean",
     default: false,
   },
   {
-    name: "isMember",
+    name: "is_member",
     type: "boolean",
     default: true,
   },
-  "isGatewayAdminsGroup",
-  "isReadOnlyGatewayAdminsGroup",
-  "isDefaultGatewayUsersGroup",
+  "is_gateway_admins_group",
+  "is_read_only_gateway_admins_group",
+  "is_default_gateway_users_group",
 ];
 
 export default class Group extends BaseModel {
@@ -55,10 +57,10 @@ export default class Group extends BaseModel {
    * Return true if group is either the "Gateway Admins" or the "Readonly Admins" group.
    */
   get isAdminGroup() {
-    return this.isReadOnlyGatewayAdminsGroup || this.isGatewayAdminsGroup;
+    return this.is_read_only_gateway_admins_group || this.is_gateway_admins_group;
   }
 
   get userHasWriteAccess() {
-    return this.isOwner || this.isAdmin;
+    return this.is_owner || this.is_admin;
   }
 }
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
index d43346f7e..74d4b2c95 100644
--- 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
@@ -1,10 +1,10 @@
 import BaseModel from "./BaseModel";
 
 const FIELDS = [
-  "resourceId",
-  "groupResourceProfileId",
-  "configName",
-  "configValue",
+  "resource_id",
+  "group_resource_profile_id",
+  "config_name",
+  "config_value",
 ];
 
 export default class GroupAccountSSHProvisionerConfig extends BaseModel {
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 f13a317fe..0975be131 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
@@ -4,25 +4,26 @@ import SlurmComputeResourcePreference from "./SlurmComputeResourcePreference";
 import AwsComputeResourcePreference from "./AwsComputeResourcePreference";
 
 const FIELDS = [
-  "computeResourceId",
-  "groupResourceProfileId",
+  "compute_resource_id",
+  "group_resource_profile_id",
   {
-    name: "overridebyAiravata",
+    name: "override_by_airavata",
     type: "boolean",
     default: true,
   },
-  "loginUserName",
-  "preferredJobSubmissionProtocol",
-  "preferredDataMovementProtocol",
-  "scratchLocation",
-  "resourceSpecificCredentialStoreToken",
+  "login_user_name",
+  // wire enum member NAMES ("SSH"/"SFTP"); sentinels *_PROTOCOL_UNKNOWN.
+  "preferred_job_submission_protocol",
+  "preferred_data_movement_protocol",
+  "scratch_location",
+  "resource_specific_credential_store_token",
   {
-    name: "resourceType",
+    name: "resource_type",
     type: ResourceType,
-    required: true,
   },
+  // oneof wrapper {slurm:{...}} | {aws:{...}}; coerced into a child model below.
   {
-    name: "specificPreferences",
+    name: "specific_preferences",
     type: null,
   },
 ];
@@ -34,153 +35,115 @@ const PREFERENCE_MODEL_MAP = {
 
 export default class GroupComputeResourcePreference extends BaseModel {
   constructor(data = {}) {
-    const topLevelAllocationProjectNumber = data.allocationProjectNumber;
-    const rawSpecificPreferences = data.specificPreferences;
+    const rawSpecificPreferences = data.specific_preferences;
 
     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;
-          }
-        }
-      }
+    if (rawSpecificPreferences !== undefined && rawSpecificPreferences !== null) {
+      this.specific_preferences = rawSpecificPreferences;
     }
 
     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;
+    const json = { ...this };
+    if (this.resource_type && this.resource_type.value !== undefined) {
+      json.resource_type = this.resource_type.value;
+    } else if (this.resource_type && this.resource_type.name) {
+      json.resource_type = this.resource_type.name;
     }
 
-    let specificPrefsPayload = this.specificPreferences;
+    let specificPrefsPayload = this.specific_preferences;
     if (
-      this.specificPreferences &&
-      typeof this.specificPreferences.toJSON === "function"
+      this.specific_preferences &&
+      typeof this.specific_preferences.toJSON === "function"
     ) {
-      specificPrefsPayload = this.specificPreferences.toJSON();
+      specificPrefsPayload = this.specific_preferences.toJSON();
     }
 
+    // re-wrap into the proto oneof shape the wire expects.
     if (specificPrefsPayload && this.isResourceType("SLURM")) {
-      json.specificPreferences = {slurm: specificPrefsPayload};
+      json.specific_preferences = { slurm: specificPrefsPayload };
     } else if (specificPrefsPayload && this.isResourceType("AWS")) {
-      json.specificPreferences = {aws: specificPrefsPayload};
+      json.specific_preferences = { aws: specificPrefsPayload };
     } else if (specificPrefsPayload) {
-      json.specificPreferences = specificPrefsPayload;
+      json.specific_preferences = specificPrefsPayload;
     } else {
-      json.specificPreferences = null;
+      json.specific_preferences = 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;
+    if (!this.resource_type) {
+      this.specific_preferences = null;
       return;
     }
 
-    if (!this.resourceType.name) {
+    if (!this.resource_type.name) {
       return;
     }
 
     if (
-      this.specificPreferences &&
-      this.specificPreferences instanceof BaseModel
+      this.specific_preferences &&
+      this.specific_preferences instanceof BaseModel
     ) {
       return;
     }
     let rawData =
-      this.specificPreferences && typeof this.specificPreferences === "object"
-        ? this.specificPreferences
+      this.specific_preferences && typeof this.specific_preferences === "object"
+        ? this.specific_preferences
         : null;
 
+    // unwrap the proto oneof wrapper ({slurm:{...}} / {aws:{...}}).
     if (rawData && !(rawData instanceof BaseModel)) {
-      if (this.resourceType.name === 'SLURM' && 'slurm' in rawData) {
+      if (this.resource_type.name === "SLURM" && "slurm" in rawData) {
         rawData = rawData.slurm;
-      } else if (this.resourceType.name === 'AWS') {
-        if ('aws' in rawData) {
-          rawData = rawData.aws;
-        }
+      } else if (this.resource_type.name === "AWS" && "aws" in rawData) {
+        rawData = rawData.aws;
       }
     }
 
-    const PreferenceModel = PREFERENCE_MODEL_MAP[this.resourceType.name];
+    const PreferenceModel = PREFERENCE_MODEL_MAP[this.resource_type.name];
     if (PreferenceModel) {
       const newPref = rawData
         ? new PreferenceModel(rawData)
         : new PreferenceModel();
-      this.specificPreferences = newPref;
+      this.specific_preferences = newPref;
     } else {
-      this.specificPreferences = rawData;
+      this.specific_preferences = rawData;
     }
   }
 
   resetSpecificPreferences(data = null) {
-    if (!this.resourceType) {
-      this.specificPreferences = null;
+    if (!this.resource_type) {
+      this.specific_preferences = null;
       return;
     }
     if (data && typeof data === "object") {
-      this.specificPreferences = data;
+      this.specific_preferences = data;
     } else {
-      this.specificPreferences = null;
+      this.specific_preferences = null;
     }
     this._coerceSpecificPreferences();
   }
 
   isResourceType(resourceTypeName) {
-    return (
-      !!this.resourceType && this.resourceType.name === resourceTypeName
-    );
+    return !!this.resource_type && this.resource_type.name === resourceTypeName;
   }
 
   _ensureSpecificPreferences() {
-    if (!this.specificPreferences) {
+    if (!this.specific_preferences) {
       this._coerceSpecificPreferences();
     }
   }
 
+  // SLURM-only convenience accessors bridging into the child preference model.
   _getSlurmField(fieldName, defaultValue) {
-    if (this.isResourceType("SLURM") && this.specificPreferences) {
-      return this.specificPreferences[fieldName];
+    if (this.isResourceType("SLURM") && this.specific_preferences) {
+      return this.specific_preferences[fieldName];
     }
     return defaultValue;
   }
@@ -190,41 +153,41 @@ export default class GroupComputeResourcePreference extends BaseModel {
       return;
     }
     this._ensureSpecificPreferences();
-    if (this.specificPreferences) {
-      this.specificPreferences[fieldName] = value;
+    if (this.specific_preferences) {
+      this.specific_preferences[fieldName] = value;
     }
   }
 
-  get allocationProjectNumber() {
-    return this._getSlurmField("allocationProjectNumber");
+  get allocation_project_number() {
+    return this._getSlurmField("allocation_project_number");
   }
 
-  set allocationProjectNumber(value) {
-    this._setSlurmField("allocationProjectNumber", value);
+  set allocation_project_number(value) {
+    this._setSlurmField("allocation_project_number", value);
   }
 
-  get preferredBatchQueue() {
-    return this._getSlurmField("preferredBatchQueue");
+  get preferred_batch_queue() {
+    return this._getSlurmField("preferred_batch_queue");
   }
 
-  set preferredBatchQueue(value) {
-    this._setSlurmField("preferredBatchQueue", value);
+  set preferred_batch_queue(value) {
+    this._setSlurmField("preferred_batch_queue", value);
   }
 
-  get qualityOfService() {
-    return this._getSlurmField("qualityOfService");
+  get quality_of_service() {
+    return this._getSlurmField("quality_of_service");
   }
 
-  set qualityOfService(value) {
-    this._setSlurmField("qualityOfService", value);
+  set quality_of_service(value) {
+    this._setSlurmField("quality_of_service", value);
   }
 
-  get usageReportingGatewayId() {
-    return this._getSlurmField("usageReportingGatewayId");
+  get usage_reporting_gateway_id() {
+    return this._getSlurmField("usage_reporting_gateway_id");
   }
 
-  set usageReportingGatewayId(value) {
-    this._setSlurmField("usageReportingGatewayId", value);
+  set usage_reporting_gateway_id(value) {
+    this._setSlurmField("usage_reporting_gateway_id", value);
   }
 
   get reservations() {
@@ -237,18 +200,18 @@ export default class GroupComputeResourcePreference extends BaseModel {
 
   validate() {
     let validationResults = {};
-    if (this.isEmpty(this.loginUserName)) {
-      validationResults["loginUserName"] = "Please provide a login username.";
+    if (this.isEmpty(this.login_user_name)) {
+      validationResults["login_user_name"] = "Please provide a login username.";
     }
-    if (this.isEmpty(this.scratchLocation)) {
-      validationResults["scratchLocation"] =
+    if (this.isEmpty(this.scratch_location)) {
+      validationResults["scratch_location"] =
         "Please provide a scratch location.";
     }
-    if (!this.resourceType) {
-      validationResults["resourceType"] = "Please select a resource type.";
+    if (!this.resource_type) {
+      validationResults["resource_type"] = "Please select a resource type.";
     }
-    if (this.resourceType && this.specificPreferences) {
-      const specificValidation = this.specificPreferences.validate();
+    if (this.resource_type && this.specific_preferences) {
+      const specificValidation = this.specific_preferences.validate();
       if (specificValidation && Object.keys(specificValidation).length > 0) {
         Object.assign(validationResults, specificValidation);
       }
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupPermission.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupPermission.js
index 14d38aadd..37e389ee4 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupPermission.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupPermission.js
@@ -10,8 +10,9 @@ export default class GroupPermission extends BaseModel {
           name: "group",
           type: Group,
         },
+        // permission_type arrives as the enum member NAME string.
         {
-          name: "permissionType",
+          name: "permission_type",
           type: ResourcePermissionType,
         },
       ],
diff --git a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
index 4866dce7d..e9299a6ed 100644
--- a/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
+++ b/airavata-django-portal/django_airavata/apps/api/static/django_airavata_api/js/models/GroupResourceProfile.js
@@ -4,37 +4,39 @@ import ComputeResourcePolicy from "./ComputeResourcePolicy";
 import GroupComputeResourcePreference from "./GroupComputeResourcePreference";
 
 const FIELDS = [
-  "gatewayId",
-  "groupResourceProfileId",
-  "groupResourceProfileName",
+  "gateway_id",
+  "group_resource_profile_id",
+  "group_resource_profile_name",
   {
-    name: "computePreferences",
+    name: "compute_preferences",
     type: GroupComputeResourcePreference,
     list: true,
     default: BaseModel.defaultNewInstance(Array),
   },
   {
-    name: "computeResourcePolicies",
+    name: "compute_resource_policies",
     type: ComputeResourcePolicy,
     list: true,
... 18517 lines suppressed ...