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 169a10466 feat(portal): repoint experiment + group-resource-profile 
writes to gRPC (Track D, D3) (#178)
169a10466 is described below

commit 169a1046640744849168767d095425e55050940e
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 21:15:42 2026 -0400

    feat(portal): repoint experiment + group-resource-profile writes to gRPC 
(Track D, D3) (#178)
    
    Migrate the two deep write families from the Thrift client to the gRPC
    facades:
    - ExperimentViewSet perform_create/perform_update/launch/cancel ->
      research.create_/update_experiment, get_experiment (read-back),
      terminate_experiment
    - GroupResourceProfileViewSet perform_create/perform_update/perform_destroy
      -> compute.create_/update_/remove_group_resource_profile and the three
      remove_group_* policy/pref calls (facade arg order verified; the large
      OrderedDict->Thrift pre-processing in perform_update is unchanged)
    
    Add grpc_requests reverse adapters: experiment (+ _user_configuration_data
    and _computational_resource_scheduling; reuses the input/output reverse
    adapters), and group_resource_profile (+ the compute-pref slurm/aws proto
    oneof, reservations, SSH configs, both policy types). The GRP protocol
    enums reuse the inverse name maps added in the previous batch; experiment_
    type goes through the prefix-tolerant _proto_enum.
    
    Verified: manage.py check clean; offline thrift->proto->thrift round-trips
    confirm the ExperimentType enum, the userConfiguration/scheduling nesting,
    and the GRP slurm-union + divergent JSP_CLOUD enum all survive. (Live
    create is backend-blocked for these in the dev env -- experiment needs a
    valid app-interface entity, GRP create needs full user context -- the same
    limitations noted for their reads.)
    
    Note (reported by the codegen pass): experiment_util.launch/clone live in
    the external airavata_django_portal_sdk package and are still Thrift
    (incl. the launchExperiment RPC) -- a separate migration.
    
    Reverse adapters drafted by parallel subagents, integrated + validated here.
---
 .../django_airavata/apps/api/grpc_requests.py      | 213 +++++++++++++++++++++
 .../django_airavata/apps/api/views.py              |  52 +++--
 2 files changed, 236 insertions(+), 29 deletions(-)

diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py 
b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
index 41b309b98..b5b40806e 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
@@ -20,11 +20,17 @@ from airavata.model.appcatalog.computeresource.ttypes 
import (
 from airavata.model.appcatalog.parallelism.ttypes import (
     ApplicationParallelismType as _ThriftParallelismType,
 )
+from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+    ResourceType as _ThriftResourceType,
+)
 from airavata.model.appcatalog.parser.ttypes import IOType as _ThriftIOType
 from airavata.model.application.io.ttypes import DataType as _ThriftDataType
 from airavata.model.data.movement.ttypes import (
     DataMovementProtocol as _ThriftDataMovementProtocol,
 )
+from airavata.model.experiment.ttypes import (
+    ExperimentType as _ThriftExperimentType,
+)
 from airavata.model.workspace.ttypes import (
     NotificationPriority as _ThriftNotificationPriority,
 )
@@ -330,3 +336,210 @@ def gateway_resource_profile(t):
         identity_server_tenant=t.identityServerTenant or '',
         identity_server_pwd_cred_token=t.identityServerPwdCredToken or '',
     )
+
+
+# --- Experiment tree (write direction) -------------------------------------
+# Reverse of grpc_adapters.experiment. The write path carries only what the
+# user submitted; status/errors/processes/workflow are server-managed.
+
+
+def _computational_resource_scheduling(t):
+    """Thrift ``ComputationalResourceSchedulingModel`` -> proto message."""
+    return 
_pb2("scheduling.scheduling_pb2").ComputationalResourceSchedulingModel(
+        resource_host_id=t.resourceHostId or '',
+        total_cpu_count=t.totalCPUCount or 0,
+        node_count=t.nodeCount or 0,
+        number_of_threads=t.numberOfThreads or 0,
+        queue_name=t.queueName or '',
+        wall_time_limit=t.wallTimeLimit or 0,
+        total_physical_memory=t.totalPhysicalMemory or 0,
+        chessis_number=t.chessisNumber or '',
+        static_working_dir=t.staticWorkingDir or '',
+        override_login_user_name=t.overrideLoginUserName or '',
+        override_scratch_location=t.overrideScratchLocation or '',
+        override_allocation_project_number=t.overrideAllocationProjectNumber 
or '',
+        m_group_count=t.mGroupCount or 0,
+    )
+
+
+def _user_configuration_data(t):
+    """Thrift ``UserConfigurationDataModel`` -> proto message."""
+    ucd = _pb2("experiment.experiment_pb2").UserConfigurationDataModel(
+        airavata_auto_schedule=bool(t.airavataAutoSchedule),
+        override_manual_scheduled_params=bool(t.overrideManualScheduledParams),
+        share_experiment_publicly=bool(t.shareExperimentPublicly),
+        throttle_resources=bool(t.throttleResources),
+        user_dn=t.userDN or '',
+        generate_cert=bool(t.generateCert),
+        input_storage_resource_id=t.inputStorageResourceId or '',
+        output_storage_resource_id=t.outputStorageResourceId or '',
+        experiment_data_dir=t.experimentDataDir or '',
+        use_user_cr_pref=bool(t.useUserCRPref),
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        auto_scheduled_comp_resource_scheduling_list=[
+            _computational_resource_scheduling(s)
+            for s in (t.autoScheduledCompResourceSchedulingList or [])],
+    )
+    if t.computationalResourceScheduling is not None:
+        ucd.computational_resource_scheduling.CopyFrom(
+            
_computational_resource_scheduling(t.computationalResourceScheduling))
+    return ucd
+
+
+def experiment(t):
+    """Thrift ``ExperimentModel`` -> proto ``ExperimentModel`` request message.
+
+    Only the user-submitted fields are populated; experiment_status, errors and
+    processes are server-managed (left empty), workflow omitted.
+    """
+    exp_pb = _pb2("experiment.experiment_pb2")
+    e = exp_pb.ExperimentModel(
+        experiment_id=t.experimentId or '',
+        project_id=t.projectId or '',
+        gateway_id=t.gatewayId or '',
+        experiment_type=_proto_enum(
+            exp_pb.ExperimentType, _ThriftExperimentType, t.experimentType,
+            'EXPERIMENT_TYPE_'),
+        user_name=t.userName or '',
+        experiment_name=t.experimentName or '',
+        description=t.description or '',
+        execution_id=t.executionId or '',
+        enable_email_notification=bool(t.enableEmailNotification),
+        email_addresses=list(t.emailAddresses or []),
+        experiment_inputs=[
+            _input_data_object(i) for i in (t.experimentInputs or [])],
+        experiment_outputs=[
+            _output_data_object(o) for o in (t.experimentOutputs or [])],
+    )
+    if t.userConfigurationData is not None:
+        e.user_configuration_data.CopyFrom(
+            _user_configuration_data(t.userConfigurationData))
+    return e
+
+
+# --- Group resource profile (write direction) ------------------------------
+# Reverse of grpc_adapters.group_resource_profile. Reuses the _proto_enum_rev
+# protocol maps defined above.
+
+
+def _grp_pb2():
+    return _pb2("appcatalog.groupresourceprofile.group_resource_profile_pb2")
+
+
+def _reverse_compute_resource_reservation(t):
+    return _grp_pb2().ComputeResourceReservation(
+        reservation_id=t.reservationId or '',
+        reservation_name=t.reservationName or '',
+        queue_names=list(t.queueNames or []),
+        start_time=t.startTime or 0,
+        end_time=t.endTime or 0,
+    )
+
+
+def _reverse_group_account_ssh_provisioner_config(t):
+    return _grp_pb2().GroupAccountSSHProvisionerConfig(
+        resource_id=t.resourceId or '',
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        config_name=t.configName or '',
+        config_value=t.configValue or '',
+    )
+
+
+def _reverse_slurm_compute_resource_preference(t):
+    return _grp_pb2().SlurmComputeResourcePreference(
+        allocation_project_number=t.allocationProjectNumber or '',
+        preferred_batch_queue=t.preferredBatchQueue or '',
+        quality_of_service=t.qualityOfService or '',
+        usage_reporting_gateway_id=t.usageReportingGatewayId or '',
+        ssh_account_provisioner=t.sshAccountProvisioner or '',
+        group_ssh_account_provisioner_configs=[
+            _reverse_group_account_ssh_provisioner_config(c)
+            for c in (t.groupSSHAccountProvisionerConfigs or [])],
+        ssh_account_provisioner_additional_info=(
+            t.sshAccountProvisionerAdditionalInfo or ''),
+        reservations=[
+            _reverse_compute_resource_reservation(r)
+            for r in (t.reservations or [])],
+    )
+
+
+def _reverse_aws_compute_resource_preference(t):
+    return _grp_pb2().AwsComputeResourcePreference(
+        region=t.region or '',
+        preferred_ami_id=t.preferredAmiId or '',
+        preferred_instance_type=t.preferredInstanceType or '',
+    )
+
+
+def _reverse_group_compute_resource_preference(t):
+    grp = _grp_pb2()
+    cr = _pb2("appcatalog.computeresource.compute_resource_pb2")
+    dm = _pb2("data.movement.data_movement_pb2")
+    msg = grp.GroupComputeResourcePreference(
+        compute_resource_id=t.computeResourceId or '',
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        override_by_airavata=bool(t.overridebyAiravata),
+        login_user_name=t.loginUserName or '',
+        scratch_location=t.scratchLocation or '',
+        preferred_job_submission_protocol=_proto_enum_rev(
+            cr.JobSubmissionProtocol, _JOB_SUBMISSION_PROTOCOL_REV,
+            t.preferredJobSubmissionProtocol),
+        preferred_data_movement_protocol=_proto_enum_rev(
+            dm.DataMovementProtocol, _DATA_MOVEMENT_PROTOCOL_REV,
+            t.preferredDataMovementProtocol),
+        resource_specific_credential_store_token=(
+            t.resourceSpecificCredentialStoreToken or ''),
+        resource_type=_proto_enum(
+            grp.ResourceType, _ThriftResourceType, t.resourceType),
+    )
+    sp = getattr(t, 'specificPreferences', None)
+    if sp is not None:
+        if t.resourceType == _ThriftResourceType.AWS and getattr(sp, 'aws', 
None):
+            
msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
+                aws=_reverse_aws_compute_resource_preference(sp.aws)))
+        elif getattr(sp, 'slurm', None):
+            
msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
+                slurm=_reverse_slurm_compute_resource_preference(sp.slurm)))
+    return msg
+
+
+def _reverse_compute_resource_policy(t):
+    return _grp_pb2().ComputeResourcePolicy(
+        resource_policy_id=t.resourcePolicyId or '',
+        compute_resource_id=t.computeResourceId or '',
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        allowed_batch_queues=list(t.allowedBatchQueues or []),
+    )
+
+
+def _reverse_batch_queue_resource_policy(t):
+    return _grp_pb2().BatchQueueResourcePolicy(
+        resource_policy_id=t.resourcePolicyId or '',
+        compute_resource_id=t.computeResourceId or '',
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        queuename=t.queuename or '',
+        max_allowed_nodes=t.maxAllowedNodes or 0,
+        max_allowed_cores=t.maxAllowedCores or 0,
+        max_allowed_walltime=t.maxAllowedWalltime or 0,
+    )
+
+
+def group_resource_profile(t):
+    """Thrift ``GroupResourceProfile`` -> proto message."""
+    return _grp_pb2().GroupResourceProfile(
+        gateway_id=t.gatewayId or '',
+        group_resource_profile_id=t.groupResourceProfileId or '',
+        group_resource_profile_name=t.groupResourceProfileName or '',
+        compute_preferences=[
+            _reverse_group_compute_resource_preference(p)
+            for p in (t.computePreferences or [])],
+        compute_resource_policies=[
+            _reverse_compute_resource_policy(p)
+            for p in (t.computeResourcePolicies or [])],
+        batch_queue_resource_policies=[
+            _reverse_batch_queue_resource_policy(p)
+            for p in (t.batchQueueResourcePolicies or [])],
+        creation_time=t.creationTime or 0,
+        updated_time=t.updatedTime or 0,
+        default_credential_store_token=t.defaultCredentialStoreToken or '',
+    )
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py 
b/airavata-django-portal/django_airavata/apps/api/views.py
index 5eed8df0e..1bd7876fc 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -228,8 +228,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
         experiment = serializer.save(
             gatewayId=self.gateway_id,
             userName=self.username)
