Skip to content

Commit

Permalink
WIP: landing...
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Sep 13, 2024
1 parent 9d296df commit 740ebbc
Show file tree
Hide file tree
Showing 20 changed files with 686 additions and 6 deletions.
4 changes: 4 additions & 0 deletions client/src/api/landings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { type components } from "@/api/schema";

export type ClaimLandingPayload = components["schemas"]["ClaimLandingPayload"];
export type WorkflowLandingRequest = components["schemas"]["WorkflowLandingRequest"];
72 changes: 72 additions & 0 deletions client/src/components/Form/Elements/FormData/FormDataUri.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<b-row align-v="center">
<b-col>
<b-form-input
:id="id"
v-model="displayValue"
:class="['ui-input', cls]"
:readonly="true" />
</b-col>
</b-row>
</template>

<script>
export default {
props: {
value: {
type: Object,
},
id: {
type: String,
default: "",
},
multiple: {
type: Boolean,
default: false,
},
cls: {
// Refers to an optional custom css class name
type: String,
default: null,
},
},
computed: {
displayValue: {
get() {
console.log(this.value);
return this.value.url;
},
set(newVal, oldValue) {
if (newVal !== this.value.url) {
this.value.url = newValue;
this.$emit("input", this.value);
}
},
},
currentValue: {
get() {
const v = this.value ?? "";
if (Array.isArray(v)) {
if (v.length === 0) {
return "";
}
return this.multiple
? this.value.reduce((str_value, v) => str_value + String(v) + "\n", "")
: String(this.value[0]);
}
return String(v);
},
set(newVal, oldVal) {
if (newVal !== oldVal) {
this.$emit("input", newVal);
}
},
},
},
};
</script>
<style scoped>
.ui-input-linked {
border-left-width: 0.5rem;
}
</style>
15 changes: 15 additions & 0 deletions client/src/components/Form/FormElement.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { FormParameterAttributes, FormParameterTypes, FormParameterValue }
import FormBoolean from "./Elements/FormBoolean.vue";
import FormColor from "./Elements/FormColor.vue";
import FormData from "./Elements/FormData/FormData.vue";
import FormDataUri from "./Elements/FormData/FormDataUri.vue";
import FormDataDialog from "./Elements/FormDataDialog.vue";
import FormDirectory from "./Elements/FormDirectory.vue";
import FormDrilldown from "./Elements/FormDrilldown/FormDrilldown.vue";
Expand Down Expand Up @@ -130,6 +131,14 @@ const elementId = computed(() => `form-element-${props.id}`);
const hasAlert = computed(() => alerts.value.length > 0);
const showPreview = computed(() => (collapsed.value && attrs.value["collapsible_preview"]) || props.disabled);
const showField = computed(() => !collapsed.value && !props.disabled);
const isUriDataField = computed(() => {
const dataField = props.type == 'data';
if (dataField && props.value && props.value.hasOwnProperty("src")) {
const src = props.value.src;
return src == "url";
}
return true;
});
const previewText = computed(() => attrs.value["text_value"]);
const helpText = computed(() => {
Expand Down Expand Up @@ -285,6 +294,12 @@ function onAlert(value: string | undefined) {
:options="attrs.options"
:optional="attrs.optional"
:multiple="attrs.multiple" />
<FormDataUri
v-else-if="isUriDataField"
:id="id"
v-model="currentValue"
:value="attrs.value"
:multiple="attrs.multiple" />
<FormData
v-else-if="['data', 'data_collection'].includes(props.type)"
:id="id"
Expand Down
8 changes: 8 additions & 0 deletions client/src/components/Landing/ToolLanding.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script setup lang="ts">
</script>

<template>
<div>
This a place holder for a feature that is not currently implemented.
</div>
</template>
62 changes: 62 additions & 0 deletions client/src/components/Landing/WorkflowLanding.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { onMounted, ref } from 'vue'
import { type components, GalaxyApi } from "@/api";
import { type ClaimLandingPayload } from "@/api/landings";
import { errorMessageAsString } from "@/utils/simple-error";
import LoadingSpan from "@/components/LoadingSpan.vue";
import WorkflowRun from "@/components/Workflow/Run/WorkflowRun.vue";
interface Props {
uuid: string;
secret?: string;
}
const props = withDefaults(defineProps<Props>(), {
secret: undefined,
});
const workflowId = ref<string | null>(null);
const errorMessage = ref<string | null>(null);
const requestState = ref<Record<string, never> | null>(null);
onMounted(async () => {
const payload: ClaimLandingPayload = {};
if (props.secret) {
payload['client_secret'] = props.secret;
}
const { data, error } = await GalaxyApi().POST("/api/workflow_landings/{uuid}/claim", {
params: {
path: { uuid: props.uuid },
},
body: payload,
});
if (data) {
workflowId.value = data.workflow_id;
requestState.value = data.request_state;
} else {
errorMessage.value = errorMessageAsString(error);
}
console.log(data);
});
</script>

<template>
<div>
<div v-if="!workflowId">
<LoadingSpan message="Loading workflow parameters" />
</div>
<div v-else-if="errorMessage">
<BAlert variant="danger" show>
{{ errorMessage }}
</BAlert>
</div>
<div v-else>
<WorkflowRun :workflow-id="workflowId" :prefer-simple-form="true" :request-state="requestState" />
</div>
</div>
</template>
3 changes: 3 additions & 0 deletions client/src/components/Workflow/Run/WorkflowRun.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ interface Props {
preferSimpleForm?: boolean;
simpleFormTargetHistory?: string;
simpleFormUseJobCache?: boolean;
requestState?: Record<string, never>;
}
const props = withDefaults(defineProps<Props>(), {
version: undefined,
preferSimpleForm: false,
simpleFormTargetHistory: "current",
simpleFormUseJobCache: false,
requestState: undefined,
});
const loading = ref(true);
Expand Down Expand Up @@ -200,6 +202,7 @@ defineExpose({
:target-history="simpleFormTargetHistory"
:use-job-cache="simpleFormUseJobCache"
:can-mutate-current-history="canRunOnHistory"
:request-state="requestState"
@submissionSuccess="handleInvocations"
@submissionError="handleSubmissionError"
@showAdvanced="showAdvanced" />
Expand Down
11 changes: 10 additions & 1 deletion client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export default {
type: Boolean,
required: true,
},
requestState: {
type: Object,
required: false,
}
},
setup() {
const { config, isConfigLoaded } = useConfig(true);
Expand Down Expand Up @@ -135,17 +139,22 @@ export default {
if (isWorkflowInput(step.step_type)) {
const stepName = new String(step.step_index);
const stepLabel = step.step_label || new String(step.step_index + 1);
const stepType = step.step_type;
const help = step.annotation;
const longFormInput = step.inputs[0];
const stepAsInput = Object.assign({}, longFormInput, {
name: stepName,
help: help,
label: stepLabel,
});
if (this.requestState[stepLabel]) {
const value = this.requestState[stepLabel];
stepAsInput.value = value;
}
// disable collection mapping...
stepAsInput.flavor = "module";
inputs.push(stepAsInput);
this.inputTypes[stepName] = step.step_type;
this.inputTypes[stepName] = stepType;
}
});
return inputs;
Expand Down
2 changes: 2 additions & 0 deletions client/src/entry/analysis/modules/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import WorkflowRun from "components/Workflow/Run/WorkflowRun";
import decodeUriComponent from "decode-uri-component";
import CenterFrame from "entry/analysis/modules/CenterFrame";
import WorkflowLanding from "./WorkflowLanding";
export default {
components: {
CenterFrame,
Expand Down
12 changes: 12 additions & 0 deletions client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import HistoryImport from "components/HistoryImport";
import InteractiveTools from "components/InteractiveTools/InteractiveTools";
import JobDetails from "components/JobInformation/JobDetails";
import CarbonEmissionsCalculations from "components/JobMetrics/CarbonEmissions/CarbonEmissionsCalculations";
import ToolLanding from "components/Landing/ToolLanding";
import WorkflowLanding from "components/Landing/WorkflowLanding";
import PageDisplay from "components/PageDisplay/PageDisplay";
import PageEditor from "components/PageEditor/PageEditor";
import ToolSuccess from "components/Tool/ToolSuccess";
Expand Down Expand Up @@ -494,6 +496,16 @@ export function getRouter(Galaxy) {
path: "tools/json",
component: ToolsJson,
},
{
path: "tool_landings/:uuid",
component: ToolLanding,
props: true,
},
{
path: "workflow_landings/:uuid",
component: WorkflowLanding,
props: true,
},
{
path: "user",
component: UserPreferences,
Expand Down
78 changes: 78 additions & 0 deletions lib/galaxy/managers/landing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import json
from uuid import uuid4

from pydantic import UUID4

from galaxy.schema.schema import (
ClaimLandingPayload,
CreateToolLandingRequestPayload,
CreateWorkflowLandingRequestPayload,
ToolLandingRequest,
WorkflowLandingRequest,
)
from galaxy.structured_app import StructuredApp
from .context import ProvidesUserContext


class LandingRequestManager:

def __init__(self, app: StructuredApp):
self.app = app

def create_tool_landing_request(self, payload: CreateToolLandingRequestPayload) -> ToolLandingRequest:
response_model = ToolLandingRequest(
tool_id=payload.tool_id,
tool_version=payload.tool_version,
request_state=payload.request_state,
uuid=uuid4(),
state="unclaimed",
)

with open("request.json", "w") as f:
f.write(json.dumps(response_model.model_dump(mode="json")))
return response_model

def create_workflow_landing_request(self, payload: CreateWorkflowLandingRequestPayload) -> WorkflowLandingRequest:
response_model = WorkflowLandingRequest(
workflow_id=payload.workflow_id,
workflow_target_type=payload.workflow_target_type,
request_state=payload.request_state,
uuid=uuid4(),
state="unclaimed",
)

with open("request.json", "w") as f:
f.write(json.dumps(response_model.model_dump(mode="json")))
return response_model

def claim_tool_landing_request(
self, trans: ProvidesUserContext, uuid: UUID4, claim: ClaimLandingPayload
) -> ToolLandingRequest:
with open("request.json") as f_in:
request = ToolLandingRequest.model_validate(json.load(f_in))
request.state = "claimed"

with open("request.json", "w") as f_out:
f_out.write(json.dumps(request.model_dump(mode="json")))
return request

def claim_workflow_landing_request(
self, trans: ProvidesUserContext, uuid: UUID4, claim: ClaimLandingPayload
) -> WorkflowLandingRequest:
with open("request.json") as f_in:
request = WorkflowLandingRequest.model_validate(json.load(f_in))
request.state = "claimed"

with open("request.json", "w") as f_out:
f_out.write(json.dumps(request.model_dump(mode="json")))
return request

def get_tool_landing_request(self, trans: ProvidesUserContext, uuid: UUID4) -> ToolLandingRequest:
with open("request.json") as f_in:
request = ToolLandingRequest.model_validate(json.load(f_in))
return request

def get_workflow_landing_request(self, trans: ProvidesUserContext, uuid: UUID4) -> WorkflowLandingRequest:
with open("request.json") as f_in:
request = WorkflowLandingRequest.model_validate(json.load(f_in))
return request
Loading

0 comments on commit 740ebbc

Please sign in to comment.