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 43604e2d4 refactor(portal): make experiment + application-interface
serializers proto-native (Track D) (#202)
43604e2d4 is described below
commit 43604e2d4909eecacd5f9e02507dbec7ce1bf017
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Tue Jun 9 04:40:41 2026 -0400
refactor(portal): make experiment + application-interface serializers
proto-native (Track D) (#202)
The largest coupled family: the experiment read tree and the application
interfaces share the InputDataObjectType/OutputDataObjectType serializers,
so
they migrate together.
- Input/Output: read the gRPC InputDataObjectType/OutputDataObjectType
protobuf
directly (DataType renders as the member name; metaData proto-'' -> null
via
new ProtoStoredJSONField); create() builds the proto.
- ApplicationInterfaceDescriptionSerializer: proto-native read + write
(build the
proto ApplicationInterfaceDescription with nested inputs/outputs;
showQueueSettings/queueSettingsCalculatorId DB handling preserved). View
_update_input_metadata reads proto fields.
- Experiment tree: full proto-native ExperimentSerializer replicating the
entire
processes -> tasks -> jobs tree (each with its status list),
userConfiguration +
scheduling, inputs/outputs, status, errors. Preserves the subtle
ISO-vs-int
timestamp quirks byte-for-byte (top-level experimentStatus = ISO; nested
process-tree statuses + the nested job creationTime = raw int; standalone
JobSerializer creationTime = ISO). State/type enums render as Thrift ints
via
the new state field helpers. save() builds the proto ExperimentModel
incl. the
writable nested userConfigurationData + computationalResourceScheduling.
- Repoint every experiment/interface consumer to pass protobuf through:
ExperimentViewSet (instance/create/update/launch/jobs/clone),
ProjectViewSet.experiments, FullExperimentViewSet (+ its nested
module/compute/
project/jobs; DataType comparisons now use the proto enum),
ApplicationInterfaceViewSet, ApplicationModuleViewSet
interface/deployment actions, output_views.get_output_views/generate_data,
workspace.edit_experiment, ExperimentArchiveView, _user_storage_path.
- Remove grpc_adapters/grpc_requests experiment-tree + interface + io
adapters and
the now-unused Thrift imports; views.py drops the
DataType/ExperimentModel Thrift
imports.
NOTE: the EXECUTING-only intermediateOutput enrichment still calls the
legacy
experiment_util (Thrift) with a proto experiment — a parked concern tracked
with
the experiment_util migration; it is best-effort (try/except) and never
runs for
the validated non-EXECUTING cases.
Validated byte-for-byte (experiment full tree, job, input, output,
interface) vs
the old adapter+serializer path; write paths (experiment incl. nested
scheduling,
interface incl. nested I/O) produce equivalent protos. manage.py check
green; api
test failures unchanged vs origin/main.
---
.../django_airavata/apps/api/grpc_adapters.py | 429 --------------
.../django_airavata/apps/api/grpc_requests.py | 157 +----
.../django_airavata/apps/api/output_views.py | 23 +-
.../django_airavata/apps/api/serializers.py | 635 +++++++++++++++++----
.../django_airavata/apps/api/views.py | 174 +++---
.../django_airavata/apps/workspace/views.py | 9 +-
6 files changed, 624 insertions(+), 803 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
b/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
index c5a01bff1..a59bb8b62 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_adapters.py
@@ -16,10 +16,6 @@ from types import SimpleNamespace
from airavata.model.appcatalog.computeresource.ttypes import (
JobSubmissionProtocol as _ThriftJobSubmissionProtocol,
)
-from airavata.model.appcatalog.groupresourceprofile.ttypes import (
- ResourceType as _ThriftResourceType,
-)
-from airavata.model.application.io.ttypes import DataType as _ThriftDataType
from airavata.model.data.replica.ttypes import (
DataProductType as _ThriftDataProductType,
ReplicaLocationCategory as _ThriftReplicaLocationCategory,
@@ -28,16 +24,6 @@ from airavata.model.data.replica.ttypes import (
from airavata.model.data.movement.ttypes import (
DataMovementProtocol as _ThriftDataMovementProtocol,
)
-from airavata.model.experiment.ttypes import (
- ExperimentType as _ThriftExperimentType,
-)
-from airavata.model.status.ttypes import (
- ExperimentState as _ThriftExperimentState,
- JobState as _ThriftJobState,
- ProcessState as _ThriftProcessState,
- TaskState as _ThriftTaskState,
-)
-from airavata.model.task.ttypes import TaskTypes as _ThriftTaskTypes
from airavata.model.user.ttypes import Status as _ThriftStatus
@@ -107,421 +93,6 @@ _JOB_SUBMISSION_PROTOCOL = {
'LOCAL_FORK': _ThriftJobSubmissionProtocol.LOCAL_FORK,
}
-def _input_data_object(pb):
- """gRPC ``InputDataObjectType`` -> ``InputDataObjectTypeSerializer``
shape."""
- return SimpleNamespace(
- name=pb.name,
- value=pb.value,
- # DataType proto/Thrift ints differ per name -> bridge by name; the
- # serializer's EnumChoiceField(DataType) reads ``.name`` off the
member.
- type=_thrift_enum(pb, 'type', _ThriftDataType),
- applicationArgument=pb.application_argument,
- standardInput=pb.standard_input,
- userFriendlyDescription=pb.user_friendly_description,
- # JSON string; empty -> None so StoredJSONField renders null like
Thrift.
- metaData=pb.meta_data or None,
- inputOrder=pb.input_order,
- isRequired=pb.is_required,
- requiredToAddedToCommandLine=pb.required_to_added_to_command_line,
- dataStaged=pb.data_staged,
- storageResourceId=pb.storage_resource_id,
- isReadOnly=pb.is_read_only,
- overrideFilename=pb.override_filename,
- )
-
-
-def _output_data_object(pb):
- """gRPC ``OutputDataObjectType`` -> ``OutputDataObjectTypeSerializer``
shape."""
- return SimpleNamespace(
- name=pb.name,
- value=pb.value,
- type=_thrift_enum(pb, 'type', _ThriftDataType),
- applicationArgument=pb.application_argument,
- isRequired=pb.is_required,
- requiredToAddedToCommandLine=pb.required_to_added_to_command_line,
- dataMovement=pb.data_movement,
- location=pb.location,
- searchQuery=pb.search_query,
- outputStreaming=pb.output_streaming,
- storageResourceId=pb.storage_resource_id,
- metaData=pb.meta_data or None,
- )
-
-
-def application_interface(pb):
- """gRPC ``ApplicationInterfaceDescription`` ->
``ApplicationInterfaceDescriptionSerializer`` shape.
-
- Recursively adapts the nested ``applicationInputs``/``applicationOutputs``
- repeated messages.
- """
- return SimpleNamespace(
- applicationInterfaceId=pb.application_interface_id,
- applicationName=pb.application_name,
- applicationDescription=pb.application_description,
- applicationModules=list(pb.application_modules),
- applicationInputs=[_input_data_object(i) for i in
pb.application_inputs],
- applicationOutputs=[_output_data_object(o) for o in
pb.application_outputs],
- archiveWorkingDirectory=pb.archive_working_directory,
- hasOptionalFileInputs=pb.has_optional_file_inputs,
- )
-
-
-# proto ResourceType member name -> Thrift ResourceType value (names align,
-# ints differ: proto SLURM=1 vs Thrift SLURM=0).
-_RESOURCE_TYPE = {
- 'SLURM': _ThriftResourceType.SLURM,
- 'AWS': _ThriftResourceType.AWS,
-}
-
-
-def _compute_resource_reservation(pb):
- """gRPC ``ComputeResourceReservation`` ->
``ComputeResourceReservationSerializer`` shape."""
- return SimpleNamespace(
- reservationId=pb.reservation_id,
- reservationName=pb.reservation_name,
- queueNames=list(pb.queue_names),
- # serializer overrides start/end with nullable UTC fields.
- startTime=pb.start_time or None,
- endTime=pb.end_time or None,
- )
-
-
-def _group_account_ssh_provisioner_config(pb):
- """gRPC ``GroupAccountSSHProvisionerConfig`` -> auto-generated serializer
shape."""
- return SimpleNamespace(
- resourceId=pb.resource_id,
- groupResourceProfileId=pb.group_resource_profile_id,
- configName=pb.config_name,
- configValue=pb.config_value,
- )
-
-
-def _slurm_compute_resource_preference(pb):
- """gRPC ``SlurmComputeResourcePreference`` -> auto-generated serializer
shape."""
- return SimpleNamespace(
- allocationProjectNumber=pb.allocation_project_number,
- preferredBatchQueue=pb.preferred_batch_queue,
- qualityOfService=pb.quality_of_service,
- usageReportingGatewayId=pb.usage_reporting_gateway_id,
- sshAccountProvisioner=pb.ssh_account_provisioner,
- groupSSHAccountProvisionerConfigs=[
- _group_account_ssh_provisioner_config(c)
- for c in pb.group_ssh_account_provisioner_configs],
-
sshAccountProvisionerAdditionalInfo=pb.ssh_account_provisioner_additional_info,
- reservations=[_compute_resource_reservation(r) for r in
pb.reservations],
- )
-
-
-def _aws_compute_resource_preference(pb):
- """gRPC ``AwsComputeResourcePreference`` -> auto-generated serializer
shape."""
- return SimpleNamespace(
- region=pb.region,
- preferredAmiId=pb.preferred_ami_id,
- preferredInstanceType=pb.preferred_instance_type,
- )
-
-
-def _environment_specific_preferences(pb):
- """proto oneof ``EnvironmentSpecificPreferences`` -> {slurm, aws} (one
set)."""
- which = pb.WhichOneof('preferences')
- return SimpleNamespace(
- slurm=(_slurm_compute_resource_preference(pb.slurm)
- if which == 'slurm' else None),
- aws=(_aws_compute_resource_preference(pb.aws)
- if which == 'aws' else None),
- )
-
-
-def _group_compute_resource_preference(pb):
- """gRPC ``GroupComputeResourcePreference`` -> auto-generated serializer
shape."""
- return SimpleNamespace(
- computeResourceId=pb.compute_resource_id,
- groupResourceProfileId=pb.group_resource_profile_id,
- overridebyAiravata=pb.override_by_airavata,
- loginUserName=pb.login_user_name,
- scratchLocation=pb.scratch_location,
- # rendered as raw ints; bridge by name (incl. the JSP_CLOUD/LOCAL
- # divergences) to the Thrift integer the frontend expects.
- preferredJobSubmissionProtocol=_thrift_enum_mapped(
- pb, 'preferred_job_submission_protocol', _JOB_SUBMISSION_PROTOCOL),
- preferredDataMovementProtocol=_thrift_enum_mapped(
- pb, 'preferred_data_movement_protocol', _DATA_MOVEMENT_PROTOCOL),
- # empty token -> None so the serializer's userHasWriteAccess token READ
- # check (``token is None or ...``) skips unset tokens.
- resourceSpecificCredentialStoreToken=(
- pb.resource_specific_credential_store_token or None),
- resourceType=_thrift_enum_mapped(pb, 'resource_type', _RESOURCE_TYPE),
- specificPreferences=(
- _environment_specific_preferences(pb.specific_preferences)
- if pb.HasField('specific_preferences') else None),
- )
-
-
-def _compute_resource_policy(pb):
- """gRPC ``ComputeResourcePolicy`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- resourcePolicyId=pb.resource_policy_id,
- computeResourceId=pb.compute_resource_id,
- groupResourceProfileId=pb.group_resource_profile_id,
- allowedBatchQueues=list(pb.allowed_batch_queues),
- )
-
-
-def _batch_queue_resource_policy(pb):
- """gRPC ``BatchQueueResourcePolicy`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- resourcePolicyId=pb.resource_policy_id,
- computeResourceId=pb.compute_resource_id,
- groupResourceProfileId=pb.group_resource_profile_id,
- queuename=pb.queuename,
- maxAllowedNodes=pb.max_allowed_nodes,
- maxAllowedCores=pb.max_allowed_cores,
- maxAllowedWalltime=pb.max_allowed_walltime,
- )
-
-
-def group_resource_profile(pb):
- """gRPC ``GroupResourceProfile`` -> ``GroupResourceProfileSerializer``
shape.
-
- Recursively adapts the compute preferences (each carrying a slurm/aws
- union of specific preferences with reservations) and the compute /
- batch-queue resource policies.
- """
- return SimpleNamespace(
- gatewayId=pb.gateway_id,
- groupResourceProfileId=pb.group_resource_profile_id,
- groupResourceProfileName=pb.group_resource_profile_name,
- computePreferences=[
- _group_compute_resource_preference(p) for p in
pb.compute_preferences],
- computeResourcePolicies=[
- _compute_resource_policy(p) for p in pb.compute_resource_policies],
- batchQueueResourcePolicies=[
- _batch_queue_resource_policy(p) for p in
pb.batch_queue_resource_policies],
- creationTime=pb.creation_time or None,
- updatedTime=pb.updated_time or None,
- defaultCredentialStoreToken=pb.default_credential_store_token or None,
- )
-
-
-# --- Experiment tree -------------------------------------------------------
-#
-# getExperiment returns the full ExperimentModel including the processes tree
-# (process -> tasks -> jobs, each with its status list). The status/type enums
-# render as raw integers and are bridged by name with the proto prefix stripped
-# (proto EXPERIMENT_STATE_CREATED -> Thrift CREATED). Status timeOfStateChange
-# stays an int (ExperimentStatusSerializer/ProcessStatusSerializer use a
-# non-nullable UTC field; the nested auto-generated ones render the int);
-# model creation/update times map proto-zero -> None (nullable).
-
-
-def _error_model(pb):
- """gRPC ``ErrorModel`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- errorId=pb.error_id,
- creationTime=pb.creation_time or None,
- actualErrorMessage=pb.actual_error_message,
- userFriendlyMessage=pb.user_friendly_message,
- transientOrPersistent=pb.transient_or_persistent,
- rootCauseErrorIdList=list(pb.root_cause_error_id_list),
- )
-
-
-def _computational_resource_scheduling(pb):
- """gRPC ``ComputationalResourceSchedulingModel`` -> auto-generated
shape."""
- return SimpleNamespace(
- resourceHostId=pb.resource_host_id,
- totalCPUCount=pb.total_cpu_count,
- nodeCount=pb.node_count,
- numberOfThreads=pb.number_of_threads,
- queueName=pb.queue_name,
- wallTimeLimit=pb.wall_time_limit,
- totalPhysicalMemory=pb.total_physical_memory,
- chessisNumber=pb.chessis_number,
- staticWorkingDir=pb.static_working_dir,
- overrideLoginUserName=pb.override_login_user_name,
- overrideScratchLocation=pb.override_scratch_location,
- overrideAllocationProjectNumber=pb.override_allocation_project_number,
- mGroupCount=pb.m_group_count,
- )
-
-
-def _user_configuration_data(pb):
- """gRPC ``UserConfigurationDataModel`` -> auto-generated serializer
shape."""
- return SimpleNamespace(
- airavataAutoSchedule=pb.airavata_auto_schedule,
- overrideManualScheduledParams=pb.override_manual_scheduled_params,
- shareExperimentPublicly=pb.share_experiment_publicly,
- computationalResourceScheduling=(
-
_computational_resource_scheduling(pb.computational_resource_scheduling)
- if pb.HasField('computational_resource_scheduling') else None),
- throttleResources=pb.throttle_resources,
- userDN=pb.user_dn,
- generateCert=pb.generate_cert,
- inputStorageResourceId=pb.input_storage_resource_id,
- outputStorageResourceId=pb.output_storage_resource_id,
- experimentDataDir=pb.experiment_data_dir,
- useUserCRPref=pb.use_user_cr_pref,
- groupResourceProfileId=pb.group_resource_profile_id,
- autoScheduledCompResourceSchedulingList=[
- _computational_resource_scheduling(s)
- for s in pb.auto_scheduled_comp_resource_scheduling_list],
- )
-
-
-def _experiment_status(pb):
- """gRPC ``ExperimentStatus`` -> ``ExperimentStatusSerializer`` shape."""
- return SimpleNamespace(
- state=_thrift_enum_prefixed(
- pb, 'state', _ThriftExperimentState, 'EXPERIMENT_STATE_'),
- timeOfStateChange=pb.time_of_state_change,
- reason=pb.reason,
- statusId=pb.status_id,
- )
-
-
-def _process_status(pb):
- """gRPC ``ProcessStatus`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- state=_thrift_enum_prefixed(
- pb, 'state', _ThriftProcessState, 'PROCESS_STATE_'),
- timeOfStateChange=pb.time_of_state_change,
- reason=pb.reason,
- statusId=pb.status_id,
- processId=pb.process_id,
- )
-
-
-def _task_status(pb):
- """gRPC ``TaskStatus`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- state=_thrift_enum_prefixed(
- pb, 'state', _ThriftTaskState, 'TASK_STATE_'),
- timeOfStateChange=pb.time_of_state_change,
- reason=pb.reason,
- statusId=pb.status_id,
- )
-
-
-def _job_status(pb):
- """gRPC ``JobStatus`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- jobState=_thrift_enum_prefixed(
- pb, 'job_state', _ThriftJobState, 'JOB_STATE_'),
- timeOfStateChange=pb.time_of_state_change,
- reason=pb.reason,
- statusId=pb.status_id,
- )
-
-
-def job_model(pb):
- """gRPC ``JobModel`` -> ``JobSerializer`` shape."""
- return SimpleNamespace(
- jobId=pb.job_id,
- taskId=pb.task_id,
- processId=pb.process_id,
- jobDescription=pb.job_description,
- creationTime=pb.creation_time or None,
- jobStatuses=[_job_status(s) for s in pb.job_statuses],
- computeResourceConsumed=pb.compute_resource_consumed,
- jobName=pb.job_name,
- workingDir=pb.working_dir,
- stdOut=pb.std_out,
- stdErr=pb.std_err,
- exitCode=pb.exit_code,
- )
-
-
-def _task_model(pb):
- """gRPC ``TaskModel`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- taskId=pb.task_id,
- taskType=_thrift_enum_prefixed(
- pb, 'task_type', _ThriftTaskTypes, 'TASK_TYPES_'),
- parentProcessId=pb.parent_process_id,
- creationTime=pb.creation_time or None,
- lastUpdateTime=pb.last_update_time or None,
- taskStatuses=[_task_status(s) for s in pb.task_statuses],
- taskDetail=pb.task_detail,
- subTaskModel=pb.sub_task_model,
- taskErrors=[_error_model(e) for e in pb.task_errors],
- jobs=[job_model(j) for j in pb.jobs],
- maxRetry=pb.max_retry,
- currentRetry=pb.current_retry,
- )
-
-
-def _process_model(pb):
- """gRPC ``ProcessModel`` -> auto-generated serializer shape."""
- return SimpleNamespace(
- processId=pb.process_id,
- experimentId=pb.experiment_id,
- creationTime=pb.creation_time or None,
- lastUpdateTime=pb.last_update_time or None,
- processStatuses=[_process_status(s) for s in pb.process_statuses],
- processDetail=pb.process_detail,
- applicationInterfaceId=pb.application_interface_id,
- applicationDeploymentId=pb.application_deployment_id,
- computeResourceId=pb.compute_resource_id,
- processInputs=[_input_data_object(i) for i in pb.process_inputs],
- processOutputs=[_output_data_object(o) for o in pb.process_outputs],
- processResourceSchedule=(
- _computational_resource_scheduling(pb.process_resource_schedule)
- if pb.HasField('process_resource_schedule') else None),
- tasks=[_task_model(t) for t in pb.tasks],
- taskDag=pb.task_dag,
- processErrors=[_error_model(e) for e in pb.process_errors],
- gatewayExecutionId=pb.gateway_execution_id,
- enableEmailNotification=pb.enable_email_notification,
- emailAddresses=list(pb.email_addresses),
- inputStorageResourceId=pb.input_storage_resource_id,
- outputStorageResourceId=pb.output_storage_resource_id,
- userDn=pb.user_dn,
- generateCert=pb.generate_cert,
- experimentDataDir=pb.experiment_data_dir,
- userName=pb.user_name,
- useUserCRPref=pb.use_user_cr_pref,
- groupResourceProfileId=pb.group_resource_profile_id,
- # the legacy workflow-engine subsystem is not adapted (rarely
populated);
- # an empty list matches the Thrift default for non-workflow processes.
- processWorkflows=[],
- )
-
-
-def experiment(pb):
- """gRPC ``ExperimentModel`` -> ``ExperimentSerializer`` shape.
-
- The deepest read model: recursively adapts the user configuration, the
- experiment input/output and status lists, the errors, and the full
- processes -> tasks -> jobs tree.
- """
- return SimpleNamespace(
- experimentId=pb.experiment_id,
- projectId=pb.project_id,
- gatewayId=pb.gateway_id,
- experimentType=_thrift_enum_prefixed(
- pb, 'experiment_type', _ThriftExperimentType, 'EXPERIMENT_TYPE_'),
- userName=pb.user_name,
- experimentName=pb.experiment_name,
- creationTime=pb.creation_time or None,
- description=pb.description,
- executionId=pb.execution_id,
- gatewayExecutionId=pb.gateway_execution_id,
- gatewayInstanceId=pb.gateway_instance_id,
- enableEmailNotification=pb.enable_email_notification,
- emailAddresses=list(pb.email_addresses),
- userConfigurationData=(
- _user_configuration_data(pb.user_configuration_data)
- if pb.HasField('user_configuration_data') else None),
- experimentInputs=[_input_data_object(i) for i in pb.experiment_inputs],
- experimentOutputs=[_output_data_object(o) for o in
pb.experiment_outputs],
- experimentStatus=[_experiment_status(s) for s in pb.experiment_status],
- errors=[_error_model(e) for e in pb.errors],
- processes=[_process_model(p) for p in pb.processes],
- # legacy workflow-engine subsystem not adapted (rarely populated).
- workflow=None,
- )
-
-
def group(pb):
"""gRPC ``GroupModel`` -> ``GroupSerializer`` shape."""
return SimpleNamespace(
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 7cb80e006..cf040d581 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
@@ -20,13 +20,9 @@ from airavata.model.appcatalog.computeresource.ttypes import
(
from airavata.model.appcatalog.groupresourceprofile.ttypes import (
ResourceType as _ThriftResourceType,
)
-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,
-)
_GEN = "airavata_sdk.generated.org.apache.airavata.model"
@@ -69,63 +65,19 @@ def password_credential(gateway_id, portal_user_name,
login_user_name,
)
-def _input_data_object(t):
- io = _pb2("application.io.application_io_pb2")
- return io.InputDataObjectType(
- name=t.name or '',
- value=t.value or '',
- type=_proto_enum(io.DataType, _ThriftDataType, t.type),
- application_argument=t.applicationArgument or '',
- standard_input=bool(t.standardInput),
- user_friendly_description=t.userFriendlyDescription or '',
- # StoredJSONField.to_internal_value json.dumps()es metaData to a
string.
- meta_data=t.metaData or '',
- input_order=t.inputOrder or 0,
- is_required=bool(t.isRequired),
- required_to_added_to_command_line=bool(t.requiredToAddedToCommandLine),
- data_staged=bool(t.dataStaged),
- storage_resource_id=t.storageResourceId or '',
- is_read_only=bool(t.isReadOnly),
- override_filename=t.overrideFilename or '',
- )
-
-
-def _output_data_object(t):
- io = _pb2("application.io.application_io_pb2")
- return io.OutputDataObjectType(
- name=t.name or '',
- value=t.value or '',
- type=_proto_enum(io.DataType, _ThriftDataType, t.type),
- application_argument=t.applicationArgument or '',
- is_required=bool(t.isRequired),
- required_to_added_to_command_line=bool(t.requiredToAddedToCommandLine),
- data_movement=bool(t.dataMovement),
- location=t.location or '',
- search_query=t.searchQuery or '',
- output_streaming=bool(t.outputStreaming),
- storage_resource_id=t.storageResourceId or '',
- meta_data=t.metaData or '',
- )
-
-
-def application_interface(t):
- """Thrift ``ApplicationInterfaceDescription`` -> proto message."""
- return
_pb2("appcatalog.appinterface.app_interface_pb2").ApplicationInterfaceDescription(
- application_interface_id=t.applicationInterfaceId or '',
- application_name=t.applicationName or '',
- application_description=t.applicationDescription or '',
- application_modules=list(t.applicationModules or []),
- application_inputs=[_input_data_object(i) for i in
(t.applicationInputs or [])],
- application_outputs=[_output_data_object(o) for o in
(t.applicationOutputs or [])],
- archive_working_directory=bool(t.archiveWorkingDirectory),
- has_optional_file_inputs=bool(t.hasOptionalFileInputs),
- )
+def _proto_enum_rev(proto_enum, rev_map, value):
+ """Thrift enum value -> proto enum value via an EXPLICIT inverse name map
+ (for protocol enums whose proto/Thrift member names diverge). None/unmapped
+ -> 0 (proto *_UNKNOWN)."""
+ if value is None:
+ return 0
+ name = rev_map.get(value)
+ return proto_enum.Value(name) if name is not None else 0
-# proto-name -> Thrift protocol value maps (mirror grpc_adapters); inverted
-# below to go Thrift value -> proto value, preserving the divergent name pairs
+# Thrift protocol value -> proto member name, preserving the divergent name
pairs
# (Thrift CLOUD <-> proto JSP_CLOUD; Thrift LOCAL <-> proto
-# DATA_MOVEMENT_PROTOCOL_LOCAL).
+# DATA_MOVEMENT_PROTOCOL_LOCAL). Used by the group-resource-profile write path.
_JOB_SUBMISSION_PROTOCOL_REV = {
_ThriftJobSubmissionProtocol.LOCAL: 'LOCAL',
_ThriftJobSubmissionProtocol.SSH: 'SSH',
@@ -143,95 +95,6 @@ _DATA_MOVEMENT_PROTOCOL_REV = {
}
-def _proto_enum_rev(proto_enum, rev_map, value):
- """Thrift enum value -> proto enum value via an EXPLICIT inverse name map
- (for protocol enums whose proto/Thrift member names diverge). None/unmapped
- -> 0 (proto *_UNKNOWN)."""
- if value is None:
- return 0
- name = rev_map.get(value)
- return proto_enum.Value(name) if name is not None else 0
-
-
-# --- 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.
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 67aafa3d1..2820d34f8 100644
--- a/airavata-django-portal/django_airavata/apps/api/output_views.py
+++ b/airavata-django-portal/django_airavata/apps/api/output_views.py
@@ -8,7 +8,9 @@ from functools import partial
import nbformat
import papermill as pm
-from airavata.model.application.io.ttypes import DataType
+from
airavata_sdk.generated.org.apache.airavata.model.application.io.application_io_pb2
import ( # noqa: E501
+ DataType,
+)
from django.conf import settings
from nbconvert import HTMLExporter
@@ -79,7 +81,7 @@ DEFAULT_VIEW_PROVIDERS = {
def get_output_views(request, experiment, application_interface=None):
output_views = {}
- for output in experiment.experimentOutputs:
+ for output in experiment.experiment_outputs:
output_views[output.name] = []
output_view_provider_ids = _get_output_view_providers(
output, application_interface)
@@ -123,9 +125,9 @@ def _get_output_view_provider(output_view_provider_id):
def _get_output_view_providers(experiment_output, application_interface):
output_view_providers = []
logger.debug("experiment_output={}".format(experiment_output))
- if experiment_output.metaData:
+ if experiment_output.meta_data:
try:
- output_metadata = json.loads(experiment_output.metaData)
+ output_metadata = json.loads(experiment_output.meta_data)
logger.debug("output_metadata={}".format(output_metadata))
if 'output-view-providers' in output_metadata:
output_view_providers.extend(
@@ -148,16 +150,16 @@ def _get_output_view_providers(experiment_output,
application_interface):
def _get_application_output_view_providers(application_interface, output_name):
app_output = [o
- for o in application_interface.applicationOutputs
+ for o in application_interface.application_outputs
if o.name == output_name]
if len(app_output) == 1:
logger.debug("{}: {}".format(output_name, app_output))
app_output = app_output[0]
else:
return []
- if app_output.metaData:
+ if app_output.meta_data:
try:
- output_metadata = json.loads(app_output.metaData)
+ output_metadata = json.loads(app_output.meta_data)
if 'output-view-providers' in output_metadata:
return output_metadata['output-view-providers']
except Exception:
@@ -175,10 +177,9 @@ 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 = grpc_adapters.experiment(
- request.airavata.research.get_experiment(experiment_id))
+ experiment = request.airavata.research.get_experiment(experiment_id)
experiment_output = [o
- for o in experiment.experimentOutputs
+ for o in experiment.experiment_outputs
if o.name == experiment_output_name]
# TODO: handle experiment_output not found by name
experiment_output = experiment_output[0]
@@ -216,7 +217,7 @@ def _generate_data(request,
DataType.URI_COLLECTION,
DataType.STDOUT,
DataType.STDERR) and
- experiment_output.value.startswith("airavata-dp")):
+ experiment_output.value.startswith("airavata-dp")):
data_product_uris = experiment_output.value.split(",")
data_products = map(
lambda dpid: grpc_adapters.data_product(
diff --git a/airavata-django-portal/django_airavata/apps/api/serializers.py
b/airavata-django-portal/django_airavata/apps/api/serializers.py
index d65db1301..35476867c 100644
--- a/airavata-django-portal/django_airavata/apps/api/serializers.py
+++ b/airavata-django-portal/django_airavata/apps/api/serializers.py
@@ -3,13 +3,9 @@ import datetime
import json
import logging
import os
-from pathlib import Path
from urllib.parse import quote
from airavata.model.application.io.ttypes import DataType
-from airavata.model.appcatalog.appinterface.ttypes import (
- ApplicationInterfaceDescription
-)
from airavata.model.appcatalog.groupresourceprofile.ttypes import (
ComputeResourceReservation,
GroupComputeResourcePreference,
@@ -19,23 +15,13 @@ from airavata.model.appcatalog.groupresourceprofile.ttypes
import (
AwsComputeResourcePreference
)
from airavata.model.appcatalog.parser.ttypes import IOType as _ThriftIOType
-from airavata.model.application.io.ttypes import (
- InputDataObjectType,
- OutputDataObjectType
-)
from airavata.model.data.replica.ttypes import (
DataProductModel,
DataReplicaLocationModel
)
-from airavata.model.experiment.ttypes import (
- ExperimentModel
-)
from airavata.model.group.ttypes import GroupModel, ResourcePermissionType
-from airavata.model.job.ttypes import JobModel
from airavata.model.status.ttypes import (
- ExperimentState,
- ExperimentStatus,
- ProcessStatus
+ ExperimentState
)
from airavata.model.user.ttypes import UserProfile
from airavata_django_portal_sdk import (
@@ -394,6 +380,16 @@ class StoredJSONField(serializers.JSONField):
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):
@@ -639,91 +635,155 @@ class EnumChoiceField(serializers.ChoiceField):
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 = EnumChoiceField(enum_class=DataType)
- applicationArgument = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- standardInput = serializers.BooleanField(default=False)
- userFriendlyDescription = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- metaData = StoredJSONField(allow_null=True, required=False)
- inputOrder = serializers.IntegerField(required=False, allow_null=True)
- isRequired = serializers.BooleanField(default=False)
- requiredToAddedToCommandLine = serializers.BooleanField(default=False)
- dataStaged = serializers.BooleanField(default=False)
- storageResourceId = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- isReadOnly = serializers.BooleanField(default=False)
- overrideFilename = 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 InputDataObjectType(**validated_data)
+ return _proto_input_data_object(validated_data)
- def update(self, instance, validated_data):
- for attr, value in validated_data.items():
- setattr(instance, attr, value)
- return instance
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 = EnumChoiceField(enum_class=DataType)
- applicationArgument = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- isRequired = serializers.BooleanField(default=False)
- requiredToAddedToCommandLine = serializers.BooleanField(default=False)
- dataMovement = serializers.BooleanField(default=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(allow_blank=True, allow_null=True,
required=False)
- outputStreaming = serializers.BooleanField(default=False)
- storageResourceId = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- metaData = StoredJSONField(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 OutputDataObjectType(**validated_data)
+ return _proto_output_data_object(validated_data)
- def update(self, instance, validated_data):
- for attr, value in validated_data.items():
- setattr(instance, attr, value)
- return instance
class ApplicationInterfaceDescriptionSerializer(serializers.Serializer):
- applicationInterfaceId = serializers.CharField(required=False,
allow_null=True, allow_blank=True)
- applicationName = serializers.CharField()
- applicationDescription = serializers.CharField(allow_blank=True,
allow_null=True, required=False)
- applicationModules = serializers.ListField(child=serializers.CharField(),
allow_null=True, required=False)
- applicationInputs = InputDataObjectTypeSerializer(many=True,
allow_null=True, required=False)
- applicationOutputs = OutputDataObjectTypeSerializer(many=True,
allow_null=True, required=False)
- archiveWorkingDirectory = serializers.BooleanField(default=False)
- hasOptionalFileInputs = serializers.BooleanField(default=False,
read_only=True)
+ """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='applicationInterfaceId',
+ 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):
- # Convert inputs/outputs from dicts to Thrift objects
- inputs_data = validated_data.pop('applicationInputs', None)
- outputs_data = validated_data.pop('applicationOutputs', None)
-
- # Remove Django specific fields
+ 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)
- validated_data.pop('url', None)
- validated_data.pop('userHasWriteAccess', None)
-
- if 'applicationInterfaceId' in validated_data and
validated_data['applicationInterfaceId'] is None:
- validated_data['applicationInterfaceId'] = ""
-
- application_interface =
ApplicationInterfaceDescription(**validated_data)
-
+ 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.applicationInputs =
[InputDataObjectType(**inp) for inp in inputs_data]
+ application_interface.application_inputs.extend(
+ _proto_input_data_object(inp) for inp in inputs_data)
if outputs_data is not None:
- application_interface.applicationOutputs =
[OutputDataObjectType(**out) for out in outputs_data]
-
+ application_interface.application_outputs.extend(
+ _proto_output_data_object(out) for out in outputs_data)
return application_interface
def update(self, instance, validated_data):
@@ -732,26 +792,40 @@ class
ApplicationInterfaceDescriptionSerializer(serializers.Serializer):
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.applicationModules[0]
+ 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('applicationInputs', None)
- outputs_data = validated_data.pop('applicationOutputs', None)
+ inputs_data = validated_data.pop('application_inputs', None)
+ outputs_data = validated_data.pop('application_outputs', None)
- for attr, value in validated_data.items():
- setattr(instance, attr, value)
+ 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:
- instance.applicationInputs = [InputDataObjectType(**inp) for inp
in inputs_data]
+ del instance.application_inputs[:]
+ instance.application_inputs.extend(
+ _proto_input_data_object(inp) for inp in inputs_data)
if outputs_data is not None:
- instance.applicationOutputs = [OutputDataObjectType(**out) for out
in outputs_data]
+ del instance.application_outputs[:]
+ instance.application_outputs.extend(
+ _proto_output_data_object(out) for out in outputs_data)
return instance
- def get_userHasWriteAccess(self, appDeployment):
+ def get_userHasWriteAccess(self, appInterface):
request = self.context['request']
return request.is_gateway_admin
@@ -1216,68 +1290,341 @@ class
GridFtpDataMovementSerializer(serializers.Serializer):
required=False)
-class ExperimentStatusSerializer(
- thrift_utils.create_serializer_class(ExperimentStatus)):
- timeOfStateChange = UTCPosixTimestampDateTimeField()
+def _status_pb2():
+ from airavata_sdk.generated.org.apache.airavata.model.status import
status_pb2
+ return status_pb2
+
+
+def _experiment_state_field(**kwargs):
+ from airavata.model.status.ttypes import ExperimentState as _T
+ return proto_enum_int_field(
+ _status_pb2().ExperimentState.DESCRIPTOR, _T,
+ proto_prefix='EXPERIMENT_STATE_', **kwargs)
-class ProcessStatusSerializer(
- thrift_utils.create_serializer_class(ProcessStatus)):
- timeOfStateChange = UTCPosixTimestampDateTimeField()
+def _process_state_field(**kwargs):
+ from airavata.model.status.ttypes import ProcessState as _T
+ return proto_enum_int_field(
+ _status_pb2().ProcessState.DESCRIPTOR, _T,
+ proto_prefix='PROCESS_STATE_', **kwargs)
-class ExperimentSerializer(
- thrift_utils.create_serializer_class(ExperimentModel)):
- class Meta:
- required = ('projectId', 'experimentType', 'experimentName')
- read_only = ('userName', 'gatewayId')
+def _task_state_field(**kwargs):
+ from airavata.model.status.ttypes import TaskState as _T
+ return proto_enum_int_field(
+ _status_pb2().TaskState.DESCRIPTOR, _T,
+ proto_prefix='TASK_STATE_', **kwargs)
+
+
+def _job_state_field(**kwargs):
+ from airavata.model.status.ttypes import JobState as _T
+ return proto_enum_int_field(
+ _status_pb2().JobState.DESCRIPTOR, _T,
+ 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.model.task.ttypes import TaskTypes as _T
+ from airavata_sdk.generated.org.apache.airavata.model.task import
task_pb2
+ field = proto_enum_int_field(
+ task_pb2.TaskTypes.DESCRIPTOR, _T, 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.model.experiment.ttypes import ExperimentType as _T
+ from airavata_sdk.generated.org.apache.airavata.model.experiment import (
+ experiment_pb2,
+ )
+ return proto_enum_int_field(
+ experiment_pb2.ExperimentType.DESCRIPTOR, _T,
+ 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='experimentId',
- lookup_url_kwarg='experiment_id')
+ lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
full_experiment = FullyEncodedHyperlinkedIdentityField(
view_name='django_airavata_api:full-experiment-detail',
- lookup_field='experimentId',
- lookup_url_kwarg='experiment_id')
+ lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
project = FullyEncodedHyperlinkedIdentityField(
view_name='django_airavata_api:project-detail',
- lookup_field='projectId',
- lookup_url_kwarg='project_id')
+ lookup_field='project_id', lookup_url_kwarg='project_id')
jobs = FullyEncodedHyperlinkedIdentityField(
view_name='django_airavata_api:experiment-jobs',
- lookup_field='experimentId',
- lookup_url_kwarg='experiment_id')
+ lookup_field='experiment_id', lookup_url_kwarg='experiment_id')
shared_entity = FullyEncodedHyperlinkedIdentityField(
view_name='django_airavata_api:shared-entity-detail',
- lookup_field='experimentId',
- lookup_url_kwarg='entity_id')
- experimentInputs = OrderedListField(
- order_by='inputOrder',
- child=InputDataObjectTypeSerializer(),
- allow_null=True)
- experimentOutputs = serializers.ListField(
- child=OutputDataObjectTypeSerializer(),
- allow_null=True)
- creationTime = UTCPosixTimestampDateTimeField(allow_null=True)
- experimentStatus = ExperimentStatusSerializer(many=True, allow_null=True)
+ 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.experimentId)
+ 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.experimentStatus and
- experiment.experimentStatus[-1].state ==
ExperimentState.EXECUTING):
+ # 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:
@@ -1298,6 +1645,60 @@ class ExperimentSerializer(
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)
+
+
class DataReplicaLocationSerializer(
thrift_utils.create_serializer_class(DataReplicaLocationModel)):
creationTime = UTCPosixTimestampDateTimeField()
@@ -1373,7 +1774,7 @@ class FullExperiment:
inputDataProducts=None, applicationModule=None,
computeResource=None, jobDetails=None, outputViews=None):
self.experiment = experimentModel
- self.experimentId = experimentModel.experimentId
+ self.experimentId = experimentModel.experiment_id
self.project = project
self.outputDataProducts = outputDataProducts
self.inputDataProducts = inputDataProducts
@@ -1383,10 +1784,6 @@ class FullExperiment:
self.outputViews = outputViews
-class JobSerializer(thrift_utils.create_serializer_class(JobModel)):
- creationTime = UTCPosixTimestampDateTimeField()
-
-
class FullExperimentSerializer(serializers.Serializer):
url = FullyEncodedHyperlinkedIdentityField(
view_name='django_airavata_api:full-experiment-detail',
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py
b/airavata-django-portal/django_airavata/apps/api/views.py
index 327caad6d..509a0617e 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -7,9 +7,7 @@ import warnings
from datetime import datetime, timedelta
from urllib.parse import quote
-from airavata.model.application.io.ttypes import DataType
from airavata.model.experiment.ttypes import (
- ExperimentModel,
ExperimentSearchFields
)
from airavata.model.appcatalog.groupresourceprofile.ttypes import (
@@ -72,6 +70,13 @@ TMP_INPUT_FILE_UPLOAD_DIR = "tmp"
log = logging.getLogger(__name__)
+def _data_type_pb2():
+ from airavata_sdk.generated.org.apache.airavata.model.application.io
import (
+ application_io_pb2,
+ )
+ return application_io_pb2
+
+
def _storage_upload_and_register(request, dir_path, uploaded_file, name=None,
content_type=None, experiment_id=None):
"""Upload a file to user storage and register a data product for it (gRPC).
@@ -217,11 +222,9 @@ class ProjectViewSet(APIBackedViewSet):
@action(detail=True)
def experiments(self, request, project_id=None):
- experiments = [
- grpc_adapters.experiment(e)
- for e in request.airavata.research.get_experiments_in_project(
- project_id, -1, 0)
- ]
+ experiments = list(
+ request.airavata.research.get_experiments_in_project(
+ project_id, -1, 0))
serializer = serializers.ExperimentSerializer(
experiments, many=True, context={'request': request})
return Response(serializer.data)
@@ -240,41 +243,39 @@ class ExperimentViewSet(mixins.CreateModelMixin,
lookup_field = 'experiment_id'
def get_instance(self, lookup_value):
- return grpc_adapters.experiment(
- self.request.airavata.research.get_experiment(lookup_value))
+ return self.request.airavata.research.get_experiment(lookup_value)
def perform_create(self, serializer):
experiment = serializer.save(
- gatewayId=self.gateway_id,
- userName=self.username)
+ gateway_id=self.gateway_id,
+ user_name=self.username)
experiment_id = self.request.airavata.research.create_experiment(
- self.gateway_id, grpc_requests.experiment(experiment))
+ self.gateway_id, experiment)
self._update_workspace_preferences(
- project_id=experiment.projectId,
-
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
-
compute_resource_id=experiment.userConfigurationData.computationalResourceScheduling.resourceHostId)
- experiment.experimentId = experiment_id
+ project_id=experiment.project_id,
+
group_resource_profile_id=experiment.user_configuration_data.group_resource_profile_id,
+
compute_resource_id=experiment.user_configuration_data.computational_resource_scheduling.resource_host_id)
+ experiment.experiment_id = experiment_id
def perform_update(self, serializer):
experiment = serializer.save(
- gatewayId=self.gateway_id,
- userName=self.username)
+ gateway_id=self.gateway_id,
+ user_name=self.username)
self.request.airavata.research.update_experiment(
- experiment.experimentId, grpc_requests.experiment(experiment))
+ experiment.experiment_id, experiment)
self._update_workspace_preferences(
- project_id=experiment.projectId,
-
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
-
compute_resource_id=experiment.userConfigurationData.computationalResourceScheduling.resourceHostId)
+ project_id=experiment.project_id,
+
group_resource_profile_id=experiment.user_configuration_data.group_resource_profile_id,
+
compute_resource_id=experiment.user_configuration_data.computational_resource_scheduling.resource_host_id)
@action(methods=['post'], detail=True)
def launch(self, request, experiment_id=None):
try:
- experiment = grpc_adapters.experiment(
- request.airavata.research.get_experiment(experiment_id))
- if (experiment.enableEmailNotification):
- experiment.emailAddresses = [request.user.email]
+ experiment =
request.airavata.research.get_experiment(experiment_id)
+ if experiment.enable_email_notification:
+ experiment.email_addresses[:] = [request.user.email]
request.airavata.research.update_experiment(
- experiment_id, grpc_requests.experiment(experiment))
+ experiment_id, experiment)
experiment_util.launch(request, experiment_id)
return Response({'success': True})
except Exception as e:
@@ -283,21 +284,18 @@ class ExperimentViewSet(mixins.CreateModelMixin,
@action(methods=['get'], detail=True)
def jobs(self, request, experiment_id=None):
- jobs = [
- grpc_adapters.job_model(j)
- for j in request.airavata.research.get_job_details(experiment_id)
- ]
+ jobs = list(request.airavata.research.get_job_details(experiment_id))
serializer = serializers.JobSerializer(
jobs, many=True, context={'request': request})
return Response(serializer.data)
@action(methods=['post'], detail=True)
def clone(self, request, experiment_id=None):
- # experiment_util.clone is the launch/clone orchestration (still
Thrift);
- # re-fetch the cloned experiment via gRPC.
+ # experiment_util.clone is the launch/clone orchestration (still on the
+ # legacy SDK); re-fetch the cloned experiment via gRPC.
cloned_experiment_id = experiment_util.clone(request, experiment_id)
- cloned_experiment = grpc_adapters.experiment(
- request.airavata.research.get_experiment(cloned_experiment_id))
+ cloned_experiment = request.airavata.research.get_experiment(
+ cloned_experiment_id)
serializer = self.serializer_class(
cloned_experiment, context={'request': request})
return Response(serializer.data)
@@ -370,28 +368,27 @@ class FullExperimentViewSet(mixins.RetrieveModelMixin,
def get_instance(self, lookup_value):
"""Get FullExperiment instance with resolved references."""
# TODO: move loading experiment and references to airavata_sdk?
- experimentModel = grpc_adapters.experiment(
- self.request.airavata.research.get_experiment(lookup_value))
+ experimentModel = self.request.airavata.research.get_experiment(
+ lookup_value)
+ DT = _data_type_pb2().DataType
outputDataProducts = [
grpc_adapters.data_product(
self.request.airavata.research.get_data_product(output.value))
- for output in experimentModel.experimentOutputs
+ for output in experimentModel.experiment_outputs
if (output.value and
output.value.startswith('airavata-dp') and
- output.type in (DataType.URI,
- DataType.STDOUT,
- DataType.STDERR))]
+ output.type in (DT.URI, DT.STDOUT, DT.STDERR))]
outputDataProducts += [
grpc_adapters.data_product(
self.request.airavata.research.get_data_product(dp))
- for output in experimentModel.experimentOutputs
+ for output in experimentModel.experiment_outputs
if (output.value and
- output.type == DataType.URI_COLLECTION)
+ output.type == DT.URI_COLLECTION)
for dp in output.value.split(',')
if output.value.startswith('airavata-dp')]
- appInterfaceId = experimentModel.executionId
+ appInterfaceId = experimentModel.execution_id
try:
- applicationInterface = grpc_adapters.application_interface(
+ applicationInterface = (
self.request.airavata.research.get_application_interface(
appInterfaceId))
except Exception as e:
@@ -402,24 +399,22 @@ class FullExperimentViewSet(mixins.RetrieveModelMixin,
inputDataProducts = [
grpc_adapters.data_product(
self.request.airavata.research.get_data_product(inp.value))
- for inp in experimentModel.experimentInputs
+ for inp in experimentModel.experiment_inputs
if (inp.value and
inp.value.startswith('airavata-dp') and
- inp.type in (DataType.URI,
- DataType.STDOUT,
- DataType.STDERR))]
+ inp.type in (DT.URI, DT.STDOUT, DT.STDERR))]
inputDataProducts += [
grpc_adapters.data_product(
self.request.airavata.research.get_data_product(dp))
- for inp in experimentModel.experimentInputs
+ for inp in experimentModel.experiment_inputs
if (inp.value and
- inp.type == DataType.URI_COLLECTION)
+ inp.type == DT.URI_COLLECTION)
for dp in inp.value.split(',')
if inp.value.startswith('airavata-dp')]
applicationModule = None
try:
if applicationInterface is not None:
- appModuleId = applicationInterface.applicationModules[0]
+ appModuleId = applicationInterface.application_modules[0]
applicationModule = (
self.request.airavata.research.get_application_module(
appModuleId))
@@ -430,10 +425,11 @@ class FullExperimentViewSet(mixins.RetrieveModelMixin,
log.exception("Failed to load app interface/module",
extra={'request': self.request})
compute_resource_id = None
- user_conf = experimentModel.userConfigurationData
- if user_conf and user_conf.computationalResourceScheduling:
- comp_res_sched = user_conf.computationalResourceScheduling
- compute_resource_id = comp_res_sched.resourceHostId
+ if experimentModel.HasField('user_configuration_data'):
+ user_conf = experimentModel.user_configuration_data
+ if user_conf.HasField('computational_resource_scheduling'):
+ compute_resource_id = (
+
user_conf.computational_resource_scheduling.resource_host_id)
try:
compute_resource = (
self.request.airavata.compute.get_compute_resource(
@@ -444,15 +440,14 @@ class FullExperimentViewSet(mixins.RetrieveModelMixin,
compute_resource_id), extra={'request': self.request})
compute_resource = None
if serializers.user_has_access(
- self.request, experimentModel.projectId, 'READ'):
+ self.request, experimentModel.project_id, 'READ'):
project = self.request.airavata.research.get_project(
- experimentModel.projectId)
+ experimentModel.project_id)
else:
# User may not have access to project, only experiment
project = None
- job_details = [
- grpc_adapters.job_model(j)
- for j in
self.request.airavata.research.get_job_details(lookup_value)]
+ job_details = list(
+ self.request.airavata.research.get_job_details(lookup_value))
full_experiment = serializers.FullExperiment(
experimentModel,
project=project,
@@ -494,15 +489,14 @@ class ApplicationModuleViewSet(APIBackedViewSet):
@action(detail=True)
def application_interface(self, request, app_module_id):
- all_app_interfaces = [
- grpc_adapters.application_interface(i)
- for i in request.airavata.research.get_all_application_interfaces(
- self.gateway_id)]
+ all_app_interfaces = list(
+ request.airavata.research.get_all_application_interfaces(
+ self.gateway_id))
app_interfaces = []
for app_interface in all_app_interfaces:
- if not app_interface.applicationModules:
+ if not app_interface.application_modules:
continue
- if app_module_id in app_interface.applicationModules:
+ if app_module_id in app_interface.application_modules:
app_interfaces.append(app_interface)
if len(app_interfaces) == 1:
serializer = serializers.ApplicationInterfaceDescriptionSerializer(
@@ -581,17 +575,14 @@ class ApplicationInterfaceViewSet(APIBackedViewSet):
lookup_field = 'app_interface_id'
def get_list(self):
- return [
- grpc_adapters.application_interface(i)
- for i in
self.request.airavata.research.get_all_application_interfaces(
- self.gateway_id)
- ]
+ return list(
+ self.request.airavata.research.get_all_application_interfaces(
+ self.gateway_id))
def get_instance(self, lookup_value):
try:
- return grpc_adapters.application_interface(
- self.request.airavata.research.get_application_interface(
- lookup_value))
+ return self.request.airavata.research.get_application_interface(
+ lookup_value)
except Exception:
# If it failed to load, check to see if it exists at all
all_interfaces =
self.request.airavata.research.get_all_application_interfaces(
@@ -607,24 +598,24 @@ class ApplicationInterfaceViewSet(APIBackedViewSet):
self._update_input_metadata(application_interface)
log.debug("application_interface: {}".format(application_interface))
app_interface_id =
self.request.airavata.research.register_application_interface(
- self.gateway_id,
grpc_requests.application_interface(application_interface))
- application_interface.applicationInterfaceId = app_interface_id
+ self.gateway_id, application_interface)
+ application_interface.application_interface_id = app_interface_id
def perform_update(self, serializer):
application_interface = serializer.save()
self._update_input_metadata(application_interface)
self.request.airavata.research.update_application_interface(
- application_interface.applicationInterfaceId,
- grpc_requests.application_interface(application_interface))
+ application_interface.application_interface_id,
+ application_interface)
def perform_destroy(self, instance):
self.request.airavata.research.delete_application_interface(
- instance.applicationInterfaceId)
+ instance.application_interface_id)
def _update_input_metadata(self, app_interface):
- for app_input in app_interface.applicationInputs:
- if app_input.metaData:
- metadata = json.loads(app_input.metaData)
+ for app_input in app_interface.application_inputs:
+ if app_input.meta_data:
+ metadata = json.loads(app_input.meta_data)
# Automatically add {showOptions: {isRequired: true/false}} to
# toggle isRequired on hidden/shown inputs
if ("editor" in metadata and
@@ -633,8 +624,8 @@ class ApplicationInterfaceViewSet(APIBackedViewSet):
if "showOptions" not in metadata["editor"]["dependencies"]:
metadata["editor"]["dependencies"]["showOptions"] = {}
o = metadata["editor"]["dependencies"]["showOptions"]
- o["isRequired"] = app_input.isRequired
- app_input.metaData = json.dumps(metadata)
+ o["isRequired"] = app_input.is_required
+ app_input.meta_data = json.dumps(metadata)
@action(detail=True)
def compute_resources(self, request, app_interface_id):
@@ -1492,13 +1483,12 @@ class CurrentGatewayResourceProfile(APIView):
class ExperimentArchiveView(APIView):
def get(self, request, experiment_id=None, format=None):
- experiment = grpc_adapters.experiment(
- request.airavata.research.get_experiment(experiment_id))
+ experiment = request.airavata.research.get_experiment(experiment_id)
result = dict(archived=False, archive_name=None, created_date=None,
max_age=settings.GATEWAY_USER_DATA_ARCHIVE_MAX_AGE_DAYS)
try:
archive_entry = UserDataArchiveEntry.objects.get(
- entry_path=experiment.userConfigurationData.experimentDataDir,
+
entry_path=experiment.user_configuration_data.experiment_data_dir,
user_data_archive__rolled_back=False)
result["archived"] = True
result["archive_name"] =
archive_entry.user_data_archive.archive_name
@@ -1590,10 +1580,10 @@ def _user_storage_path(path, experiment_id=None,
request=None):
"""
rel = (path or "").lstrip("/")
if experiment_id:
- experiment = grpc_adapters.experiment(
- request.airavata.research.get_experiment(experiment_id))
- data_dir = (experiment.userConfigurationData.experimentDataDir
- if experiment.userConfigurationData else None) or ""
+ experiment = request.airavata.research.get_experiment(experiment_id)
+ data_dir = (experiment.user_configuration_data.experiment_data_dir
+ if experiment.HasField('user_configuration_data')
+ else None) or ""
base = data_dir.rstrip("/")
full = base + ("/" + rel if rel else "")
return full if (full.startswith("/") or full.startswith("~/")) else
"~/" + full
diff --git a/airavata-django-portal/django_airavata/apps/workspace/views.py
b/airavata-django-portal/django_airavata/apps/workspace/views.py
index a53711ff5..64120977d 100644
--- a/airavata-django-portal/django_airavata/apps/workspace/views.py
+++ b/airavata-django-portal/django_airavata/apps/workspace/views.py
@@ -132,11 +132,10 @@ def create_experiment(request, app_module_id):
def edit_experiment(request, experiment_id):
request.active_nav_item = 'experiments'
- experiment = grpc_adapters.experiment(
- request.airavata.research.get_experiment(experiment_id))
- applicationInterface = grpc_adapters.application_interface(
-
request.airavata.research.get_application_interface(experiment.executionId))
- app_module_id = applicationInterface.applicationModules[0]
+ experiment = request.airavata.research.get_experiment(experiment_id)
+ applicationInterface = request.airavata.research.get_application_interface(
+ experiment.execution_id)
+ app_module_id = applicationInterface.application_modules[0]
context = {
'bundle_name': 'edit-experiment',
'experiment_id': experiment_id,