lhcb-ntuple-wizard 2.0.0 → 2.0.2

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 (57) 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.js +13 -16
  8. package/dist/components/NtupleWizard.d.ts +2 -7
  9. package/dist/components/NtupleWizard.js +6 -11
  10. package/dist/components/ProductionNameInput.d.ts +1 -0
  11. package/dist/components/ProductionNameInput.js +7 -0
  12. package/dist/components/RequestButtonGroup.d.ts +7 -0
  13. package/dist/components/RequestButtonGroup.js +29 -0
  14. package/dist/components/RequestEmailInput.d.ts +1 -0
  15. package/dist/components/RequestEmailInput.js +7 -0
  16. package/dist/components/RequestRow.d.ts +7 -0
  17. package/dist/components/RequestRow.js +20 -0
  18. package/dist/components/StrippingLineBadge.js +2 -3
  19. package/dist/components/StrippingLineDropdown.d.ts +1 -0
  20. package/dist/components/StrippingLineDropdown.js +48 -0
  21. package/dist/components/modals/ReasonForRequestModal.d.ts +2 -3
  22. package/dist/components/modals/ReasonForRequestModal.js +3 -10
  23. package/dist/components/modals/UploadDttConfigModal.d.ts +2 -2
  24. package/dist/components/modals/UploadDttConfigModal.js +55 -25
  25. package/dist/constants.d.ts +2 -0
  26. package/dist/constants.js +2 -0
  27. package/dist/models/bkPath.d.ts +9 -9
  28. package/dist/models/bkPath.js +37 -30
  29. package/dist/models/dtt.d.ts +33 -3
  30. package/dist/models/dtt.js +62 -0
  31. package/dist/models/yamlFile.d.ts +0 -1
  32. package/dist/models/yamlFile.js +1 -24
  33. package/dist/pages/DecaySearchPage.js +1 -1
  34. package/dist/pages/DecayTreeConfigPage.js +2 -4
  35. package/dist/pages/RequestPage.d.ts +10 -0
  36. package/dist/pages/RequestPage.js +105 -0
  37. package/dist/providers/RequestProvider.d.ts +14 -1
  38. package/dist/providers/RequestProvider.js +48 -2
  39. package/dist/providers/RowProvider.d.ts +15 -0
  40. package/dist/providers/RowProvider.js +41 -0
  41. package/dist/providers/RowsProvider.d.ts +4 -1
  42. package/dist/providers/RowsProvider.js +9 -2
  43. package/dist/utils/utils.d.ts +15 -0
  44. package/dist/utils/utils.js +535 -0
  45. package/package.json +8 -4
  46. package/dist/components/LineTableRow.d.ts +0 -9
  47. package/dist/components/LineTableRow.js +0 -123
  48. package/dist/components/SubmitRequestButton.d.ts +0 -11
  49. package/dist/components/SubmitRequestButton.js +0 -39
  50. package/dist/components/modals/UploadProdConfigModal.d.ts +0 -5
  51. package/dist/components/modals/UploadProdConfigModal.js +0 -27
  52. package/dist/models/strippingLineOption.d.ts +0 -6
  53. package/dist/models/strippingLineOption.js +0 -1
  54. package/dist/pages/LinesTablePage.d.ts +0 -14
  55. package/dist/pages/LinesTablePage.js +0 -120
  56. package/dist/providers/ProductionConfigProvider.d.ts +0 -14
  57. package/dist/providers/ProductionConfigProvider.js +0 -76
package/README.md CHANGED
@@ -53,11 +53,10 @@ const App = () => {
53
53
  return (
54
54
  <NtupleWizard
55
55
  basePath="/"
56
- decaysPath="/select-decays"
57
- variablesPath="/variables"
58
- submitLocation="" // Leave empty to hide the submit button -> overrides hideDownloadButtons to false
59
56
  contactEmail="" // Leave empty to let the user fill in their email address
60
- hideDownloadButtons={true} // Can be overridden by submitLocation
57
+ submitLocation="" // Some endpoint to submit the request to
58
+ requestReasonMessage="Please provide a reason for your request."
59
+ requestSubmittedMessage={<>Some message to display when the request is submitted</>}
61
60
  />
62
61
  );
63
62
  };
package/dist/App.js CHANGED
@@ -16,7 +16,5 @@ import { config } from "./config";
16
16
  import { NtupleWizard } from "./components/NtupleWizard";
17
17
  import { BrowserRouter } from "react-router-dom";
