lhcb-ntuple-wizard 2.0.1 → 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.
Files changed (60) hide show
  1. package/README.md +3 -4
  2. package/dist/App.js +1 -3
  3. package/dist/components/BookkeepingPathDropdown.d.ts +1 -0
  4. package/dist/components/BookkeepingPathDropdown.js +35 -0
  5. package/dist/components/DatasetInfo.d.ts +1 -1
  6. package/dist/components/DatasetInfo.js +9 -3
  7. package/dist/components/DttNameInput.d.ts +2 -2
  8. package/dist/components/DttNameInput.js +16 -18
  9. package/dist/components/NtupleWizard.d.ts +2 -7
  10. package/dist/components/NtupleWizard.js +6 -11
  11. package/dist/components/ProductionNameInput.d.ts +1 -0
  12. package/dist/components/ProductionNameInput.js +7 -0
  13. package/dist/components/RequestButtonGroup.d.ts +7 -0
  14. package/dist/components/RequestButtonGroup.js +29 -0
  15. package/dist/components/RequestEmailInput.d.ts +1 -0
  16. package/dist/components/RequestEmailInput.js +7 -0
  17. package/dist/components/RequestRow.d.ts +7 -0
  18. package/dist/components/RequestRow.js +20 -0
  19. package/dist/components/StrippingLineBadge.js +2 -3
  20. package/dist/components/StrippingLineDropdown.d.ts +1 -0
  21. package/dist/components/StrippingLineDropdown.js +48 -0
  22. package/dist/components/modals/ReasonForRequestModal.d.ts +2 -3
  23. package/dist/components/modals/ReasonForRequestModal.js +1 -8
  24. package/dist/components/modals/UploadDttConfigModal.d.ts +2 -2
  25. package/dist/components/modals/UploadDttConfigModal.js +46 -27
  26. package/dist/constants.d.ts +2 -0
  27. package/dist/constants.js +2 -0
  28. package/dist/models/bkPath.d.ts +9 -9
  29. package/dist/models/bkPath.js +37 -30
  30. package/dist/models/dtt.d.ts +33 -3
  31. package/dist/models/dtt.js +62 -0
  32. package/dist/models/yamlFile.d.ts +14 -1
  33. package/dist/models/yamlFile.js +16 -38
  34. package/dist/pages/DecaySearchPage.js +1 -1
  35. package/dist/pages/DecayTreeConfigPage.js +2 -4
  36. package/dist/pages/RequestPage.d.ts +20 -0
  37. package/dist/pages/RequestPage.js +101 -0
  38. package/dist/providers/RequestProvider.d.ts +14 -1
  39. package/dist/providers/RequestProvider.js +48 -2
  40. package/dist/providers/RowProvider.d.ts +15 -0
  41. package/dist/providers/RowProvider.js +41 -0
  42. package/dist/providers/RowsProvider.d.ts +4 -1
  43. package/dist/providers/RowsProvider.js +9 -2
  44. package/dist/utils/utils.d.ts +12 -1
  45. package/dist/utils/utils.js +533 -0
  46. package/package.json +8 -4
  47. package/dist/components/LineTableRow.d.ts +0 -9
  48. package/dist/components/LineTableRow.js +0 -123
  49. package/dist/components/SubmitRequestButton.d.ts +0 -10
  50. package/dist/components/SubmitRequestButton.js +0 -31
  51. package/dist/components/modals/UploadProdConfigModal.d.ts +0 -5
  52. package/dist/components/modals/UploadProdConfigModal.js +0 -27
  53. package/dist/models/blobFile.d.ts +0 -12
  54. package/dist/models/blobFile.js +0 -1
  55. package/dist/models/strippingLineOption.d.ts +0 -6
  56. package/dist/models/strippingLineOption.js +0 -1
  57. package/dist/pages/LinesTablePage.d.ts +0 -14
  58. package/dist/pages/LinesTablePage.js +0 -125
  59. package/dist/providers/ProductionConfigProvider.d.ts +0 -14
  60. package/dist/providers/ProductionConfigProvider.js +0 -76
