lhcb-ntuple-wizard 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ interface Props {
3
3
  row: RowData;
4
4
  hideDownloadButtons: boolean;
5
5
  hideUploadButtons: boolean;
6
- variablesPath: string;
6
+ basePath: string;
7
7
  }
8
- export declare function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, variablesPath }: Props): import("react/jsx-runtime").JSX.Element | null;
8
+ export declare function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, basePath }: Props): import("react/jsx-runtime").JSX.Element | null;
9
9
  export {};
@@ -11,7 +11,8 @@ import { useMetadata } from "../providers/MetadataProvider";
11
11
  import { downloadText } from "../utils/utils";
12
12
  import { YamlFile } from "../models/yamlFile";
13
13
  import { UploadDttConfigModal } from "./modals/UploadDttConfigModal";
14
- export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, variablesPath }) {
14
+ import { VARIABLES_PATH } from "../constants";
15
+ export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, basePath }) {
15
16
  const { rows, updateRow } = useRows();
16
17
  const navigate = useNavigate();
17
18
  const metadata = useMetadata();
@@ -46,7 +47,7 @@ export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, vari
46
47
  };
47
48
  const handleConfigureDtt = () => {
48
49
  updateRow(row.id, (r) => ({ ...r, editTree: true }));
49
- void navigate(variablesPath);
50
+ void navigate(`${basePath}${VARIABLES_PATH}`);
50
51
  };