18
18
  export function App() {
19
- return (_jsxs(_Fragment, { children: [_jsx(Navbar, { collapseOnSelect: true, expand: "lg", bg: "dark", variant: "dark", children: _jsxs(Container, { children: [_jsxs(Navbar.Brand, { href: "/", children: [_jsx("img", { src: "/logo.svg", height: "30", className: "d-inline-block align-top", alt: "LHCb logo" }), " LHCb NTuple Wizard"] }), _jsx(Navbar.Toggle, { "aria-controls": "responsive-navbar-nav" }), _jsx(Navbar.Collapse, { className: "justify-content-end", children: _jsxs(Nav, { children: [_jsxs(NavDropdown, { title: "About", id: "about-dropdown", menuVariant: "dark", children: [_jsxs(NavDropdown.Item, { href: "https://lhcb-dpa.web.cern.ch/lhcb-dpa/wp6/ntupling-wizard.html", target: "_blank", children: [_jsx(House, {}), " Project home"] }), _jsxs(NavDropdown.Item, { href: "https://gitlab.cern.ch/lhcb-dpa/wp6-analysis-preservation-and-open-data/lhcb-ntuple-wizard-frontend", target: "_blank", children: [_jsx(CodeSlash, {}), " Source code"] }), _jsxs(NavDropdown.Item, { href: config.metadata_baseurl, target: "_blank", children: [_jsx(Archive, {}), " Metadata files"] })] }), _jsx(Nav.Link, { href: "https://opendata.cern.ch/", target: "_blank", children: _jsx("img", { src: "/open_data_portal.png", height: "30", alt: "CERN Open Data Portal" }) })] }) })] }) }), _jsx(Container, { className: "mt-4", children: _jsx(BrowserRouter, { children: _jsx(NtupleWizard, { basePath: "/", decaysPath: "/select-decays", variablesPath: "/variables", submitLocation: "" // Leave empty to hide the submit button -> overrides hideDownloadButtons to false
20
- , contactEmail: "" // Leave empty to let the user fill in their email address
21
- , hideDownloadButtons: false, hideUploadButtons: false, requestReasonMessage: "Please provide a reason for your request.", requestSubmittedMessage: _jsx(_Fragment, { children: "Your request has been submitted." }) }) }) })] }));
19
+ return (_jsxs(_Fragment, { children: [_jsx(Navbar, { collapseOnSelect: true, expand: "lg", bg: "dark", variant: "dark", children: _jsxs(Container, { children: [_jsxs(Navbar.Brand, { href: "/", children: [_jsx("img", { src: "/logo.svg", height: "30", className: "d-inline-block align-top", alt: "LHCb logo" }), " LHCb NTuple Wizard"] }), _jsx(Navbar.Toggle, { "aria-controls": "responsive-navbar-nav" }), _jsx(Navbar.Collapse, { className: "justify-content-end", children: _jsxs(Nav, { children: [_jsxs(NavDropdown, { title: "About", id: "about-dropdown", menuVariant: "dark", children: [_jsxs(NavDropdown.Item, { href: "https://lhcb-dpa.web.cern.ch/lhcb-dpa/wp6/ntupling-wizard.html", target: "_blank", children: [_jsx(House, {}), " Project home"] }), _jsxs(NavDropdown.Item, { href: "https://gitlab.cern.ch/lhcb-dpa/wp6-analysis-preservation-and-open-data/lhcb-ntuple-wizard-frontend", target: "_blank", children: [_jsx(CodeSlash, {}), " Source code"] }), _jsxs(NavDropdown.Item, { href: config.metadata_baseurl, target: "_blank", children: [_jsx(Archive, {}), " Metadata files"] })] }), _jsx(Nav.Link, { href: "https://opendata.cern.ch/", target: "_blank", children: _jsx("img", { src: "/open_data_portal.png", height: "30", alt: "CERN Open Data Portal" }) })] }) })] }) }), _jsx(Container, { className: "mt-4", children: _jsx(BrowserRouter, { children: _jsx(NtupleWizard, {}) }) })] }));
22
20
  }