-        experiment_id = self.request.airavata_client.createExperiment(
-            self.authz_token, self.gateway_id, experiment)
+        experiment_id = self.request.airavata.research.create_experiment(
+            self.gateway_id, grpc_requests.experiment(experiment))
         self._update_workspace_preferences(
             project_id=experiment.projectId,
             
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
@@ -240,8 +240,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
         experiment = serializer.save(
             gatewayId=self.gateway_id,
             userName=self.username)
-        self.request.airavata_client.updateExperiment(
-            self.authz_token, experiment.experimentId, experiment)
+        self.request.airavata.research.update_experiment(
+            experiment.experimentId, grpc_requests.experiment(experiment))
         self._update_workspace_preferences(
             project_id=experiment.projectId,
             
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
@@ -250,12 +250,12 @@ class ExperimentViewSet(mixins.CreateModelMixin,
     @action(methods=['post'], detail=True)
     def launch(self, request, experiment_id=None):
         try:
-            experiment = request.airavata_client.getExperiment(
-                self.authz_token, experiment_id)
+            experiment = grpc_adapters.experiment(
+                request.airavata.research.get_experiment(experiment_id))
             if (experiment.enableEmailNotification):
                 experiment.emailAddresses = [request.user.email]
-            request.airavata_client.updateExperiment(
-                self.authz_token, experiment_id, experiment)
+            request.airavata.research.update_experiment(
+                experiment_id, grpc_requests.experiment(experiment))
             experiment_util.launch(request, experiment_id)
             return Response({'success': True})
         except Exception as e:
@@ -284,8 +284,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
     @action(methods=['post'], detail=True)
     def cancel(self, request, experiment_id=None):
         try:
-            request.airavata_client.terminateExperiment(
-                request.authz_token, experiment_id, self.gateway_id)
+            request.airavata.research.terminate_experiment(
+                experiment_id, self.gateway_id)
             return Response({'success': True})
         except Exception as e:
             log.exception("Cancel action has thrown the following error", 
extra={'request': request})
@@ -972,13 +972,10 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
     def perform_create(self, serializer):
         group_resource_profile = serializer.save()
         group_resource_profile.gatewayId = self.gateway_id
-        group_resource_profile_id = 
self.request.airavata_client.createGroupResourceProfile(
-            authzToken=self.authz_token, 
groupResourceProfile=group_resource_profile)
-        group_resource_profile.groupResourceProfileId = 
group_resource_profile_id
-        # Update the creationTime field on the group resource profile
-        new_group_resource_profile = 
self.request.airavata_client.getGroupResourceProfile(
-            self.authz_token, group_resource_profile_id)
-        group_resource_profile.creationTime = 
new_group_resource_profile.creationTime
+        created = self.request.airavata.compute.create_group_resource_profile(
+            grpc_requests.group_resource_profile(group_resource_profile))
+        group_resource_profile.groupResourceProfileId = 
created.group_resource_profile_id
+        group_resource_profile.creationTime = created.creation_time
 
     def perform_update(self, serializer):
         original_instance = serializer.instance
@@ -986,19 +983,16 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
         grp = serializer.save()
         for removed_compute_resource_preference \
                 in grp._removed_compute_resource_preferences:
-            self.request.airavata_client.removeGroupComputePrefs(
-                self.authz_token,
-                removed_compute_resource_preference.computeResourceId,
-                removed_compute_resource_preference.groupResourceProfileId)
+            self.request.airavata.compute.remove_group_compute_prefs(
+                removed_compute_resource_preference.groupResourceProfileId,
+                removed_compute_resource_preference.computeResourceId)
         for removed_compute_resource_policy \
                 in grp._removed_compute_resource_policies:
-            self.request.airavata_client.removeGroupComputeResourcePolicy(
-                self.authz_token,
+            self.request.airavata.compute.remove_group_compute_resource_policy(
                 removed_compute_resource_policy.resourcePolicyId)
         for removed_batch_queue_resource_policy \
                 in grp._removed_batch_queue_resource_policies:
-            self.request.airavata_client.removeGroupBatchQueueResourcePolicy(
-                self.authz_token,
+            
self.request.airavata.compute.remove_group_batch_queue_resource_policy(
                 removed_batch_queue_resource_policy.resourcePolicyId)
         if hasattr(grp, 'computePreferences') and grp.computePreferences:
             from collections import OrderedDict
@@ -1150,12 +1144,12 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
                                         from 
airavata.model.appcatalog.groupresourceprofile.ttypes import 
GroupAccountSSHProvisionerConfig
                                         
pref.specificPreferences.slurm.groupSSHAccountProvisionerConfigs[cfg_idx] = 
GroupAccountSSHProvisionerConfig(**cfg)
 
-        self.request.airavata_client.updateGroupResourceProfile(
-            self.authz_token, grp)
+        self.request.airavata.compute.update_group_resource_profile(
+            grp.groupResourceProfileId, 
grpc_requests.group_resource_profile(grp))
 
     def perform_destroy(self, instance):
-        self.request.airavata_client.removeGroupResourceProfile(
-            self.authz_token, instance.groupResourceProfileId)
+        self.request.airavata.compute.remove_group_resource_profile(
+            instance.groupResourceProfileId)
 
 
 class SharedEntityViewSet(mixins.RetrieveModelMixin,

Reply via email to