51
52
  const handleDeleteDtt = () => {
52
53
  updateRow(row.id, (r) => ({ ...r, dtt: null }));
@@ -1,7 +1,7 @@
1
1
  interface Props {
2
2
  hideDownloadButtons: boolean;
3
3
  hideUploadButtons: boolean;
4
- variablesPath: string;
4
+ basePath: string;
5
5
  }
6
- export declare function RequestRow({ hideDownloadButtons, hideUploadButtons, variablesPath }: Props): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function RequestRow({ hideDownloadButtons, hideUploadButtons, basePath }: Props): import("react/jsx-runtime").JSX.Element | null;
7
7
  export {};
@@ -9,12 +9,12 @@ import { useRows } from "../providers/RowsProvider";
9
9
  import { BookkeepingPathDropdown } from "./BookkeepingPathDropdown";
10
10
  import { StrippingLineDropdown } from "./StrippingLineDropdown";
11
11
  import { useRow } from "../providers/RowProvider";
12
- export function RequestRow({ hideDownloadButtons, hideUploadButtons, variablesPath }) {
12
+ export function RequestRow({ hideDownloadButtons, hideUploadButtons, basePath }) {
13
13
  const metadata = useMetadata();
14
14
  const { row } = useRow();
15
15
  const { removeRow } = useRows();
16
16
  if (!metadata) {
17
17
  return null;
18
18
  }
19
- return (_jsxs(Row, { className: "align-items-center", children: [_jsx(Col, { lg: true, children: _jsx(DttNameInput, { row: row, hideDownloadButtons: hideDownloadButtons, hideUploadButtons: hideUploadButtons, variablesPath: variablesPath }) }), _jsx(Col, { lg: true, children: _jsx(StrippingLineDropdown, {}) }), _jsx(Col, { xs: "auto", children: _jsx(OverlayTrigger, { trigger: "click", placement: "right", rootClose: true, overlay: _jsx(Popover, { children: _jsx(Popover.Body, { children: row.lines.map((line) => (_jsx(StrippingLineInfo, { line: line.line, stream: line.stream, versions: line.versions, showLink: true }, line.line))) }) }), children: _jsx(Button, { disabled: row.lines.length === 0, children: _jsx(InfoCircle, {}) }) }) }), _jsx(Col, { children: _jsx(BookkeepingPathDropdown, {}) }), _jsx(Col, { xs: "auto", children: _jsx(DeleteButton, { action: () => removeRow(row.id) }) })] }));
19
+ return (_jsxs(Row, { className: "align-items-center", children: [_jsx(Col, { lg: true, children: _jsx(DttNameInput, { row: row, hideDownloadButtons: hideDownloadButtons, hideUploadButtons: hideUploadButtons, basePath: basePath }) }), _jsx(Col, { lg: true, children: _jsx(StrippingLineDropdown, {}) }), _jsx(Col, { xs: "auto", children: _jsx(OverlayTrigger, { trigger: "click", placement: "right", rootClose: true, overlay: _jsx(Popover, { children: _jsx(Popover.Body, { children: row.lines.map((line) => (_jsx(StrippingLineInfo, { line: line.line, stream: line.stream, versions: line.versions, showLink: true }, line.line))) }) }), children: _jsx(Button, { disabled: row.lines.length === 0, children: _jsx(InfoCircle, {}) }) }) }), _jsx(Col, { children: _jsx(BookkeepingPathDropdown, {}) }), _jsx(Col, { xs: "auto", children: _jsx(DeleteButton, { action: () => removeRow(row.id) }) })] }));
20
20
  }
@@ -16,25 +16,14 @@ export function UploadDttConfigModal({ onClose, currentRow }) {
16
16
  return;
17
17
  }
18
18
  setLoading(true);
19
- const { rows, emails, errors, warnings } = await parseProductionFiles(Array.from(files), metadata);
19
+ const result = await parseProductionFiles(Array.from(files), metadata);
20
20
  setLoading(false);
21
- if (Object.keys(errors).length > 0) {
22
- const errorMessages = Object.entries(errors)
23
- .map(([file, error]) => `${file}: ${error}`)
24
- .join("\n");
25
- alert(`Some errors were found while importing the configuration files:\n\n${errorMessages}`);
21
+ if (typeof result === "string") {
22
+ alert(result);
26
23
  return;
27
24
  }
28
- else if (Object.keys(warnings).length > 0) {
29
- const warningMessages = Object.entries(warnings)
30
- .map(([file, warning]) => `${file}: ${warning}`)
31
- .join("\n");
32
- if (!confirm(`Some warnings were found while importing the configuration files:\n\n${warningMessages}\n\nContinue?`)) {
33
- return;
34
- }
35
- }
36
25
  if (currentRow) {
37
- const importedRow = rows[0];
26
+ const importedRow = result.rows[0];
38
27
  const importedConfig = importedRow.dtt.config;
39
28
  if (importedConfig.descriptorTemplate === currentRow.decay.descriptors.template) {
40
29
  updateRow(currentRow.id, (r) => ({
@@ -48,8 +37,8 @@ export function UploadDttConfigModal({ onClose, currentRow }) {
48
37
  }
49
38
  }
50
39
  else {
51
- setRows(rows);
52
- setContactEmails(emails);
40
+ setRows(result.rows);
41
+ setContactEmails(result.emails);
53
42
  onClose();
54
43
  }
55
44
  }
@@ -1,6 +1,19 @@
1
1
  import Dtt from "./dtt";
2
2
  import { RowData } from "./rowData";
3
3
  import { MetadataContext } from "../providers/MetadataProvider";
4
+ import { JobConfig } from "./jobConfig";
5
+ interface InfoYamlDefaults {
6
+ application: string;
7
+ wg: string;
8
+ inform: string[];
9
+ automatically_configure: boolean;
10
+ output: string;
11
+ }
12
+ export type InfoYaml = {
13
+ defaults: InfoYamlDefaults;
14
+ } & {
15
+ [K in string as K extends "defaults" ? never : K]?: JobConfig;
16
+ };
4
17
  export declare class YamlFile {
5
18
  name: string;
6
19
  content: string;
@@ -8,3 +21,4 @@ export declare class YamlFile {
8
21
  static fromDtt(dtt: Dtt): YamlFile;
9
22
  static createInfoYaml(rows: RowData[], metadata: MetadataContext): YamlFile;
10
23
  }
24
+ export {};
@@ -14,15 +14,7 @@ export class YamlFile {
14
14
  const uniquePathSet = new Set(rows.flatMap((row) => row.paths));
15
15
  const uniquePaths = [...uniquePathSet].sort();
16
16
  const pathIndex = new Map(uniquePaths.map((p, i) => [p, i]));
17
- const info = {
18
- defaults: {
19
- application: `DaVinci/${metadata.tupleTools.applicationInfo.DaVinci}`,
20
- wg: "OpenData",
21
- inform: [],
22
- automatically_configure: true,
23
- output: "DVNtuple.root",
24
- },
25
- };
17
+ const jobs = {};
26
18
  for (const row of rows) {
27
19
  if (!row.dtt)
28
20
  continue;
@@ -32,8 +24,8 @@ export class YamlFile {
32
24
  continue;
33
25
  const key = `job${jobID}`;
34
26
  const dttFile = row.dtt.getName();
35
- if (key in info) {
36
- info[key].options.push(dttFile);
27
+ if (jobs[key]) {
28
+ jobs[key].options.push(dttFile);
37
29
  }
38
30
  else {
39
31
  const job = {
@@ -42,14 +34,23 @@ export class YamlFile {
42
34
  };
43
35
  if (path.includes("MDST")) {
44
36
  const stream = row.lines[0]?.stream;
45
- if (stream) {
37
+ if (stream)
46
38
  job.root_in_tes = `/Event/${stream}`;
47
- }
48
39
  }
49
- info[key] = job;
40
+ jobs[key] = job;
50
41
  }
51
42
  }
52
43
  }
44
+ const info = {
45
+ defaults: {
46
+ application: `DaVinci/${metadata.tupleTools.applicationInfo.DaVinci}`,
47
+ wg: "OpenData",
48
+ inform: [],
49
+ automatically_configure: true,
50
+ output: "DVNtuple.root",
51
+ },
52
+ ...jobs,
53
+ };
53
54
  return new YamlFile("info.yaml", yaml.dump(info));
54
55
  }
55
56
  }
@@ -1,3 +1,13 @@
1
+ /*****************************************************************************\
2
+ * (c) Copyright 2021-2024 CERN for the benefit of the LHCb Collaboration *
3
+ * *
4
+ * This software is distributed under the terms of the GNU General Public *
5
+ * Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". *
6
+ * *
7
+ * In applying this licence, CERN does not waive the privileges and immunities *
8
+ * granted to it by virtue of its status as an Intergovernmental Organization *
9
+ * or submit itself to any jurisdiction. *
10
+ \*****************************************************************************/
1
11
  import { ReactNode } from "react";
2
12
  export type NtupleWizardVariant = "standalone" | "embedded";
3
13
  interface Props {
@@ -9,12 +9,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
9
9
  * granted to it by virtue of its status as an Intergovernmental Organization *
10
10
  * or submit itself to any jurisdiction. *
11
11
  \*****************************************************************************/
12
- import yaml from "js-yaml";
13
12
  import { useEffect, useState } from "react";
14
13
  import { Alert, Button, Col, Row, Stack } from "react-bootstrap";
15
14
  import { Download, Send } from "react-bootstrap-icons";
16
15
  import { useLocation } from "react-router-dom";
17
- import { downloadZip, parseProductionFiles } from "../utils/utils";
16
+ import { downloadZip, processProductionFiles } from "../utils/utils";
18
17
  import { DeleteButton } from "../components/DeleteButton";
19
18
  import { useMetadata } from "../providers/MetadataProvider";
20
19
  import { ConfigFilesUploadingAlert } from "../components/ConfigFilesUploadingAlert";
@@ -23,7 +22,6 @@ import { useRequest } from "../providers/RequestProvider";
23
22
  import { LoadingIndicator } from "../components/LoadingIndicator";
24
23
  import { RequestRow } from "../components/RequestRow";
25
24
  import { UploadDttConfigModal } from "../components/modals/UploadDttConfigModal";
26
- import { VARIABLES_PATH } from "../constants";
27
25
  import { ReasonForRequestModal } from "../components/modals/ReasonForRequestModal";
28
26
  import { RowProvider } from "../providers/RowProvider";
29
27
  import { ProductionNameInput } from "../components/ProductionNameInput";
@@ -33,7 +31,7 @@ export function RequestPage({ basePath, submitLocation, requestReasonMessage, re
33
31
  const location = useLocation();
34
32
  const metadata = useMetadata();
35
33
  const { rows, setRows, generateAllFiles } = useRows();
36
- const { emailIsKnown, productionName, validation, showErrors, clearAll, trySubmit } = useRequest();
34
+ const { emailIsKnown, productionName, setProductionName, validation, showErrors, clearAll, trySubmit } = useRequest();
37
35
  const [showProdUploadModal, setShowProdUploadModal] = useState(false);
38
36
  const [prodUploadLoading, setProdUploadLoading] = useState(false);
39
37
  const [requestSubmitted, setRequestSubmitted] = useState(false);
@@ -45,34 +43,32 @@ export function RequestPage({ basePath, submitLocation, requestReasonMessage, re
45
43
  }
46
44
  const urlParams = new URLSearchParams(location.search);
47
45
  if (urlParams.get("clone") === "1") {
48
- handleClone().catch(console.error);
49
- }
50
- }, [metadata, location.search]);
51
- async function handleClone() {
52
- if (!metadata) {
53
- return;
54
- }
55
- const raw = localStorage.getItem("yamlFilesToClone");
56
- if (!raw) {
57
- return;
58
- }
59
- setProdUploadLoading(true);
60
- setRows([]);
61
- try {
62
- const yamlFiles = JSON.parse(raw);
63
- for (const file of yamlFiles) {
64
- if (file.defaults)
65
- file.name = "info.yaml";
66
- file.blob = new Blob([yaml.dump(file)]);
46
+ const infoYamlString = localStorage.getItem("infoYamlToClone");
47
+ const dttConfigsString = localStorage.getItem("dttConfigsToClone");
48
+ if (!dttConfigsString) {
49
+ return;
50
+ }
51
+ setProdUploadLoading(true);
52
+ setRows([]);
53
+ try {
54
+ const infoYaml = infoYamlString ? JSON.parse(infoYamlString) : null;
55
+ const dttConfigs = JSON.parse(dttConfigsString);
56
+ const result = processProductionFiles(metadata, dttConfigs, infoYaml);
57
+ if (typeof result === "string") {
58
+ // Show the error message
59
+ alert(result);
60
+ return;
61
+ }
62
+ setRows(result.rows);
63
+ setProductionName("");
64
+ }
65
+ finally {
66
+ setProdUploadLoading(false);
67
+ localStorage.removeItem("infoYamlToClone");
68
+ localStorage.removeItem("dttConfigsToClone");
67
69
  }
68
- const { rows } = await parseProductionFiles(yamlFiles, metadata);
69
- setRows(rows);
70
- }
71
- finally {
72
- setProdUploadLoading(false);
73
- localStorage.removeItem("yamlFilesToClone");
74
70
  }
75
- }
71
+ }, [metadata, location.search]);
76
72
  const handlePrimaryAction = async () => {
77
73
  if (trySubmit()) {
78
74
  if (variant === "standalone") {
@@ -95,7 +91,7 @@ export function RequestPage({ basePath, submitLocation, requestReasonMessage, re
95
91
  if (submitted) {
96
92
  setRequestSubmitted(true);
97
93
  }
98
- } })), showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs("div", { className: "d-flex flex-column gap-3", children: [rows.map((row) => (_jsx(RowProvider, { row: row, children: _jsx(RequestRow, { hideDownloadButtons: variant === "standalone", hideUploadButtons: variant === "standalone", variablesPath: VARIABLES_PATH }, row.id) }, row.id))), prodUploadLoading && _jsx(ConfigFilesUploadingAlert, {}), requestSubmitted && (_jsxs(Alert, { variant: "success", dismissible: true, onClose: () => setRequestSubmitted(false), children: [_jsx(Alert.Heading, { children: "Request submitted!" }), requestSubmittedMessage] })), showErrors &&
94
+ } })), showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs("div", { className: "d-flex flex-column gap-3", children: [rows.map((row) => (_jsx(RowProvider, { row: row, children: _jsx(RequestRow, { hideDownloadButtons: variant === "standalone", hideUploadButtons: variant === "standalone", basePath: basePath }, row.id) }, row.id))), prodUploadLoading && _jsx(ConfigFilesUploadingAlert, {}), requestSubmitted && (_jsxs(Alert, { variant: "success", dismissible: true, onClose: () => setRequestSubmitted(false), children: [_jsx(Alert.Heading, { children: "Request submitted!" }), requestSubmittedMessage] })), showErrors &&
99
95
  (rows.length === 0 ||
100
96
  !validation.allRowsHaveDtt ||
101
97
  !validation.allRowsHaveStrippingLine ||
@@ -1,19 +1,15 @@
1
- import { YamlFile } from "../models/yamlFile";
2
- import { BlobFile } from "../models/blobFile";
1
+ import { InfoYaml, YamlFile } from "../models/yamlFile";
2
+ import { SavedDttConfig } from "../models/dtt";
3
3
  import { RowData } from "../models/rowData";
4
4
  import { MetadataContext } from "../providers/MetadataProvider";
5
- /**
6
- * Parses a set of production configuration files to DttConfigs and returns the corresponding rows and emails.
7
- * @param files The files to parse
8
- * @param metadata The metadata to use for parsing
9
- * @returns The parsed rows and emails
10
- */
11
- export declare const parseProductionFiles: (files: (BlobFile | File)[], metadata: MetadataContext) => Promise<{
5
+ export declare function parseProductionFiles(files: File[], metadata: MetadataContext): Promise<string | {
12
6
  rows: RowData[];
13
7
  emails: string[];
14
- errors: Record<string, string>;
15
- warnings: Record<string, string>;
16
8
  }>;
9
+ export declare function processProductionFiles(metadata: MetadataContext, configs: SavedDttConfig[], infoYaml: InfoYaml | null): string | {
10
+ rows: RowData[];
11
+ emails: string[];
12
+ };
17
13
  /**
18
14
  * Sanitizes a user-provided filename to prevent path traversal (Zip Slip)
19
15
  * and unsafe filesystem characters.