@@ -0,0 +1 @@
1
+ export declare function BookkeepingPathDropdown(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Creatable from "react-select/creatable";
3
+ import { useRow } from "../providers/RowProvider";
4
+ import { useCallback, useMemo } from "react";
5
+ import { useRows } from "../providers/RowsProvider";
6
+ import { useMetadata } from "../providers/MetadataProvider";
7
+ import { DatasetInfo } from "./DatasetInfo";
8
+ export function BookkeepingPathDropdown() {
9
+ const metadata = useMetadata();
10
+ const { row, validateBkPath, updateBkPaths } = useRow();
11
+ const { updateRow } = useRows();
12
+ if (!metadata) {
13
+ return null;
14
+ }
15
+ const getOptionFromPath = useCallback((path) => ({
16
+ value: path,
17
+ label: _jsx(DatasetInfo, { pathString: path }),
18
+ }), []);
19
+ const defaultPaths = useMemo(() => {
20
+ const defaults = metadata.dataset.filter((path) => validateBkPath(path)).map(getOptionFromPath);
21
+ return [...defaults, ...row.pathOptions.map(getOptionFromPath)];
22
+ }, [metadata.dataset, row.pathOptions, validateBkPath, getOptionFromPath]);
23
+ const handleCreatePath = useCallback((path) => {
24
+ if (!validateBkPath(path)) {
25
+ alert(`The path '${path}' is not valid for the selected stripping line.`);
26
+ return;
27
+ }
28
+ updateRow(row.id, (r) => ({
29
+ ...r,
30
+ paths: [...r.paths, path],
31
+ pathOptions: [...r.pathOptions, path],
32
+ }));
33
+ }, [row.id, validateBkPath, updateRow]);
34
+ return (_jsx(Creatable, { isMulti: true, options: defaultPaths, value: row.paths.map(getOptionFromPath), isDisabled: !row.lines.length, placeholder: "Bookkeeping path", onChange: (v) => updateBkPaths(v.map((i) => i.value)), onCreateOption: handleCreatePath, closeMenuOnSelect: false }));
35
+ }
@@ -1,5 +1,5 @@
1
1
  interface Props {
2
2
  pathString: string;
3
3
  }
4
- export declare function DatasetInfo({ pathString }: Props): import("react/jsx-runtime").JSX.Element;
4
+ export declare function DatasetInfo({ pathString }: Props): import("react/jsx-runtime").JSX.Element | null;
5
5
  export {};
@@ -14,8 +14,14 @@ import { YearBadge } from "./YearBadge.js";
14
14
  import { PolarityBadge } from "./PolarityBadge.js";
15
15
  import { StrippingLineBadge } from "./StrippingLineBadge";
16
16
  import { DatasetEventTypeBadge } from "./DatasetEventTypeBadge";
17
- import BkPath from "../models/bkPath.js";
17
+ import { BkPath } from "../models/bkPath.js";
18
18
  export function DatasetInfo({ pathString }) {
19
- const path = new BkPath(pathString);
20
- return (_jsxs("span", { children: [path.getFilename(), _jsx("br", {}), _jsxs(Stack, { direction: "horizontal", style: { flexWrap: "wrap" }, gap: 1, children: [_jsx(DatasetEventTypeBadge, { eventType: path.getEventType() }), _jsx(YearBadge, { year: path.getYear() }), _jsx(PolarityBadge, { polarity: path.getPolarity() || "?" }), _jsx(StrippingLineBadge, { version: path.getStrippingVersion() })] })] }));
19
+ try {
20
+ const path = new BkPath(pathString);
21
+ return (_jsxs("span", { children: [path.fileName, _jsx("br", {}), _jsxs(Stack, { direction: "horizontal", style: { flexWrap: "wrap" }, gap: 1, children: [_jsx(DatasetEventTypeBadge, { eventType: path.eventType }), _jsx(YearBadge, { year: path.year }), _jsx(PolarityBadge, { polarity: path.polarity || "?" }), _jsx(StrippingLineBadge, { version: path.strippingVersion })] })] }));
22
+ }
23
+ catch (e) {
24
+ console.error("Error parsing bookkeeping path:", e);
25
+ return null;
26
+ }
21
27
  }
@@ -9,10 +9,10 @@ import { useRows } from "../providers/RowsProvider";
9
9
  import { useNavigate } from "react-router-dom";
10
10
  import { useMetadata } from "../providers/MetadataProvider";
11
11
  import { downloadText } from "../utils/utils";
12
- import { UploadDttConfigModal } from "./modals/UploadDttConfigModal";
13
12
  import { YamlFile } from "../models/yamlFile";
13
+ import { UploadDttConfigModal } from "./modals/UploadDttConfigModal";
14
14
  export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, variablesPath }) {
15
- const { rows, setRows } = useRows();
15
+ const { rows, updateRow } = useRows();
16
16
  const navigate = useNavigate();
17
17
  const metadata = useMetadata();
18
18
  const [showUploadModal, setShowUploadModal] = useState(false);
@@ -38,26 +38,25 @@ export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, vari
38
38
  }
39
39
  const handleCreateDTT = () => {
40
40
  const dtt = Dtt.create(row.decay.descriptors.template, row.decay.descriptors.mapped_list.flatMap((item) => (Array.isArray(item) ? item : [item])), [], "", metadata.tupleTools.tupleTools);
41
- setRows((prev) => {
42
- return prev.map((r) => (r.id === row.id ? { ...r, dtt: dtt.withInputsFromLines(row.lines) } : r));
43
- });
41
+ updateRow(row.id, (r) => ({
42
+ ...r,
43
+ dtt: dtt.withInputsFromLines(row.lines),
44
+ }));
44
45
  setAutoFocus(true);
45
46
  };
