diff --git a/src/components/modals/LibrarySequenceModal.svelte b/src/components/modals/LibrarySequenceModal.svelte
new file mode 100644
index 0000000000..9f2b463522
--- /dev/null
+++ b/src/components/modals/LibrarySequenceModal.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+ {modalTitle}
+
+
+ Parcel (required)
+
+
+
+
+
+
+
+
+
diff --git a/src/components/sequencing/Sequences.svelte b/src/components/sequencing/Sequences.svelte
index 7a4651e436..958392f9bb 100644
--- a/src/components/sequencing/Sequences.svelte
+++ b/src/components/sequencing/Sequences.svelte
@@ -9,9 +9,13 @@
import { parcels, userSequences, userSequencesColumns, workspaces } from '../../stores/sequencing';
import type { User } from '../../types/app';
import type { Parcel, UserSequence, Workspace } from '../../types/sequencing';
+ import { satfToSequence } from '../../utilities/codemirror/satf/satf-sasf-utils';
+ import effects from '../../utilities/effects';
import { getSearchParameterNumber, setQueryParam } from '../../utilities/generic';
+ import { showLibrarySequenceModel } from '../../utilities/modal';
import { permissionHandler } from '../../utilities/permissionHandler';
import { featurePermissions } from '../../utilities/permissions';
+ import { showFailureToast } from '../../utilities/toast';
import Input from '../form/Input.svelte';
import CssGrid from '../ui/CssGrid.svelte';
import CssGridGutter from '../ui/CssGridGutter.svelte';
@@ -59,6 +63,41 @@
const workspaceId = getSearchParameterNumber(SearchParameters.WORKSPACE_ID);
goto(`${base}/sequencing/new${workspaceId ? `?${SearchParameters.WORKSPACE_ID}=${workspaceId}` : ''}`);
}
+
+ async function importLibarySequences(): Promise {
+ const workspaceId = getSearchParameterNumber(SearchParameters.WORKSPACE_ID);
+ if (workspaceId === null) {
+ console.log("Workspace doesn't exist");
+ showFailureToast("Library Import: Workspace doesn't exist");
+ return;
+ }
+ const { confirm, value } = await showLibrarySequenceModel();
+
+ if (confirm && value) {
+ const fileName = await value.libraryFile.name;
+ const type = fileName.slice(fileName.lastIndexOf('.') + 1);
+ if (type !== 'satf') {
+ console.log(`Unsupported file type ${type}`);
+ showFailureToast('Library Import: Unsupported file type');
+ return;
+ }
+ const contents = await value.libraryFile.text();
+ const parcel = await value.parcel;
+ const sequences = (await satfToSequence(contents)).sequences;
+ sequences.forEach(async seqN => {
+ await effects.createUserSequence(
+ {
+ definition: seqN.sequence,
+ name: seqN.name,
+ parcel_id: parcel,
+ seq_json: '',
+ workspace_id: workspaceId,
+ },
+ user,
+ );
+ });
+ }
+ }
@@ -86,6 +125,18 @@
>
New Sequence
+
+
diff --git a/src/utilities/codemirror/codemirror-utils.ts b/src/utilities/codemirror/codemirror-utils.ts
index 5dc316e65f..daa480b063 100644
--- a/src/utilities/codemirror/codemirror-utils.ts
+++ b/src/utilities/codemirror/codemirror-utils.ts
@@ -120,10 +120,11 @@ export function getDefaultVariableArgs(parameters: VariableDeclaration[]): strin
return parameter.allowable_values && parameter.allowable_values.length > 0
? `"${parameter.allowable_values[0]}"`
: parameter.enum_name
- ? `${parameter.enum_name}`
+ ? `"${parameter.enum_name}"`
: 'UNKNOWN';
default:
- throw Error(`unknown argument type ${parameter.type}`);
+ console.log(`Unknown argument type ${parameter.type}`);
+ return `ERROR:"${parameter.name}"`;
}
}) as string[];
}
diff --git a/src/utilities/codemirror/satf/satf-sasf-utils.test.ts b/src/utilities/codemirror/satf/satf-sasf-utils.test.ts
index 93a1f3cf5e..02da771e8d 100644
--- a/src/utilities/codemirror/satf/satf-sasf-utils.test.ts
+++ b/src/utilities/codemirror/satf/satf-sasf-utils.test.ts
@@ -91,9 +91,9 @@ describe('satfToSequence', () => {
expect(result.sequences[0].name).toStrictEqual('test');
expect(result.sequences[0].sequence).toStrictEqual(`## test
R00:01:00 01VV param6 10 false "abc" # This command turns, to correct position.
-@MODEL(x,1,"00:00:00")
-@MODEL(z,1.1,"00:00:00")
-@MODEL(y,"abc","00:00:00")`);
+@MODEL "x" 1 "00:00:00"
+@MODEL "z" 1.1 "00:00:00"
+@MODEL "y" "abc" "00:00:00"`);
});
it('should return multiple sequence with models', async () => {
@@ -127,9 +127,9 @@ R00:01:00 01VV param6 10 false "abc" # This command turns, to correct position.
expect(result.sequences[0].name).toStrictEqual('test');
expect(result.sequences[0].sequence).toStrictEqual(`## test
R00:01:00 01VV param6 10 false "abc" # This command turns, to correct position.
-@MODEL(x,1,"00:00:00")
-@MODEL(z,1.1,"00:00:00")
-@MODEL(y,"abc","00:00:00")`);
+@MODEL "x" 1 "00:00:00"
+@MODEL "z" 1.1 "00:00:00"
+@MODEL "y" "abc" "00:00:00"`);
});
});
diff --git a/src/utilities/codemirror/satf/satf-sasf-utils.ts b/src/utilities/codemirror/satf/satf-sasf-utils.ts
index 6018d8ebec..582e2c50ad 100644
--- a/src/utilities/codemirror/satf/satf-sasf-utils.ts
+++ b/src/utilities/codemirror/satf/satf-sasf-utils.ts
@@ -881,7 +881,7 @@ function parseModel(modelNode: SyntaxNode | null, text: string): string {
if (!keyNode || !valueNode) {
return null;
}
- return `@MODEL(${text.slice(keyNode.from, keyNode.to)},${text.slice(valueNode.from, valueNode.to)},"00:00:00")`;
+ return `@MODEL "${text.slice(keyNode.from, keyNode.to)}" ${text.slice(valueNode.from, valueNode.to)} "00:00:00"`;
})
.filter(model => model !== null)
.join('\n');
diff --git a/src/utilities/codemirror/satf/satf-sasf.grammar b/src/utilities/codemirror/satf/satf-sasf.grammar
index dea9ecfd47..e40037d42f 100644
--- a/src/utilities/codemirror/satf/satf-sasf.grammar
+++ b/src/utilities/codemirror/satf/satf-sasf.grammar
@@ -26,7 +26,7 @@
HeaderPairs { HeaderPair* }
HeaderPair {Key"="Value ";" newLine}
Key { (identifier | ":")* }
- Value { headerValue+ }
+ Value { (headerValue | "/")+ }
SfduHeader { headerMarker newLine HeaderPairs headerMarker}
Start { "$$"identifier identifier* newLine}
diff --git a/src/utilities/modal.ts b/src/utilities/modal.ts
index 34cb179fb6..ed04afcff1 100644
--- a/src/utilities/modal.ts
+++ b/src/utilities/modal.ts
@@ -12,6 +12,7 @@ import DeleteExternalEventSourceTypeModal from '../components/modals/DeleteExter
import DeleteExternalSourceModal from '../components/modals/DeleteExternalSourceModal.svelte';
import EditViewModal from '../components/modals/EditViewModal.svelte';
import ExpansionSequenceModal from '../components/modals/ExpansionSequenceModal.svelte';
+import LibrarySequenceModal from '../components/modals/LibrarySequenceModal.svelte';
import ManageGroupsAndTypesModal from '../components/modals/ManageGroupsAndTypesModal.svelte';
import ManagePlanConstraintsModal from '../components/modals/ManagePlanConstraintsModal.svelte';
import ManagePlanDerivationGroupsModal from '../components/modals/ManagePlanDerivationGroupsModal.svelte';
@@ -556,6 +557,39 @@ export async function showWorkspaceModal(
});
}
+export async function showLibrarySequenceModel(): Promise> {
+ return new Promise(resolve => {
+ if (browser) {
+ const target: ModalElement | null = document.querySelector('#svelte-modal');
+
+ if (target) {
+ const workspaceModal = new LibrarySequenceModal({
+ target,
+ });
+ target.resolve = resolve;
+
+ workspaceModal.$on('close', () => {
+ target.replaceChildren();
+ target.resolve = null;
+ resolve({ confirm: false });
+ workspaceModal.$destroy();
+ });
+
+ workspaceModal.$on('save', (e: CustomEvent<{ library: FileList; parcel: number }>) => {
+ const library = e.detail.library[0];
+ const parcel = e.detail.parcel;
+ target.replaceChildren();
+ target.resolve = null;
+ resolve({ confirm: true, value: { libraryFile: library, parcel } });
+ workspaceModal.$destroy();
+ });
+ }
+ } else {
+ resolve({ confirm: false });
+ }
+ });
+}
+
/**
* Shows a CreatePlanBranchModal with the supplied arguments.
*/
diff --git a/src/utilities/sequence-editor/sequence-linter.ts b/src/utilities/sequence-editor/sequence-linter.ts
index 653e849fa4..1324f9acf7 100644
--- a/src/utilities/sequence-editor/sequence-linter.ts
+++ b/src/utilities/sequence-editor/sequence-linter.ts
@@ -566,16 +566,24 @@ function validateActivateLoad(
} else {
value = parseInt(num);
}
- parameter.allowable_ranges?.forEach(range => {
- if (value < range.min || value > range.max) {
+
+ if (parameter.allowable_ranges) {
+ const invalidRanges = parameter.allowable_ranges.filter(range => {
+ return value < range.min || value > range.max;
+ });
+ if (invalidRanges.length === parameter.allowable_ranges.length) {
diagnostics.push({
from: arg.from,
- message: `Value must be between ${range.min} and ${range.max}`,
+ message: `Value must be between ${parameter.allowable_ranges
+ .map(range => {
+ return `[${range.min} and ${range.max}]`;
+ })
+ .join(' or ')}`,
severity: 'error',
to: arg.to,
});
}
- });
+ }
if (parameter.type === 'UINT') {
if (value < 0) {
@@ -1237,7 +1245,7 @@ function validateCommandStructure(
addDefault: (view: any) => any,
): Diagnostic | undefined {
if (arguments.length > 0) {
- if (!argsNode || argsNode.length === 0) {
+ if (!argsNode || (argsNode.length === 0 && exactArgSize > 0)) {
return {
actions: [],
from: stemNode.from,