@@ -1,123 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Col, OverlayTrigger, Popover, Row } from "react-bootstrap";
3
- import { StrippingLineInfo } from "./StrippingLineInfo";
4
- import { useMetadata } from "../providers/MetadataProvider";
5
- import { useRows } from "../providers/RowsProvider";
6
- import { DatasetInfo } from "./DatasetInfo";
7
- import BkPath from "../models/bkPath";
8
- import { QuestionCircle } from "react-bootstrap-icons";
9
- import { DeleteButton } from "./DeleteButton";
10
- import Select from "react-select";
11
- import Creatable from "react-select/creatable";
12
- import { DttNameInput } from "./DttNameInput";
13
- export function LineTableRow({ row, hideDownloadButtons, hideUploadButtons, variablesPath }) {
14
- const metadata = useMetadata();
15
- const { rows, setRows } = useRows();
16
- if (!metadata) {
17
- return null;
18
- }
19
- function getStrippingLineOptions() {
20
- let opts = {};
21
- let streams = [];
22
- let lines = [];
23
- let groups = [];
24
- Object.keys(row.decay.lines).forEach((streamLine) => {
25
- const [stream, line] = streamLine.split("/");
26
- streams.push(stream);
27
- lines.push(line);
28
- });
29
- const uniqueStreams = [...new Set(streams)];
30
- uniqueStreams.forEach((uniqueStream) => {
31
- opts[uniqueStream] = [];
32
- lines.forEach((line, i) => {
33
- if (streams[i] === uniqueStream) {
34
- let duplicate = false;
35
- const option = {
36
- value: line,
37
- label: (_jsx(StrippingLineInfo, { line: line, stream: uniqueStream, versions: row.decay.lines[uniqueStream + "/" + line] })),
38
- group: uniqueStream,
39
- };
40
- // Check for duplicates
41
- for (const option of opts[uniqueStream]) {
42
- if (option.label === line) {
43
- duplicate = true;
44
- }
45
- }
46
- if (!duplicate) {
47
- opts[uniqueStream].push(option);
48
- }
49
- }
50
- });
51
- groups.push({ label: uniqueStream, options: opts[uniqueStream] });
52
- });
53
- return groups;
54
- }
55
- function getOptionFromPath(path) {
56
- return {
57
- value: path,
58
- label: _jsx(DatasetInfo, { pathString: path }),
59
- };
60
- }
61
- const validatePath = (choice) => {
62
- const path = new BkPath(choice);
63
- if (!path.isValid()) {
64
- return false;
65
- }
66
- return row.lines.every((line) => {
67
- const streamName = line.stream.toLowerCase();
68
- const fileName = path.getFilename().split(".")[0].toLowerCase();
69
- const matchingVersion = line.versions.some((version) => path.getStrippingVersion() === version);
70
- return ["allstreams", streamName].includes(fileName) && matchingVersion;
71
- });
72
- };
73
- const handlePathChoice = (choices) => {
74
- const validPaths = choices.filter((choice) => validatePath(choice)).map((choice) => choice);
75
- const validPathOptions = row.pathOptions.filter((choice) => validatePath(choice));
76
- setRows((prev) => {
77
- return prev.map((r) => (r.id === row.id ? { ...r, paths: validPaths, pathOptions: validPathOptions } : r));
78
- });
79
- };
80
- const handleRemoveRow = () => {
81
- setRows(rows.filter((r) => r.id !== row.id));
82
- };
83
- const changeSelStrLines = (choices) => {
84
- if (choices.length > 1) {
85
- alert("Choosing multiple stripping lines per row can result in duplication of candidates within a given event and is therefore forbidden. To choose a different stripping line, remove the current selection.");
86
- choices.pop();
87
- }
88
- const lines = choices.map((choice) => ({
89
- stream: choice.group,
90
- line: choice.value,
91
- versions: row.decay.lines[choice.group + "/" + choice.value],
92
- }));
93
- setRows((prev) => {
94
- return prev.map((r) => r.id === row.id ? { ...r, lines, dtt: r.dtt ? r.dtt.withInputsFromLines(lines) : null } : r);
95
- });
96
- handlePathChoice(row.paths);
97
- };
98
- const handleCreatePath = (choice) => {
99
- if (!validatePath(choice)) {
100
- return;
101
- }
102
- const paths = [...row.paths, choice];
103
- const pathOptions = [...row.pathOptions, choice];
104
- setRows((prev) => {
105
- return prev.map((r) => (r.id === row.id ? { ...r, paths, pathOptions } : r));
106
- });
107
- };
108
- const getDefaultBkPaths = () => {
109
- const defaultOptions = metadata.dataset
110
- .filter((path) => validatePath(path))
111
- .map((path) => getOptionFromPath(path));
112
- return [...defaultOptions, ...row.pathOptions.map(getOptionFromPath)];
113
- };
114
- 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(Select, { isMulti: true, options: getStrippingLineOptions(), defaultValue: row.lines.map((lineInfo) => ({
115
- value: lineInfo.line,
116
- label: (_jsx(StrippingLineInfo, { line: lineInfo.line, stream: lineInfo.stream, versions: lineInfo.versions, showLink: false })),
117
- group: lineInfo.stream,
118
- })), placeholder: "Stripping line", onChange: (newValue) => changeSelStrLines(newValue),
119
- // Display the dropdown above text inputs and other elements
120
- menuPortalTarget: document.body, menuPosition: "fixed", styles: {
121
- menuPortal: (base) => ({ ...base, zIndex: 9999 }),
122
- } }) }), _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, { className: "ms-auto", type: "button", disabled: row.lines.length === 0, children: _jsx(QuestionCircle, {}) }) }) }), _jsx(Col, { children: _jsx(Creatable, { isMulti: true, options: getDefaultBkPaths() /*TODO: use ODP records when ready*/, value: row.paths.map(getOptionFromPath), isDisabled: row.lines.length === 0, placeholder: "Bookkeeping path", onChange: (newValue) => handlePathChoice(newValue.map((item) => item.value)), onCreateOption: (inputValue) => handleCreatePath(inputValue), closeMenuOnSelect: false }) }), _jsx(Col, { xs: "auto", children: _jsx(DeleteButton, { action: handleRemoveRow }) })] }, row.id));
123
- }
@@ -1,10 +0,0 @@
1
- interface Props {
2
- disabledMessage: string | null;
3
- onSubmit: () => void;
4
- submitLocation?: string;
5
- hideDownloadButtons?: boolean;
6
- requestReasonMessage?: string;
7
- csrfToken?: string;
8
- }
9
- export declare function SubmitRequestButton({ disabledMessage, onSubmit, submitLocation, hideDownloadButtons, requestReasonMessage, csrfToken, }: Props): import("react/jsx-runtime").JSX.Element | null;
10
- export {};
@@ -1,31 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Button, OverlayTrigger, Tooltip } from "react-bootstrap";
3
- import { Download, Send } from "react-bootstrap-icons";
4
- import { useState } from "react";
5
- import { ReasonForRequestModal } from "./modals/ReasonForRequestModal";
6
- import { downloadZip } from "../utils/utils";
7
- import { useRows } from "../providers/RowsProvider";
8
- import { useRequest } from "../providers/RequestProvider";
9
- export function SubmitRequestButton({ disabledMessage, onSubmit, submitLocation, hideDownloadButtons, requestReasonMessage, csrfToken, }) {
10
- const { generateAllFiles } = useRows();
11
- const { productionName } = useRequest();
12
- const [showReasonForRequestModal, setShowReasonForRequestModal] = useState(false);
13
- const downloadAllFiles = () => {
14
- const allFiles = generateAllFiles();
15
- return downloadZip(allFiles, `${productionName}.zip`);
16
- };
17
- if (submitLocation) {
18
- return (_jsxs(_Fragment, { children: [_jsx(OverlayTrigger, { show: !!disabledMessage, overlay: _jsx(Tooltip, { children: disabledMessage }), children: _jsx("span", { children: _jsxs(Button, { className: "align-items-center d-flex gap-1", disabled: !!disabledMessage, onClick: () => setShowReasonForRequestModal(true), children: [_jsx(Send, {}), " Submit"] }) }) }), showReasonForRequestModal && (_jsx(ReasonForRequestModal, { submitLocation: submitLocation, requestReasonMessage: requestReasonMessage, onClose: (submitted) => {
19
- setShowReasonForRequestModal(false);
20
- if (submitted) {
21
- onSubmit();
22
- }
23
- }, csrfToken: csrfToken }))] }));
24
- }
25
- if (!hideDownloadButtons) {
26
- return (_jsx(OverlayTrigger, { show: !!disabledMessage, overlay: _jsx(Tooltip, { children: disabledMessage }), children: _jsx("span", { children: _jsxs(Button, { className: "align-items-center d-flex gap-1", disabled: !!disabledMessage, onClick: () => {
27
- downloadAllFiles().catch(console.error);
28
- }, children: [_jsx(Download, {}), " Download"] }) }) }));
29
- }
30
- return null;
31
- }
@@ -1,5 +0,0 @@
1
- interface Props {
2
- onClose: () => void;
3
- }
4
- export declare function UploadProdConfigModal({ onClose }: Props): import("react/jsx-runtime").JSX.Element | null;
5
- export {};
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Form, Modal } from "react-bootstrap";
3
- import { useMetadata } from "../../providers/MetadataProvider";
4
- import { useProductionConfig } from "../../providers/ProductionConfigProvider";
5
- import { useRows } from "../../providers/RowsProvider";
6
- import { useRequest } from "../../providers/RequestProvider";
7
- export function UploadProdConfigModal({ onClose }) {
8
- const metadata = useMetadata();
9
- const { parseProductionFiles } = useProductionConfig();
10
- const { setRows } = useRows();
11
- const { setContactEmails } = useRequest();
12
- if (!metadata) {
13
- return null;
14
- }
15
- return (_jsxs(Modal, { show: true, animation: false, children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: " Upload configuration file " }) }), _jsx(Modal.Body, { children: _jsx(Form.Group, { controlId: "formFile2", className: "mb-3", children: _jsx(Form.Control, { type: "file", multiple: true, onChange: (event) => {
16
- const files = event.target.files;
17
- if (!files) {
18
- return;
19
- }
20
- parseProductionFiles(Array.from(files), metadata)
21
- .then(({ rows, emails }) => {
22
- setRows(rows);
23
- setContactEmails(emails);
24
- })
25
- .catch(console.error);
26
- } }) }) }), _jsx(Modal.Footer, { children: _jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" }) })] }));
27
- }
@@ -1,12 +0,0 @@
1
- export interface BlobFileDefaults {
2
- application: string;
3
- wg: string;
4
- inform: string[];
5
- automatically_configure: boolean;
6
- output: string;
7
- }
8
- export interface BlobFile {
9
- name: string;
10
- defaults: BlobFileDefaults;
11
- blob: Blob;
12
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,6 +0,0 @@
1
- import { JSX } from "react";
2
- export interface StrippingLineOption {
3
- group: string;
4
- label: JSX.Element | string;
5
- value: string;
6
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,14 +0,0 @@
1
- import { ReactNode } from "react";
2
- interface Props {
3
- decaysPath: string;
4
- variablesPath: string;
5
- submitLocation?: string;
6
- hideDownloadButtons?: boolean;
7
- hideUploadButtons?: boolean;
8
- emailIsKnown?: boolean;
9
- requestReasonMessage?: string;
10
- requestSubmittedMessage?: ReactNode;
11
- csrfToken?: string;
12
- }
13
- export declare function LinesTablePage({ submitLocation, hideDownloadButtons, hideUploadButtons, emailIsKnown, requestReasonMessage, requestSubmittedMessage, csrfToken, variablesPath, decaysPath, }: Props): import("react/jsx-runtime").JSX.Element;
14
- export {};
@@ -1,125 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /*****************************************************************************\
3
- * (c) Copyright 2021-2024 CERN for the benefit of the LHCb Collaboration *
4
- * *
5
- * This software is distributed under the terms of the GNU General Public *
6
- * Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". *
7
- * *
8
- * In applying this licence, CERN does not waive the privileges and immunities *
9
- * granted to it by virtue of its status as an Intergovernmental Organization *
10
- * or submit itself to any jurisdiction. *
11
- \*****************************************************************************/
12
- import EmailValidator from "email-validator";
13
- import yaml from "js-yaml";
14
- import { useEffect, useMemo, useState } from "react";
15
- import { Alert, Badge, Button, ButtonGroup, Col, FormControl, InputGroup, OverlayTrigger, Row, Stack, Tooltip, } from "react-bootstrap";
16
- import { Download, GearWideConnected, PlusLg, Upload } from "react-bootstrap-icons";
17
- import { useLocation, useNavigate } from "react-router-dom";
18
- import { downloadText } from "../utils/utils";
19
- import { DeleteButton } from "../components/DeleteButton";
20
- import { useMetadata } from "../providers/MetadataProvider";
21
- import { ConfigFilesUploadingAlert } from "../components/ConfigFilesUploadingAlert";
22
- import { useRows } from "../providers/RowsProvider";
23
- import { useRequest } from "../providers/RequestProvider";
24
- import { LoadingIndicator } from "../components/LoadingIndicator";
25
- import { LineTableRow } from "../components/LineTableRow";
26
- import { useProductionConfig } from "../providers/ProductionConfigProvider";
27
- import { UploadProdConfigModal } from "../components/modals/UploadProdConfigModal";
28
- import { SubmitRequestButton } from "../components/SubmitRequestButton";
29
- import { YamlFile } from "../models/yamlFile";
30
- export function LinesTablePage({ submitLocation, hideDownloadButtons, hideUploadButtons, emailIsKnown, requestReasonMessage, requestSubmittedMessage, csrfToken, variablesPath, decaysPath, }) {
31
- const navigate = useNavigate();
32
- const location = useLocation();
33
- const metadata = useMetadata();
34
- const { rows, setRows } = useRows();
35
- const request = useRequest();
36
- const { parseProductionFiles } = useProductionConfig();
37
- const [showProdUploadModal, setShowProdUploadModal] = useState(false);
38
- const [prodUploadLoading, setProdUploadLoading] = useState(false);
39
- const [requestSubmitted, setRequestSubmitted] = useState(false);
40
- useEffect(() => {
41
- if (!metadata) {
42
- return;
43
- }
44
- const urlParams = new URLSearchParams(location.search);
45
- const cloneParam = urlParams.get("clone");
46
- if (cloneParam === "1") {
47
- handleClone().catch(console.error);
48
- }
49
- }, [metadata, location.search]);
50
- async function handleClone() {
51
- if (!metadata) {
52
- return;
53
- }
54
- const raw = localStorage.getItem("yamlFilesToClone");
55
- if (!raw) {
56
- return;
57
- }
58
- setProdUploadLoading(true);
59
- setRows([]);
60
- try {
61
- const yamlFiles = JSON.parse(raw);
62
- for (const file of yamlFiles) {
63
- if (file.defaults)
64
- file.name = "info.yaml";
65
- file.blob = new Blob([yaml.dump(file)]);
66
- }
67
- const { rows } = await parseProductionFiles(yamlFiles, metadata);
68
- setRows(rows);
69
- }
70
- finally {
71
- setProdUploadLoading(false);
72
- localStorage.removeItem("yamlFilesToClone");
73
- }
74
- }
75
- const handleSubmitRows = async () => {
76
- setRows((prev) => prev.map((r) => ({ ...r, editTree: true })));
77
- await navigate(variablesPath);
78
- };
79
- const updateProductionName = ({ target }) => {
80
- const safeName = target.value.replaceAll(/[^\w]/g, "");
81
- request.setProductionName(safeName);
82
- };
83
- const updateContactEmails = ({ target }) => {
84
- const emails = target.value.split(/[\s,]+/).filter((s) => s);
85
- request.setContactEmails(emails);
86
- };
87
- const clearAll = () => {
88
- request.setProductionName("");
89
- request.setContactEmails([]);
90
- request.setReasonForRequest("");
91
- setRows([]);
92
- };
93
- const configuredRows = useMemo(() => rows.filter((r) => !!r.dtt), [rows]);
94
- const { isEmptySession, isEmailValid, allRowsHaveDtt, allRowsHavePaths, validDttNames } = useMemo(() => {
95
- const isEmptySession = !request.productionName && request.contactEmails.length === 0 && rows.length === 0;
96
- const isEmailValid = request.contactEmails.length > 0 && request.contactEmails.every(EmailValidator.validate);
97
- const allRowsHaveDtt = configuredRows.length > 0 && configuredRows.length === rows.length;
98
- const allRowsHavePaths = rows.every((r) => r.paths.length > 0);
99
- const names = configuredRows.map((r) => r.dtt.getName().trim() ?? "");
100
- const validDttNames = names.every(Boolean) && new Set(names).size === names.length;
101
- return {
102
- isEmptySession,
103
- isEmailValid,
104
- allRowsHaveDtt,
105
- allRowsHavePaths,
106
- validDttNames,
107
- };
108
- }, [request, rows, configuredRows]);
109
- if (!metadata || !request) {
110
- return _jsx(LoadingIndicator, { height: "70vh" });
111
- }
112
- return (_jsxs("div", { className: "d-flex flex-column gap-3", children: [rows.map((row) => (_jsx(LineTableRow, { row: row, hideDownloadButtons: hideDownloadButtons ?? false, hideUploadButtons: hideUploadButtons ?? false, variablesPath: variablesPath }, row.id))), prodUploadLoading && _jsx(ConfigFilesUploadingAlert, {}), requestSubmitted && (_jsx(Row, { className: "mt-3", children: _jsx(Col, { children: _jsxs(Alert, { variant: "success", dismissible: true, onClose: () => setRequestSubmitted(false), children: [_jsx(Alert.Heading, { children: "Request submitted!" }), requestSubmittedMessage] }) }) })), _jsx(Row, { children: _jsx(Col, { xs: "auto", children: _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Select decays" }), children: _jsxs(Button, { type: "submit", variant: "success", onClick: () => void navigate(decaysPath), className: "align-items-center d-flex gap-1", children: [_jsx(PlusLg, {}), " Select decays"] }) }), !hideUploadButtons && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload Production configuration file" }), children: _jsx(Button, { className: "ms-auto align-items-center d-flex", type: "button", onClick: () => setShowProdUploadModal(true), children: _jsx(Upload, {}) }) })), showProdUploadModal && _jsx(UploadProdConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure all DecayTreeTuples" }), children: _jsxs(Button, { type: "submit", variant: "secondary", onClick: () => {
113
- handleSubmitRows().catch(console.error);
114
- }, disabled: !allRowsHaveDtt, className: "align-items-center d-flex gap-1", children: [_jsx(GearWideConnected, {}), " ", _jsx(Badge, { pill: true, bg: "primary", children: configuredRows.length })] }) }), !hideDownloadButtons && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Download Analysis Production configuration file (info.yaml)" }), children: _jsx(Button, { variant: "primary", type: "button", onClick: () => downloadText(YamlFile.createInfoYaml(rows, metadata)), disabled: !allRowsHaveDtt || !allRowsHavePaths || !isEmailValid, className: "align-items-center d-flex", children: _jsx(Download, {}) }) }))] }) }) }), _jsx(Row, { children: _jsxs(Col, { xs: 4, children: [_jsxs(InputGroup, { hasValidation: true, children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Name of this production. This will become the subdirectory in AnalysisProductions" }), children: _jsx(InputGroup.Text, { children: "Production name" }) }), _jsx(FormControl, { value: request.productionName, onChange: updateProductionName, isValid: !!request.productionName, placeholder: "MyAnalysis" })] }), !emailIsKnown && (_jsxs(InputGroup, { hasValidation: true, className: "mt-1", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Email addresses to notify (comma-separated)" }), children: _jsx(InputGroup.Text, { children: "Email" }) }), _jsx(FormControl, { value: request.contactEmails.join(", "), onChange: updateContactEmails, isInvalid: !isEmailValid && request.contactEmails.length > 0, placeholder: "name@example.com" }), _jsx(FormControl.Feedback, { type: "invalid", children: "Please enter valid email addresses" })] })), _jsxs(Stack, { direction: "horizontal", gap: 1, className: "mt-3", children: [_jsx(SubmitRequestButton, { disabledMessage: !allRowsHaveDtt
115
- ? "Please configure a DecayTreeTuple for each selected decay before submitting"
116
- : !allRowsHavePaths
117
- ? "Please select at least one Bookkeeping path for each decay before submitting"
118
- : !request.productionName
119
- ? "Please enter a production name before submitting"
120
- : !(emailIsKnown || isEmailValid)
121
- ? "Please enter valid email addresses before submitting"
122
- : !validDttNames
123
- ? "Please make sure all DecayTreeTuple names are unique before submitting"
124
- : null, onSubmit: () => setRequestSubmitted(true), submitLocation: submitLocation, hideDownloadButtons: hideDownloadButtons, requestReasonMessage: requestReasonMessage, csrfToken: csrfToken }), _jsx(DeleteButton, { action: clearAll, disabled: isEmptySession, outline: undefined, children: "Clear all" })] })] }) })] }));
125
- }
@@ -1,14 +0,0 @@
1
- import { ReactNode } from "react";
2
- import { BlobFile } from "../models/blobFile";
3
- import { RowData } from "../models/rowData";
4
- import { MetadataContext } from "./MetadataProvider";
5
- export interface ProductionConfigApi {
6
- parseProductionFiles(this: void, files: (BlobFile | File)[], metadata: MetadataContext): Promise<{
7
- rows: RowData[];
8
- emails: string[];
9
- }>;
10
- }
11
- export declare function ProductionConfigProvider({ children }: {
12
- children: ReactNode;
13
- }): import("react/jsx-runtime").JSX.Element;
14
- export declare function useProductionConfig(): ProductionConfigApi;
@@ -1,76 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useCallback, useContext, useMemo } from "react";
3
- import yaml from "js-yaml";
4
- import Dtt from "../models/dtt";
5
- const ProductionConfigContext = createContext(null);
6
- export function ProductionConfigProvider({ children }) {
7
- const parseInput = useCallback((decay) => (input) => {
8
- const [, , stream, , lineName] = input.split("/");
9
- const line = `Stripping${lineName}`;
10
- return {
11
- stream,
12
- line,
13
- versions: decay.lines[`${stream}/${line}`],
14
- };
15
- }, []);
16
- const applyInfoYaml = useCallback((config, rows, emails) => {
17
- const defaults = config.defaults;
18
- if (defaults?.inform?.[0]) {
19
- emails.push(defaults.inform[0]);
20
- }
21
- rows.forEach((row) => {
22
- if (!row.dtt?.config.name)
23
- return;
24
- const dttName = row.dtt.config.name.split("/")[1];
25
- Object.entries(config).forEach(([key, jobConfig]) => {
26
- if (key === "defaults")
27
- return;
28
- const job = jobConfig;
29
- if (!job.options)
30
- return;
31
- if (job.options.some((opt) => opt.split(".")[0] === dttName)) {
32
- row.paths.push(job.input.bk_query);
33
- }
34
- });
35
- });
36
- }, []);
37
- const parseProductionFiles = useCallback(async (files, metadata) => {
38
- const rows = [];
39
- const emails = [];
40
- const sorted = [...files].sort((a, b) => (a.name === "info.yaml" ? 1 : b.name === "info.yaml" ? -1 : 0));
41
- for (let i = 0; i < sorted.length; i++) {
42
- const file = sorted[i];
43
- const blob = file.blob ?? file;
44
- const text = await blob.text();
45
- if (file.name === "info.yaml") {
46
- const config = yaml.load(text);
47
- applyInfoYaml(config, rows, emails);
48
- continue;
49
- }
50
- const config = yaml.load(text);
51
- const decay = Object.values(metadata.decays).find((d) => d.descriptors.template === config.descriptorTemplate);
52
- if (!decay || !config.inputs)
53
- continue;
54
- rows.push({
55
- id: i,
56
- decay,
57
- lines: config.inputs.map(parseInput(decay)),
58
- paths: [],
59
- pathOptions: [],
60
- dtt: new Dtt(config, {}),
61
- });
62
- }
63
- return { rows, emails };
64
- }, [applyInfoYaml, parseInput]);
65
- const api = useMemo(() => ({
66
- parseProductionFiles,
67
- }), [parseProductionFiles]);
68
- return _jsx(ProductionConfigContext.Provider, { value: api, children: children });
69
- }
70
- export function useProductionConfig() {
71
- const ctx = useContext(ProductionConfigContext);
72
- if (!ctx) {
73
- throw new Error("useProductionConfig must be used within ProductionConfigProvider");
74
- }
75
- return ctx;
76
- }