46
- const handleConfigureDtt = (id) => {
47
- setRows(rows.map((row) => ({ ...row, editTree: row.id === id })));
47
+ const handleConfigureDtt = () => {
48
+ updateRow(row.id, (r) => ({ ...r, editTree: true }));
48
49
  void navigate(variablesPath);
49
50
  };
50
- const handleDeleteDtt = (id) => {
51
- setRows((prev) => prev.map((r) => (r.id === id ? { ...r, dtt: null } : r)));
51
+ const handleDeleteDtt = () => {
52
+ updateRow(row.id, (r) => ({ ...r, dtt: null }));
52
53
  setDttName("");
53
54
  };
54
55
  return (_jsxs(Card, { children: [_jsxs(Card.Header, { className: "d-flex justify-content-between align-items-start gap-2", children: [row.dtt && (_jsxs(InputGroup, { hasValidation: true, children: [_jsx(FormControl, { autoFocus: autoFocus, placeholder: "DecayTree", value: dttName, onBlur: () => {
55
56
  if (!validateName()) {
56
57
  return;
57
58
  }
58
- setRows((prev) => {
59
- return prev.map((r) => r.id === row.id ? { ...r, dtt: r.dtt.withName(dttName) } : r);
60
- });
59
+ updateRow(row.id, (r) => ({ ...r, dtt: r.dtt.withName(dttName) }));
61
60
  }, onChange: (event) => {
62
61
  setDttName(event.target.value);
63
62
  setDttNameValid(true);
@@ -65,9 +64,7 @@ export function DttNameInput({ row, hideDownloadButtons, hideUploadButtons, vari
65
64
  ? "Name cannot be empty"
66
65
  : !allNameCharsValid(dttName.trim())
67
66
  ? "Name contains invalid characters"
68
- : "Must give a unique name" })] })), _jsx(ButtonGroup, { children: row.dtt ? (_jsxs(_Fragment, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure this DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "secondary", onClick: () => {
69
- handleConfigureDtt(row.id);
70
- }, disabled: !dttNameValid, className: "align-items-center d-flex", children: _jsx(GearWideConnected, {}) }) }), !hideDownloadButtons && (_jsx(_Fragment, { children: _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Download DecayTreeTuple YAML configuration file" }), children: _jsx(Button, { className: "align-items-center d-flex ms-auto", type: "button", onClick: () => {
67
+ : "Name is already taken" })] })), _jsx(ButtonGroup, { children: row.dtt ? (_jsxs(_Fragment, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure this DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "secondary", onClick: handleConfigureDtt, disabled: !dttNameValid, className: "align-items-center d-flex", children: _jsx(GearWideConnected, {}) }) }), !hideDownloadButtons && (_jsx(_Fragment, { children: _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Download DecayTreeTuple YAML configuration file" }), children: _jsx(Button, { className: "align-items-center d-flex ms-auto", type: "button", onClick: () => {
71
68
  downloadText(YamlFile.fromDtt(row.dtt));
72
- }, disabled: !dttNameValid, children: _jsx(Download, {}) }) }) })), _jsx(DeleteButton, { action: () => handleDeleteDtt(row.id) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center gap-2", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Please add a DecayTreeTuple in order to complete the production configuration" }), children: _jsx(ExclamationCircle, { width: 20, height: 20, className: "text-danger me-2" }) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Add a DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "success", onClick: handleCreateDTT, disabled: !metadata, children: _jsx(PlusLg, {}) }) }), !hideUploadButtons && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload DecayTreeTuple configuration file" }), children: _jsx(Button, { className: "ms-auto", type: "button", onClick: () => setShowUploadModal(true), children: _jsx(Upload, {}) }) }))] })] }), showUploadModal && (_jsx(UploadDttConfigModal, { row: row, onClose: () => setShowUploadModal(false) }))] })) })] }), _jsx(Card.Body, { children: _jsx(DecayLatex, { decay: row.decay }) })] }));
69
+ }, disabled: !dttNameValid, children: _jsx(Download, {}) }) }) })), _jsx(DeleteButton, { action: handleDeleteDtt })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center gap-2", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Please add a DecayTreeTuple in order to complete the production configuration" }), children: _jsx(ExclamationCircle, { width: 20, height: 20, className: "text-danger me-2" }) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Add a DecayTreeTuple" }), children: _jsx(Button, { type: "submit", variant: "success", onClick: handleCreateDTT, disabled: !metadata, children: _jsx(PlusLg, {}) }) }), !hideUploadButtons && (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Upload DecayTreeTuple configuration file" }), children: _jsx(Button, { className: "ms-auto", type: "button", onClick: () => setShowUploadModal(true), children: _jsx(Upload, {}) }) }))] })] }), showUploadModal && (_jsx(UploadDttConfigModal, { currentRow: row, onClose: () => setShowUploadModal(false) }))] })) })] }), _jsx(Card.Body, { children: _jsx(DecayLatex, { decay: row.decay }) })] }));
73
70
  }
@@ -11,16 +11,11 @@
11
11
  import "bootstrap/dist/css/bootstrap.css";
12
12
  import { ReactNode } from "react";
13
13
  interface Props {
14
- basePath: string;
15
- decaysPath: string;
16
- variablesPath: string;
14
+ basePath?: string;
17
15
  contactEmail?: string;
18
16
  submitLocation?: string;
19
- hideDownloadButtons?: boolean;
20
- hideUploadButtons?: boolean;
21
17
  requestReasonMessage?: string;
22
18
  requestSubmittedMessage?: ReactNode;
23
- csrfToken?: string;
24
19
  }
25
- export declare function NtupleWizard({ basePath, decaysPath, variablesPath, submitLocation, hideDownloadButtons, hideUploadButtons, requestReasonMessage, requestSubmittedMessage, csrfToken, ...props }: Props): import("react/jsx-runtime").JSX.Element;
20
+ export declare function NtupleWizard({ basePath, contactEmail, submitLocation, requestReasonMessage, requestSubmittedMessage, }: Props): import("react/jsx-runtime").JSX.Element;
26
21
  export {};
@@ -14,20 +14,15 @@ import { useLocation } from "react-router-dom";
14
14
  import { MetadataProvider } from "../providers/MetadataProvider.js";
15
15
  import { DecaySearchPage } from "../pages/DecaySearchPage";
16
16
  import { DecayTreeConfigPage } from "../pages/DecayTreeConfigPage";
17
- import { LinesTablePage } from "../pages/LinesTablePage";
17
+ import { RequestPage } from "../pages/RequestPage";
18
18
  import { MathJaxContext } from "better-react-mathjax";
19
19
  import { RowsProvider } from "../providers/RowsProvider.js";
20
20
  import { RequestProvider } from "../providers/RequestProvider";
21
- import { ProductionConfigProvider } from "../providers/ProductionConfigProvider";
22
- export function NtupleWizard({ basePath, decaysPath, variablesPath, submitLocation, hideDownloadButtons, hideUploadButtons, requestReasonMessage, requestSubmittedMessage, csrfToken = "", ...props }) {
21
+ import { SELECT_DECAYS_PATH, VARIABLES_PATH } from "../constants";
22
+ export function NtupleWizard({ basePath = "", contactEmail, submitLocation, requestReasonMessage, requestSubmittedMessage, }) {
23
23
  const { pathname } = useLocation();
24
- let emailIsKnown = false;
25
- if (props.contactEmail) {
26
- localStorage.setItem("email", props.contactEmail);
27
- emailIsKnown = true;
24
+ if (contactEmail) {
25
+ localStorage.setItem("email", contactEmail);
28
26
  }
29
- if (!submitLocation) {
30
- hideDownloadButtons = false;
31
- }
32
- return (_jsx(MetadataProvider, { children: _jsx(ProductionConfigProvider, { children: _jsx(RowsProvider, { children: _jsx(RequestProvider, { children: _jsx(MathJaxContext, { children: pathname.endsWith("/select-decays") ? (_jsx(DecaySearchPage, { basePath: basePath })) : pathname.endsWith("/variables") ? (_jsx(DecayTreeConfigPage, { basePath: basePath })) : (_jsx(LinesTablePage, { decaysPath: decaysPath, variablesPath: variablesPath, submitLocation: submitLocation, hideDownloadButtons: hideDownloadButtons, hideUploadButtons: hideUploadButtons, emailIsKnown: emailIsKnown, requestReasonMessage: requestReasonMessage, requestSubmittedMessage: requestSubmittedMessage, csrfToken: csrfToken })) }) }) }) }) }));
27
+ return (_jsx(MetadataProvider, { children: _jsx(RowsProvider, { children: _jsx(RequestProvider, { emailIsKnown: !!contactEmail, children: _jsx(MathJaxContext, { children: pathname.endsWith(SELECT_DECAYS_PATH) ? (_jsx(DecaySearchPage, { basePath: basePath })) : pathname.endsWith(VARIABLES_PATH) ? (_jsx(DecayTreeConfigPage, { basePath: basePath })) : (_jsx(RequestPage, { basePath: basePath, submitLocation: submitLocation, requestReasonMessage: requestReasonMessage, requestSubmittedMessage: requestSubmittedMessage })) }) }) }) }));
33
28
  }
@@ -0,0 +1 @@
1
+ export declare function ProductionNameInput(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FormControl, InputGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
3
+ import { useRequest } from "../providers/RequestProvider";
4
+ export function ProductionNameInput() {
5
+ const { productionName, setProductionName, showErrors } = useRequest();
6
+ return (_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: productionName, onChange: (event) => setProductionName(event.target.value.replaceAll(/[^\w]/g, "")), isInvalid: showErrors && !productionName, placeholder: "MyAnalysis" }), _jsx(FormControl.Feedback, { type: "invalid", children: "Please enter a production name" })] }));
7
+ }
@@ -0,0 +1,7 @@
1
+ import { NtupleWizardVariant } from "../pages/RequestPage";
2
+ interface Props {
3
+ variant: NtupleWizardVariant;
4
+ basePath: string;
5
+ }
6
+ export declare function RequestButtonGroup({ variant, basePath }: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Badge, Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
3
+ import { SELECT_DECAYS_PATH, VARIABLES_PATH } from "../constants";
4
+ import { useNavigate } from "react-router-dom";
5
+ import { Download, GearWideConnected, PlusLg, Upload } from "react-bootstrap-icons";
6
+ import { UploadDttConfigModal } from "./modals/UploadDttConfigModal";
7
+ import { downloadText } from "../utils/utils";
8
+ import { YamlFile } from "../models/yamlFile";
9
+ import { useRows } from "../providers/RowsProvider";
10
+ import { useMetadata } from "../providers/MetadataProvider";
11
+ import { useRequest } from "../providers/RequestProvider";
12
+ import { useState } from "react";
13
+ export function RequestButtonGroup({ variant, basePath }) {
14
+ const metadata = useMetadata();
15
+ const { validation } = useRequest();
16
+ const { rows, configuredRows, setRows } = useRows();
17
+ const navigate = useNavigate();
18
+ const [showProdUploadModal, setShowProdUploadModal] = useState(false);
19
+ const handleSubmitRows = async () => {
20
+ setRows((prev) => prev.map((r) => ({ ...r, editTree: true })));
21
+ await navigate(`${basePath}${VARIABLES_PATH}`);
22
+ };
23
+ if (!metadata) {
24
+ return null;
25
+ }
26
+ return (_jsxs(_Fragment, { children: [showProdUploadModal && _jsx(UploadDttConfigModal, { onClose: () => setShowProdUploadModal(false) }), _jsxs(ButtonGroup, { children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Select decays" }), children: _jsxs(Button, { type: "submit", variant: "success", onClick: () => void navigate(`${basePath}${SELECT_DECAYS_PATH}`), className: "align-items-center d-flex gap-1", children: [_jsx(PlusLg, {}), " Select decays"] }) }), variant === "standalone" && (_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, {}) }) })), _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Configure all DecayTreeTuples" }), children: _jsxs(Button, { type: "submit", variant: "secondary", onClick: () => {
27
+ handleSubmitRows().catch(console.error);
28
+ }, disabled: !validation.allRowsHaveDtt, className: "align-items-center d-flex gap-1", children: [_jsx(GearWideConnected, {}), " ", _jsx(Badge, { pill: true, bg: "primary", children: configuredRows.length })] }) }), variant === "standalone" && (_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: !validation.allRowsHaveDtt || !validation.allRowsHavePaths || !validation.isEmailValid, className: "align-items-center d-flex", children: _jsx(Download, {}) }) }))] })] }));
29
+ }
@@ -0,0 +1 @@
1
+ export declare function RequestEmailInput(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FormControl, InputGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
3
+ import { useRequest } from "../providers/RequestProvider";
4
+ export function RequestEmailInput() {
5
+ const { contactEmails, setContactEmails, showErrors, validation } = useRequest();
6
+ return (_jsxs(InputGroup, { hasValidation: true, className: "mt-1", children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Email address(es) to notify (comma-separated)" }), children: _jsx(InputGroup.Text, { children: "Email(s)" }) }), _jsx(FormControl, { value: contactEmails.join(", "), onChange: (event) => setContactEmails(event.target.value.split(/[\s,]+/).filter(Boolean)), isInvalid: showErrors && !validation.isEmailValid, placeholder: "name@example.com" }), _jsx(FormControl.Feedback, { type: "invalid", children: "Please enter valid email addresses" })] }));
7
+ }
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ hideDownloadButtons: boolean;
3
+ hideUploadButtons: boolean;
4
+ variablesPath: string;
5
+ }
6
+ export declare function RequestRow({ hideDownloadButtons, hideUploadButtons, variablesPath }: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Col, OverlayTrigger, Popover, Row } from "react-bootstrap";
3
+ import { InfoCircle } from "react-bootstrap-icons";
4
+ import { StrippingLineInfo } from "./StrippingLineInfo";
5
+ import { DeleteButton } from "./DeleteButton";
6
+ import { DttNameInput } from "./DttNameInput";
7
+ import { useMetadata } from "../providers/MetadataProvider";
8
+ import { useRows } from "../providers/RowsProvider";
9
+ import { BookkeepingPathDropdown } from "./BookkeepingPathDropdown";
10
+ import { StrippingLineDropdown } from "./StrippingLineDropdown";
11
+ import { useRow } from "../providers/RowProvider";
12
+ export function RequestRow({ hideDownloadButtons, hideUploadButtons, variablesPath }) {
13
+ const metadata = useMetadata();
14
+ const { row } = useRow();
15
+ const { removeRow } = useRows();
16
+ if (!metadata) {
17
+ return null;
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) }) })] }));
20
+ }
@@ -9,7 +9,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
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 { Badge, OverlayTrigger, Spinner, Tooltip } from "react-bootstrap";
12
+ import { Badge, OverlayTrigger, Tooltip } from "react-bootstrap";
13
13
  import { useMetadata } from "../providers/MetadataProvider.js";
14
14
  export function StrippingLineBadge({ version, line, stream, showLink }) {
15
15
  const metadata = useMetadata();
@@ -17,7 +17,6 @@ export function StrippingLineBadge({ version, line, stream, showLink }) {
17
17
  const url = line && metadata && stream
18
18
  ? metadata.stripping[version][line].url[stream]
19
19
  : `http://lhcbdoc.web.cern.ch/lhcbdoc/stripping/config/stripping${version}/index.html`;
20
- const { description } = metadata ? metadata.strippingHints[version] : { description: _jsx(Spinner, { animation: "border" }) };
21
20
  const badgeProps = showLink
22
21
  ? {
23
22
  as: "a",
@@ -25,5 +24,5 @@ export function StrippingLineBadge({ version, line, stream, showLink }) {
25
24
  target: "_blank",
26
25
  }
27
26
  : {};
28
- return (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: description }), children: _jsx(Badge, { pill: true, bg: "info", ...badgeProps, children: "S" + version }) }));
27
+ return (_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: metadata ? metadata.strippingHints[version].description : "..." }), children: _jsx(Badge, { pill: true, bg: "info", ...badgeProps, children: "S" + version }) }));
29
28
  }
@@ -0,0 +1 @@
1
+ export declare function StrippingLineDropdown(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Select from "react-select";
3
+ import { useRow } from "../providers/RowProvider";
4
+ import { StrippingLineInfo } from "./StrippingLineInfo";
5
+ import { useRows } from "../providers/RowsProvider";
6
+ import { useCallback, useMemo } from "react";
7
+ export function StrippingLineDropdown() {
8
+ const { row, updateBkPaths } = useRow();
9
+ const { updateRow } = useRows();
10
+ const handleStrippingLineChanged = useCallback((lineOption) => {
11
+ const lines = [
12
+ {
13
+ stream: lineOption.group,
14
+ line: lineOption.value,
15
+ versions: row.decay.lines[`${lineOption.group}/${lineOption.value}`],
16
+ },
17
+ ];
18
+ updateBkPaths(row.paths, lines);
19
+ updateRow(row.id, (r) => ({
20
+ ...r,
21
+ lines,
22
+ dtt: r.dtt ? r.dtt.withInputsFromLines(lines) : null,
23
+ }));
24
+ }, [row, updateBkPaths, updateRow]);
25
+ const strippingLineOptions = useMemo(() => {
26
+ const grouped = {};
27
+ Object.entries(row.decay.lines).forEach(([streamLine, versions]) => {
28
+ const [stream, line] = streamLine.split("/");
29
+ grouped[stream] ??= [];
30
+ if (grouped[stream].some((o) => o.value === line))
31
+ return;
32
+ grouped[stream].push({
33
+ value: line,
34
+ group: stream,
35
+ label: _jsx(StrippingLineInfo, { line: line, stream: stream, versions: versions }),
36
+ });
37
+ });
38
+ return Object.entries(grouped).map(([label, options]) => ({
39
+ label,
40
+ options,
41
+ }));
42
+ }, [row.decay.lines]);
43
+ return (_jsx(Select, { options: strippingLineOptions, value: row.lines.map((l) => ({
44
+ value: l.line,
45
+ group: l.stream,
46
+ label: _jsx(StrippingLineInfo, { line: l.line, stream: l.stream, versions: l.versions, showLink: false }),
47
+ })), placeholder: "Stripping line", onChange: (v) => handleStrippingLineChanged(v), menuPortalTarget: document.body, menuPosition: "fixed", styles: { menuPortal: (b) => ({ ...b, zIndex: 9999 }) } }));
48
+ }
@@ -1,8 +1,7 @@
1
1
  interface Props {
2
2
  submitLocation: string;
3
3
  onClose: (submitted: boolean) => void;
4
- requestReasonMessage?: string;
5
- csrfToken?: string;
4
+ requestReasonMessage: string;
6
5
  }
7
- export declare function ReasonForRequestModal({ submitLocation, onClose, requestReasonMessage, csrfToken }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export declare function ReasonForRequestModal({ submitLocation, onClose, requestReasonMessage }: Props): import("react/jsx-runtime").JSX.Element;
8
7
  export {};
@@ -1,9 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Alert, Button, FormControl, InputGroup, Modal, OverlayTrigger, Spinner, Tooltip } from "react-bootstrap";
3
3
  import { useRequest } from "../../providers/RequestProvider";
4
4
  import { useState } from "react";
5
5
  import { useRows } from "../../providers/RowsProvider";
6
- export function ReasonForRequestModal({ submitLocation, onClose, requestReasonMessage, csrfToken }) {
6
+ export function ReasonForRequestModal({ submitLocation, onClose, requestReasonMessage }) {
7
7
  const { generateAllFiles } = useRows();
8
8
  const { productionName, reasonForRequest, setReasonForRequest } = useRequest();
9
9
  const [requestLoading, setRequestLoading] = useState(false);
@@ -19,17 +19,10 @@ export function ReasonForRequestModal({ submitLocation, onClose, requestReasonMe
19
19
  formData.append("email", localStorage.getItem("email") || "");
20
20
  formData.append("name", productionName);
21
21
  formData.append("reasonForRequest", reasonForRequest);
22
- let headers = {};
23
- if (csrfToken) {
24
- headers = {
25
- "X-Csrf-Token": csrfToken,
26
- };
27
- }
28
22
  fetch(url, {
29
23
  method: "POST",
30
24
  body: formData,
31
25
  credentials: "include",
32
- headers: headers,
33
26
  })
34
27
  .then((response) => {
35
28
  if (!response.ok) {
@@ -45,5 +38,5 @@ export function ReasonForRequestModal({ submitLocation, onClose, requestReasonMe
45
38
  setRequestLoading(false);
46
39
  });
47
40
  };
48
- return (_jsxs(Modal, { show: true, onHide: () => onClose(false), size: "lg", children: [_jsx(Modal.Header, { closeButton: true, children: _jsx(Modal.Title, { children: "Reason for request" }) }), _jsxs(Modal.Body, { children: [_jsx(Alert, { children: requestReasonMessage }), requestError && _jsx(Alert, { variant: "danger", children: requestError }), _jsxs(InputGroup, { hasValidation: true, children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Reason for requesting the chosen ntuples" }), children: _jsx(InputGroup.Text, { children: "Reason for request" }) }), _jsx(FormControl, { as: "textarea", rows: 3, value: reasonForRequest, onChange: (event) => setReasonForRequest(event.target.value), isValid: !!reasonForRequest, placeholder: "I need these ntuples for..." })] })] }), _jsx(Modal.Footer, { children: _jsx(Button, { variant: "primary", onClick: () => submitRequest(submitLocation), children: requestLoading ? _jsx(Spinner, { animation: "border" }) : "Submit" }) })] }));
41
+ return (_jsxs(Modal, { show: true, onHide: () => onClose(false), size: "lg", children: [_jsx(Modal.Header, { closeButton: true, children: _jsx(Modal.Title, { children: "Reason for request" }) }), _jsxs(Modal.Body, { children: [_jsx(Alert, { children: requestReasonMessage }), requestError && _jsx(Alert, { variant: "danger", children: requestError }), _jsxs(InputGroup, { hasValidation: true, children: [_jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Reason for requesting the chosen ntuples" }), children: _jsx(InputGroup.Text, { children: "Reason for request" }) }), _jsx(FormControl, { as: "textarea", rows: 3, value: reasonForRequest, onChange: (event) => setReasonForRequest(event.target.value), isValid: !!reasonForRequest, placeholder: "I need these ntuples for..." })] })] }), _jsx(Modal.Footer, { children: _jsx(Button, { variant: "primary", disabled: !reasonForRequest || requestLoading, onClick: () => submitRequest(submitLocation), children: requestLoading ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { animation: "border", size: "sm" }), " Submitting..."] })) : ("Submit request") }) })] }));
49
42
  }
@@ -1,7 +1,7 @@
1
1
  import { RowData } from "../../models/rowData";
2
2
  interface Props {
3
- row: RowData;
4
3
  onClose: () => void;
4
+ currentRow?: RowData;
5
5
  }
6
- export declare function UploadDttConfigModal({ row, onClose }: Props): import("react/jsx-runtime").JSX.Element;
6
+ export declare function UploadDttConfigModal({ onClose, currentRow }: Props): import("react/jsx-runtime").JSX.Element | null;
7
7
  export {};
@@ -1,35 +1,65 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Form, Modal } from "react-bootstrap";
3
- import yaml from "js-yaml";
4
- import Dtt from "../../models/dtt";
2
+ import { Button, Form, ListGroup, Modal } from "react-bootstrap";
3
+ import { useState } from "react";
4
+ import { useMetadata } from "../../providers/MetadataProvider";
5
5
  import { useRows } from "../../providers/RowsProvider";
6
- export function UploadDttConfigModal({ row, onClose }) {
7
- const { setRows } = useRows();
8
- const uploadConfig = (files) => {
9
- const reader = new FileReader();
10
- reader.onload = (e) => {
11
- if (!e.target) {
6
+ import { useRequest } from "../../providers/RequestProvider";
7
+ import { parseProductionFiles } from "../../utils/utils";
8
+ export function UploadDttConfigModal({ onClose, currentRow }) {
9
+ const metadata = useMetadata();
10
+ const { setRows, updateRow } = useRows();
11
+ const { setContactEmails } = useRequest();
12
+ const [files, setFiles] = useState([]);
13
+ const [loading, setLoading] = useState(false);
14
+ async function handleImport(metadata) {
15
+ if (files.length === 0) {
16
+ return;
17
+ }
18
+ setLoading(true);
19
+ const { rows, emails, errors, warnings } = await parseProductionFiles(Array.from(files), metadata);
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}`);
26
+ return;
27
+ }
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?`)) {
12
33
  return;
13
34
  }
14
- const text = e.target.result;
15
- const config = yaml.load(text);
16
- if (config.descriptorTemplate === row.decay.descriptors.template) {
17
- const dtt = new Dtt(config, {});
18
- setRows((prev) => {
19
- return prev.map((r) => (r.id === row.id ? { ...r, dtt: dtt.withInputsFromLines(row.lines) } : r));
20
- });
35
+ }
36
+ if (currentRow) {
37
+ const importedRow = rows[0];
38
+ const importedConfig = importedRow.dtt.config;
39
+ if (importedConfig.descriptorTemplate === currentRow.decay.descriptors.template) {
40
+ updateRow(currentRow.id, (r) => ({
41
+ ...r,
42
+ dtt: importedRow.dtt.withInputsFromLines(currentRow.lines),
43
+ }));
21
44
  onClose();
22
45
  }
23
46
  else {
24
47
  alert("Make sure to upload a previously downloaded configuration file for the same decay.");
25
48
  }
26
- };
27
- reader.readAsText(files[0]);
28
- };
29
- 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: "formFile1", className: "mb-3", children: _jsx(Form.Control, { type: "file", onChange: (event) => {
30
- if (!event.target.files)
31
- return;
32
- event.preventDefault();
33
- uploadConfig(event.target.files);
34
- } }) }) }), _jsx(Modal.Footer, { children: _jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" }) })] }));
49
+ }
50
+ else {
51
+ setRows(rows);
52
+ setContactEmails(emails);
53
+ onClose();
54
+ }
55
+ }
56
+ if (!metadata) {
57
+ return null;
58
+ }
59
+ return (_jsxs(Modal, { show: true, animation: false, children: [_jsx(Modal.Header, { children: _jsx(Modal.Title, { children: " Upload configuration file " }) }), _jsxs(Modal.Body, { children: [_jsx(Form.Group, { controlId: "formFile2", className: "mb-3", children: _jsx(Form.Control, { type: "file", multiple: !currentRow, accept: ".yaml", onChange: (event) => {
60
+ const files = event.target.files;
61
+ if (files) {
62
+ setFiles(Array.from(files));
63
+ }
64
+ } }) }), files.length > 0 && (_jsx(ListGroup, { children: files.map((file, i) => (_jsx(ListGroup.Item, { children: file.name }, i))) }))] }), _jsxs(Modal.Footer, { children: [_jsx(Button, { variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { disabled: loading, variant: "primary", onClick: () => void handleImport(metadata), children: loading ? "Importing..." : "Import" })] })] }));
35
65
  }
@@ -0,0 +1,2 @@
1
+ export declare const SELECT_DECAYS_PATH = "/select-decays";
2
+ export declare const VARIABLES_PATH = "/variables";
@@ -0,0 +1,2 @@
1
+ export const SELECT_DECAYS_PATH = "/select-decays";
2
+ export const VARIABLES_PATH = "/variables";
@@ -1,11 +1,11 @@
1
- export default class BkPath {
2
- path: string;
3
- isMC: boolean;
1
+ export declare class BkPath {
2
+ year: string;
3
+ strippingVersion: string;
4
+ polarity: string | null;
5
+ eventType: string;
6
+ fileName: string;
4
7
  constructor(path: string);
5
- getStrippingVersion(): string;
6
- getYear(): string;
7
- getPolarity(): string | null;
8
- getFilename(): string;
9
- getEventType(): string;
10
- isValid(): boolean;
8
+ private static extractMcParts;
9
+ private static extractLhcbParts;
10
+ private static isValid;
11
11
  }