lhcb-ntuple-wizard-test 1.3.3 → 2.0.1

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 (167) hide show
  1. package/README.md +1 -2
  2. package/dist/App.d.ts +12 -0
  3. package/dist/App.js +22 -0
  4. package/dist/components/ConfigFilesUploadingAlert.d.ts +1 -0
  5. package/dist/components/ConfigFilesUploadingAlert.js +5 -0
  6. package/dist/components/DatasetEventTypeBadge.d.ts +5 -0
  7. package/dist/components/DatasetEventTypeBadge.js +23 -0
  8. package/dist/components/DatasetInfo.d.ts +5 -0
  9. package/dist/components/DatasetInfo.js +21 -0
  10. package/dist/components/DecayLatex.d.ts +7 -0
  11. package/dist/components/DecayLatex.js +103 -0
  12. package/dist/components/DecayList.d.ts +8 -0
  13. package/dist/components/DecayList.js +37 -0
  14. package/dist/components/DecayListItem.d.ts +11 -0
  15. package/dist/components/DecayListItem.js +23 -0
  16. package/dist/components/DecayTagBadge.d.ts +5 -0
  17. package/dist/components/DecayTagBadge.js +21 -0
  18. package/dist/components/DecayTreeCard.d.ts +7 -0
  19. package/dist/components/DecayTreeCard.js +43 -0
  20. package/dist/components/DecayTreeCardHeader.d.ts +7 -0
  21. package/dist/components/DecayTreeCardHeader.js +5 -0
  22. package/dist/components/DecayTreeGraph.d.ts +9 -0
  23. package/dist/components/DecayTreeGraph.js +89 -0
  24. package/dist/components/DeleteButton.d.ts +9 -0
  25. package/dist/components/DeleteButton.js +16 -53
  26. package/dist/components/DttNameInput.d.ts +9 -0
  27. package/dist/components/DttNameInput.js +73 -0
  28. package/dist/components/LineTableRow.d.ts +9 -0
  29. package/dist/components/LineTableRow.js +123 -0
  30. package/dist/components/LoadingIndicator.d.ts +6 -0
  31. package/dist/components/LoadingIndicator.js +5 -0
  32. package/dist/components/NtupleWizard.d.ts +26 -0
  33. package/dist/components/NtupleWizard.js +28 -137
  34. package/dist/components/NumStrippingLinesBadge.d.ts +8 -0
  35. package/dist/components/NumStrippingLinesBadge.js +10 -0
  36. package/dist/components/ParticleDropdown.d.ts +8 -0
  37. package/dist/components/ParticleDropdown.js +33 -0
  38. package/dist/components/ParticleTagBadge.d.ts +9 -0
  39. package/dist/components/ParticleTagBadge.js +22 -0
  40. package/dist/components/ParticleTagFilters.d.ts +6 -0
  41. package/dist/components/ParticleTagFilters.js +51 -0
  42. package/dist/components/PolarityBadge.d.ts +5 -0
  43. package/dist/components/PolarityBadge.js +13 -33
  44. package/dist/components/StrippingLineBadge.d.ts +7 -0
  45. package/dist/components/StrippingLineBadge.js +29 -0
  46. package/dist/components/StrippingLineInfo.d.ts +8 -0
  47. package/dist/components/StrippingLineInfo.js +17 -0
  48. package/dist/components/SubmitRequestButton.d.ts +11 -0
  49. package/dist/components/SubmitRequestButton.js +39 -0
  50. package/dist/components/TagDropdown.d.ts +12 -0
  51. package/dist/components/TagDropdown.js +31 -0
  52. package/dist/components/TupleToolDocsAccordion.d.ts +5 -0
  53. package/dist/components/TupleToolDocsAccordion.js +14 -0
  54. package/dist/components/TupleToolDropdown.d.ts +21 -0
  55. package/dist/components/TupleToolDropdown.js +29 -0
  56. package/dist/components/TupleToolGroup.d.ts +6 -0
  57. package/dist/components/TupleToolGroup.js +22 -0
  58. package/dist/components/TupleToolLabel.d.ts +6 -0
  59. package/dist/components/TupleToolLabel.js +20 -0
  60. package/dist/components/TupleToolList.d.ts +7 -0
  61. package/dist/components/TupleToolList.js +38 -0
  62. package/dist/components/YearBadge.d.ts +5 -0
  63. package/dist/components/YearBadge.js +13 -33
  64. package/dist/components/modals/AddTupleToolModal.d.ts +7 -0
  65. package/dist/components/modals/AddTupleToolModal.js +32 -0
  66. package/dist/components/modals/ConfigureTupleToolModal.d.ts +8 -0
  67. package/dist/components/modals/ConfigureTupleToolModal.js +43 -0
  68. package/dist/components/modals/ReasonForRequestModal.d.ts +8 -0
  69. package/dist/components/modals/ReasonForRequestModal.js +49 -0
  70. package/dist/components/modals/UploadDttConfigModal.d.ts +7 -0
  71. package/dist/components/modals/UploadDttConfigModal.js +35 -0
  72. package/dist/components/modals/UploadProdConfigModal.d.ts +5 -0
  73. package/dist/components/modals/UploadProdConfigModal.js +27 -0
  74. package/dist/components/tupleToolParams/BoolParamInput.d.ts +16 -0
  75. package/dist/components/tupleToolParams/BoolParamInput.js +15 -0
  76. package/dist/components/tupleToolParams/DictParamInput.d.ts +14 -0
  77. package/dist/components/tupleToolParams/DictParamInput.js +31 -0
  78. package/dist/components/tupleToolParams/ListParamInput.d.ts +14 -0
  79. package/dist/components/tupleToolParams/ListParamInput.js +46 -0
  80. package/dist/components/tupleToolParams/NumParamInput.d.ts +18 -0
  81. package/dist/components/tupleToolParams/NumParamInput.js +25 -0
  82. package/dist/components/tupleToolParams/StrParamInput.d.ts +19 -0
  83. package/dist/components/tupleToolParams/StrParamInput.js +22 -0
  84. package/dist/config.d.ts +78 -0
  85. package/dist/config.js +72 -0
  86. package/dist/{style/DecaysList.css → index.d.ts} +2 -6
  87. package/dist/index.js +2 -11
  88. package/dist/models/bkPath.d.ts +11 -0
  89. package/dist/models/bkPath.js +40 -0
  90. package/dist/models/blobFile.d.ts +12 -0
  91. package/dist/models/blobFile.js +1 -0
  92. package/dist/models/decayData.d.ts +15 -0
  93. package/dist/models/decayData.js +1 -0
  94. package/dist/models/dropdownOption.d.ts +5 -0
  95. package/dist/models/dropdownOption.js +1 -0
  96. package/dist/models/dtt.d.ts +108 -0
  97. package/dist/models/dtt.js +149 -0
  98. package/dist/models/jobConfig.d.ts +7 -0
  99. package/dist/models/jobConfig.js +1 -0
  100. package/dist/models/particle.d.ts +12 -0
  101. package/dist/models/particle.js +1 -0
  102. package/dist/models/particleTag.d.ts +4 -0
  103. package/dist/models/particleTag.js +1 -0
  104. package/dist/models/rowData.d.ts +15 -0
  105. package/dist/models/rowData.js +1 -0
  106. package/dist/models/strippingLine.d.ts +5 -0
  107. package/dist/models/strippingLine.js +1 -0
  108. package/dist/models/strippingLineOption.d.ts +6 -0
  109. package/dist/models/strippingLineOption.js +1 -0
  110. package/dist/models/tupleTool.d.ts +8 -0
  111. package/dist/models/tupleTool.js +18 -0
  112. package/dist/models/tupleTreeGraphData.d.ts +20 -0
  113. package/dist/models/tupleTreeGraphData.js +1 -0
  114. package/dist/models/yamlFile.d.ts +11 -0
  115. package/dist/models/yamlFile.js +78 -0
  116. package/dist/pages/DecaySearchPage.d.ts +5 -0
  117. package/dist/pages/DecaySearchPage.js +197 -0
  118. package/dist/pages/DecayTreeConfigPage.d.ts +5 -0
  119. package/dist/pages/DecayTreeConfigPage.js +63 -0
  120. package/dist/pages/LinesTablePage.d.ts +14 -0
  121. package/dist/pages/LinesTablePage.js +120 -0
  122. package/dist/providers/DttProvider.d.ts +24 -0
  123. package/dist/providers/DttProvider.js +77 -0
  124. package/dist/providers/MetadataProvider.d.ts +87 -0
  125. package/dist/providers/MetadataProvider.js +50 -0
  126. package/dist/providers/ProductionConfigProvider.d.ts +14 -0
  127. package/dist/providers/ProductionConfigProvider.js +76 -0
  128. package/dist/providers/RequestProvider.d.ts +15 -0
  129. package/dist/providers/RequestProvider.js +35 -0
  130. package/dist/providers/RowsProvider.d.ts +14 -0
  131. package/dist/providers/RowsProvider.js +31 -0
  132. package/dist/utils/mathjaxUtils.d.ts +12 -0
  133. package/dist/utils/mathjaxUtils.js +24 -0
  134. package/dist/utils/utils.d.ts +31 -0
  135. package/dist/utils/utils.js +80 -0
  136. package/package.json +43 -21
  137. package/dist/components/App.js +0 -99
  138. package/dist/components/ConfigDict.js +0 -103
  139. package/dist/components/ConfigList.js +0 -94
  140. package/dist/components/ConfigNode.js +0 -320
  141. package/dist/components/ConfigValue.js +0 -86
  142. package/dist/components/Dataset.js +0 -67
  143. package/dist/components/Decay.js +0 -50
  144. package/dist/components/DecayItem.js +0 -101
  145. package/dist/components/DecayTag.js +0 -52
  146. package/dist/components/DecayTree.js +0 -451
  147. package/dist/components/DecaysList.js +0 -107
  148. package/dist/components/DescriptorsSearch.js +0 -352
  149. package/dist/components/EventTypeBadge.js +0 -45
  150. package/dist/components/ItemSearch.js +0 -120
  151. package/dist/components/LinesTable.js +0 -1105
  152. package/dist/components/ParticleTag.js +0 -54
  153. package/dist/components/SearchItem.js +0 -99
  154. package/dist/components/SelectParticle.js +0 -61
  155. package/dist/components/SelectTag.js +0 -66
  156. package/dist/components/SelectTool.js +0 -59
  157. package/dist/components/SelectVariables.js +0 -106
  158. package/dist/components/StrippingBadge.js +0 -64
  159. package/dist/components/StrippingLine.js +0 -50
  160. package/dist/components/TupleTool.js +0 -46
  161. package/dist/components/VariablesSearch.js +0 -128
  162. package/dist/config.json +0 -72
  163. package/dist/contexts/MetadataContext.js +0 -135
  164. package/dist/lib/BKPath.js +0 -58
  165. package/dist/lib/DTTConfig.js +0 -178
  166. package/dist/lib/mathjax.js +0 -35
  167. package/dist/lib/utils.js +0 -191
@@ -0,0 +1,197 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*****************************************************************************\
3
+ * (c) Copyright 2021 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 memoize from "lodash.memoize";
13
+ import { useEffect, useState, useTransition } from "react";
14
+ import { Alert, Badge, Button, Dropdown, DropdownButton, InputGroup, OverlayTrigger, Spinner, Tooltip, } from "react-bootstrap";
15
+ import { Link } from "react-router-dom";
16
+ import Select from "react-select";
17
+ import { useMetadata } from "../providers/MetadataProvider";
18
+ import { DecayList } from "../components/DecayList";
19
+ import { ParticleDropdown } from "../components/ParticleDropdown";
20
+ import { getSelectTagOptions, TagDropdown } from "../components/TagDropdown";
21
+ import { useRows } from "../providers/RowsProvider";
22
+ import { LoadingIndicator } from "../components/LoadingIndicator";
23
+ var MatchType;
24
+ (function (MatchType) {
25
+ MatchType["any"] = "any";
26
+ MatchType["all"] = "all";
27
+ MatchType["none"] = "none";
28
+ })(MatchType || (MatchType = {}));
29
+ var HeadMatchType;
30
+ (function (HeadMatchType) {
31
+ HeadMatchType["exactly"] = "exactly";
32
+ HeadMatchType["category"] = "category";
33
+ })(HeadMatchType || (HeadMatchType = {}));
34
+ export function DecaySearchPage({ basePath }) {
35
+ const metadata = useMetadata();
36
+ const { rows, setRows } = useRows();
37
+ const [selectedContains, setSelectedContains] = useState([]);
38
+ const [selectedHead, setSelectedHead] = useState(null);
39
+ const [selectedHeadTags, setSelectedHeadTags] = useState([]);
40
+ const [selectedDecays, setSelectedDecays] = useState(rows.map((row) => row.decay));
41
+ const [selectedTags, setSelectedTags] = useState(null);
42
+ const [selectedLine, setSelectedLine] = useState(null);
43
+ const [chargeConjugateContains, setChargeConjugateContains] = useState(true);
44
+ const [chargeConjugateHeads, setChargeConjugateHeads] = useState(true);
45
+ const [containsMatchType, setContainsMatchType] = useState(MatchType.all);
46
+ const [headMatchType, setHeadMatchType] = useState(HeadMatchType.exactly);
47
+ const [tagMatchType, setTagMatchType] = useState(MatchType.none);
48
+ const [headTagMatchType, setHeadTagMatchType] = useState(MatchType.all);
49
+ const [onlySelected, setOnlySelected] = useState(false);
50
+ const [showDecaysList, setShowDecaysList] = useState(false);
51
+ const [isLoadingDecaysList, startTransition] = useTransition();
52
+ useEffect(() => {
53
+ startTransition(() => {
54
+ setShowDecaysList(true);
55
+ });
56
+ }, []);
57
+ useEffect(() => {
58
+ if (!metadata) {
59
+ return;
60
+ }
61
+ // Set default value for selectedTags when metadata is loaded
62
+ setSelectedTags(getSelectTagOptions(metadata.userHints.decayTags, (tag) => tag.hide));
63
+ }, [metadata]);
64
+ if (!metadata) {
65
+ return _jsx(LoadingIndicator, { height: "70vh" });
66
+ }
67
+ const sendData = () => {
68
+ const selectedDecaySet = new Set(selectedDecays.map((d) => d.descriptors.plain));
69
+ // Keep only rows whose decay is still selected
70
+ const selectedRows = rows.filter((row) => selectedDecaySet.has(row.decay.descriptors.plain));
71
+ const existingDecaySet = new Set(selectedRows.map((row) => row.decay.descriptors.plain));
72
+ // Track used row IDs
73
+ const usedIds = new Set(selectedRows.map((row) => row.id));
74
+ let nextId = 0;
75
+ const getNextId = () => {
76
+ while (usedIds.has(nextId))
77
+ nextId++;
78
+ usedIds.add(nextId);
79
+ return nextId;
80
+ };
81
+ // Add missing decays
82
+ for (const decay of selectedDecays) {
83
+ const key = decay.descriptors.plain;
84
+ if (!existingDecaySet.has(key)) {
85
+ selectedRows.push({
86
+ id: getNextId(),
87
+ decay,
88
+ dtt: null,
89
+ lines: [],
90
+ paths: [],
91
+ pathOptions: [],
92
+ });
93
+ }
94
+ }
95
+ setRows(selectedRows);
96
+ };
97
+ const variableMatch = (type, conditions) => {
98
+ switch (type) {
99
+ case MatchType.all:
100
+ return conditions.every((x) => x);
101
+ case MatchType.any:
102
+ return conditions.some((x) => x);
103
+ case MatchType.none:
104
+ return !conditions.some((x) => x);
105
+ }
106
+ };
107
+ const allStrippingLines = memoize(() => {
108
+ const allLines = Object.values(metadata.stripping).flatMap((stream) => Object.keys(stream));
109
+ const uniqueLines = new Set(allLines);
110
+ return [...uniqueLines];
111
+ });
112
+ const filterDecays = () => {
113
+ // TODO: can probably do some refactoring in here (cf variableMatch)
114
+ let newDecays = Object.values(metadata.decays);
115
+ if (selectedContains) {
116
+ newDecays = newDecays.filter((decay) => {
117
+ const conditions = selectedContains.map((option) => {
118
+ const particle = option.value;
119
+ const antiparticle = metadata.particleProperties[particle]["antiparticle"];
120
+ return (decay.descriptors.plain.includes(particle) ||
121
+ (chargeConjugateContains && decay.descriptors.plain.includes(antiparticle)));
122
+ });
123
+ return variableMatch(containsMatchType, conditions);
124
+ });
125
+ }
126
+ if (headMatchType === HeadMatchType.exactly) {
127
+ if (selectedHead) {
128
+ const particle = selectedHead.value;
129
+ const antiparticle = metadata.particleProperties[particle].antiparticle;
130
+ newDecays = newDecays.filter((decay) => {
131
+ const head = decay.descriptors.list[0];
132
+ return head === particle || (chargeConjugateHeads && head === antiparticle);
133
+ });
134
+ }
135
+ }
136
+ else if (headMatchType === HeadMatchType.category) {
137
+ if (selectedHeadTags) {
138
+ newDecays = newDecays.filter((decay) => {
139
+ const conditions = selectedHeadTags.map((option) => {
140
+ const tag = option.value;
141
+ const head = metadata.particleProperties[decay.descriptors.list[0]];
142
+ return head.tags.includes(tag);
143
+ });
144
+ return variableMatch(headTagMatchType, conditions);
145
+ });
146
+ }
147
+ }
148
+ if (onlySelected) {
149
+ newDecays = newDecays.filter((decay) => selectedDecays
150
+ .map((selectedDecay) => selectedDecay.descriptors.plain)
151
+ .includes(decay.descriptors.plain));
152
+ }
153
+ if (selectedTags) {
154
+ newDecays = newDecays.filter((decay) => {
155
+ const conditions = selectedTags.map((tag) => {
156
+ return decay.tags.includes(tag.value);
157
+ });
158
+ return variableMatch(tagMatchType, conditions);
159
+ });
160
+ }
161
+ if (selectedLine) {
162
+ const line = selectedLine.value;
163
+ newDecays = newDecays.filter((decay) => {
164
+ const decayStreamLines = Object.keys(decay.lines);
165
+ const decayLines = new Set(decayStreamLines.map((streamLine) => streamLine.split("/")[1]));
166
+ return decayLines.has(line);
167
+ });
168
+ }
169
+ return newDecays;
170
+ };
171
+ function DropdownMatchTypes({ options, stateItem, stateSetter }) {
172
+ return (_jsx(_Fragment, { children: options.map((option) => (_jsx(Dropdown.Item, { active: stateItem === option, onClick: () => stateSetter(option), children: option }, option))) }));
173
+ }
174
+ function DropdownChargeConjugate({ stateItem, stateSetter }) {
175
+ return (_jsxs(_Fragment, { children: [_jsx(Dropdown.Item, { active: stateItem, onClick: () => stateSetter(true), children: "Include antiparticles" }), _jsx(Dropdown.Item, { active: !stateItem, onClick: () => stateSetter(false), children: "Exclude antiparticles" })] }));
176
+ }
177
+ const getDecayList = () => {
178
+ if (!selectedTags) {
179
+ return (_jsxs(Alert, { variant: "warning", children: [_jsx(Spinner, { size: "sm", animation: "border" }), " Loading decays..."] }));
180
+ }
181
+ if (!showDecaysList || isLoadingDecaysList) {
182
+ return _jsx(LoadingIndicator, { height: "40vh" });
183
+ }
184
+ const filteredDecays = selectedTags ? filterDecays() : [];
185
+ return (_jsx(DecayList, { decays: filteredDecays, selectedDecays: selectedDecays, onItemSelected: (newSelectedDecays) => {
186
+ setSelectedDecays([...newSelectedDecays]);
187
+ } }, JSON.stringify(filteredDecays)));
188
+ };
189
+ const getExtraInputs = () => {
190
+ return (_jsx(_Fragment, { children: _jsxs(InputGroup, { size: "sm", children: [_jsx(DropdownButton, { title: "Tags (" + tagMatchType + " of):", variant: "outline-secondary", children: _jsx(DropdownMatchTypes, { options: Object.values(MatchType), stateItem: tagMatchType, stateSetter: setTagMatchType }) }), _jsx(TagDropdown, { type: "decayTags", placeholder: "DecayLatex tags", value: selectedTags, onChange: (values) => setSelectedTags([...values]), closeMenuOnSelect: false }), _jsx("div", { className: "react-select form-control p-0", children: _jsx(Select, { placeholder: "Stripping line", isClearable: true, options: metadata
191
+ ? allStrippingLines().map((line) => ({ value: line, label: line }))
192
+ : [], onChange: setSelectedLine, isLoading: !metadata }) })] }) }));
193
+ };
194
+ return (_jsxs(_Fragment, { children: [_jsx("h3", { children: "DecayLatex search" }), _jsxs(InputGroup, { size: "sm", children: [_jsxs(DropdownButton, { title: "Head (" +
195
+ (headMatchType === HeadMatchType.exactly ? headMatchType : headTagMatchType + " of") +
196
+ "):", variant: "outline-secondary", children: [_jsx(DropdownMatchTypes, { options: Object.values(HeadMatchType), stateItem: headMatchType, stateSetter: setHeadMatchType }), _jsx(Dropdown.Divider, {}), headMatchType === HeadMatchType.exactly ? (_jsx(DropdownChargeConjugate, { stateItem: chargeConjugateHeads, stateSetter: setChargeConjugateHeads })) : (_jsx(DropdownMatchTypes, { options: Object.values(MatchType), stateItem: headTagMatchType, stateSetter: setHeadTagMatchType }))] }), headMatchType === HeadMatchType.exactly ? (_jsx(ParticleDropdown, { isClearable: true, onlyHeads: true, placeholder: "DecayLatex head", onChange: (value) => setSelectedHead(value) })) : (_jsx(TagDropdown, { type: "particleTags", placeholder: "DecayLatex head", filter: (tag) => !tag.hide, onChange: (values) => setSelectedHeadTags([...values]) })), _jsxs(DropdownButton, { title: "Contains (" + containsMatchType + " of):", variant: "outline-secondary", children: [_jsx(DropdownMatchTypes, { options: Object.values(MatchType), stateItem: containsMatchType, stateSetter: setContainsMatchType }), _jsx(Dropdown.Divider, {}), _jsx(DropdownChargeConjugate, { stateItem: chargeConjugateContains, stateSetter: setChargeConjugateContains })] }), _jsx(ParticleDropdown, { isMulti: true, onlyKnown: true, placeholder: "Particles in decay...", onChange: (values) => setSelectedContains(values), closeMenuOnSelect: false }), _jsx(InputGroup.Text, { children: "Show only selected:" }), _jsx(InputGroup.Checkbox, { onChange: () => setOnlySelected(!onlySelected) })] }), getExtraInputs(), getDecayList(), _jsx(Link, { to: basePath, children: _jsx(OverlayTrigger, { overlay: _jsx(Tooltip, { children: "Return to production configuration" }), children: _jsxs(Button, { variant: "success", type: "submit", onClick: sendData, children: ["Select ", _jsx(Badge, { pill: true, bg: "secondary", children: selectedDecays.length })] }) }) })] }));
197
+ }
@@ -0,0 +1,5 @@
1
+ interface Props {
2
+ basePath: string;
3
+ }
4
+ export declare function DecayTreeConfigPage({ basePath }: Props): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*****************************************************************************\
3
+ * (c) Copyright 2021 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 { Button, Col, OverlayTrigger, Row, Stack, Tooltip } from "react-bootstrap";
13
+ import { useNavigate } from "react-router-dom";
14
+ import { useMetadata } from "../providers/MetadataProvider";
15
+ import { DecayTreeCard } from "../components/DecayTreeCard";
16
+ import { useRows } from "../providers/RowsProvider";
17
+ import Dtt from "../models/dtt";
18
+ import { DttProvider } from "../providers/DttProvider";
19
+ import { LoadingIndicator } from "../components/LoadingIndicator";
20
+ import { useEffect, useState } from "react";
21
+ export function DecayTreeConfigPage({ basePath }) {
22
+ const navigate = useNavigate();
23
+ const metadata = useMetadata();
24
+ const { rows, setRows } = useRows();
25
+ const [dirtyDtts, setDirtyDtts] = useState(new Set());
26
+ // Listen for when the user is about to leave the page and alert them if there are unsaved changes
27
+ useEffect(() => {
28
+ const handleBeforeUnload = (event) => {
29
+ if (dirtyDtts.size > 0) {
30
+ event.preventDefault();
31
+ }
32
+ };
33
+ window.addEventListener("beforeunload", handleBeforeUnload);
34
+ return () => {
35
+ window.removeEventListener("beforeunload", handleBeforeUnload);
36
+ };
37
+ }, [dirtyDtts]);
38
+ if (!metadata) {
39
+ return _jsx(LoadingIndicator, { height: "70vh" });
40
+ }
41
+ const rowsToEdit = rows.filter((row) => !!row.editTree);
42
+ return (_jsxs(_Fragment, { children: [_jsx("h3", { children: "DecayTreeTuple configuration" }), _jsx(Stack, { gap: 5, children: rowsToEdit.map((row) => (_jsx(Row, { className: "justify-content-lg-center", children: _jsx(Col, { lg: "auto", children: _jsx(DttProvider, { metadata: metadata, decay: row.decay, initialConfig: row.dtt.config, children: _jsx(DecayTreeCard, { onConfigSaved: (newConfig) => {
43
+ setRows((prev) => {
44
+ return prev.map((r) => r.id === row.id ? { ...r, dtt: new Dtt(newConfig, {}) } : r);
45
+ });
46
+ }, onDirtyUpdated: (dttName, dirty) => setDirtyDtts((prev) => {
47
+ if (dirty) {
48
+ return new Set(prev.add(dttName));
49
+ }
50
+ else {
51
+ const newSet = new Set(prev);
52
+ newSet.delete(dttName);
53
+ return newSet;
54
+ }
55
+ }) }) }) }, row.id) }, row.id))) }), _jsx(OverlayTrigger, { placement: "right", overlay: _jsx(Tooltip, { children: "Return to Stripping line and dataset selection" }), children: _jsx(Button, { className: "mt-2 mb-4", type: "submit", onClick: () => {
56
+ if (dirtyDtts.size > 0) {
57
+ if (!confirm("You have unsaved changes. Are you sure you want to discard them?")) {
58
+ return;
59
+ }
60
+ }
61
+ void navigate(basePath);
62
+ }, variant: dirtyDtts.size > 0 ? "outline-danger" : "primary", children: dirtyDtts.size > 0 ? "Discard changes" : "Return" }) })] }));
63
+ }
@@ -0,0 +1,14 @@
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 {};
@@ -0,0 +1,120 @@
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, { allRowsHaveDtt: allRowsHaveDtt, disabled: !allRowsHaveDtt ||
115
+ !allRowsHavePaths ||
116
+ !request.productionName ||
117
+ (submitLocation && !request.reasonForRequest) ||
118
+ !(emailIsKnown || isEmailValid) ||
119
+ !validDttNames, onSubmit: () => setRequestSubmitted(true), submitLocation: submitLocation, hideDownloadButtons: hideDownloadButtons, requestReasonMessage: requestReasonMessage, csrfToken: csrfToken }), _jsx(DeleteButton, { action: clearAll, disabled: isEmptySession, outline: undefined, children: "Clear all" })] })] }) })] }));
120
+ }
@@ -0,0 +1,24 @@
1
+ import { ReactNode } from "react";
2
+ import Dtt, { DttConfig, ToolParamTypeMap } from "../models/dtt";
3
+ import { MetadataContext } from "./MetadataProvider";
4
+ import { DecayData } from "../models/decayData";
5
+ import { TupleTool } from "../models/tupleTool";
6
+ interface DttContextType {
7
+ dtt: Dtt;
8
+ decay: DecayData;
9
+ updateToolParam<K extends keyof ToolParamTypeMap>(this: void, branch: string[], tool: TupleTool, param: string, value: ToolParamTypeMap[K]): void;
10
+ addTool(this: void, branch: string[], tool: TupleTool): void;
11
+ removeTool(this: void, branch: string[], tool: TupleTool): void;
12
+ dirty: boolean;
13
+ save(this: void): void;
14
+ revert(this: void): void;
15
+ }
16
+ interface DttProviderProps {
17
+ initialConfig: DttConfig;
18
+ decay: DecayData;
19
+ metadata: MetadataContext;
20
+ children: ReactNode;
21
+ }
22
+ export declare function DttProvider({ initialConfig, decay, metadata, children }: DttProviderProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function useDtt(): DttContextType;
24
+ export {};
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useReducer } from "react";
3
+ import Dtt from "../models/dtt";
4
+ const DttContext = createContext(null);
5
+ function reducer(state, action) {
6
+ switch (action.type) {
7
+ case "INIT":
8
+ return {
9
+ dtt: action.dtt,
10
+ lastSaved: structuredClone(action.config),
11
+ dirty: false,
12
+ };
13
+ case "UPDATE":
14
+ return {
15
+ ...state,
16
+ dtt: action.dtt,
17
+ dirty: true,
18
+ };
19
+ case "SAVE":
20
+ return {
21
+ ...state,
22
+ lastSaved: structuredClone(state.dtt.config),
23
+ dirty: false,
24
+ };
25
+ case "REVERT":
26
+ return {
27
+ ...state,
28
+ dtt: state.dtt.withConfig(state.lastSaved),
29
+ dirty: false,
30
+ };
31
+ }
32
+ }
33
+ export function DttProvider({ initialConfig, decay, metadata, children }) {
34
+ const [state, dispatch] = useReducer(reducer, null, () => ({
35
+ dtt: new Dtt(structuredClone(initialConfig), metadata.tupleTools.tupleTools),
36
+ decay,
37
+ lastSaved: structuredClone(initialConfig),
38
+ dirty: false,
39
+ }));
40
+ const updateToolParam = useCallback((branch, tool, param, value) => {
41
+ const next = state.dtt.withUpdatedToolParam(branch, tool, param, value);
42
+ if (next === state.dtt)
43
+ return;
44
+ dispatch({ type: "UPDATE", dtt: next });
45
+ }, [state.dtt]);
46
+ const addTool = useCallback((branch, tool) => {
47
+ dispatch({
48
+ type: "UPDATE",
49
+ dtt: state.dtt.withAddedTool(tool, branch),
50
+ });
51
+ }, [state.dtt]);
52
+ const removeTool = useCallback((branch, tool) => {
53
+ dispatch({
54
+ type: "UPDATE",
55
+ dtt: state.dtt.withRemovedTool(tool, branch),
56
+ });
57
+ }, [state.dtt]);
58
+ const save = useCallback(() => dispatch({ type: "SAVE" }), []);
59
+ const revert = useCallback(() => dispatch({ type: "REVERT" }), []);
60
+ const value = {
61
+ dtt: state.dtt,
62
+ decay,
63
+ dirty: state.dirty,
64
+ updateToolParam,
65
+ addTool,
66
+ removeTool,
67
+ save,
68
+ revert,
69
+ };
70
+ return _jsx(DttContext.Provider, { value: value, children: children });
71
+ }
72
+ export function useDtt() {
73
+ const ctx = useContext(DttContext);
74
+ if (!ctx)
75
+ throw new Error("useDtt must be used inside DttProvider");
76
+ return ctx;
77
+ }
@@ -0,0 +1,87 @@
1
+ /*****************************************************************************\
2
+ * (c) Copyright 2021 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
+ \*****************************************************************************/
11
+ import { ReactNode } from "react";
12
+ import { Particle } from "../models/particle.js";
13
+ import { DecayData } from "../models/decayData";
14
+ import { ToolMetadata } from "../models/dtt.js";
15
+ export type TagHint = DecayTagHint | ParticleTagHint;
16
+ export interface DecayTagHint {
17
+ description: string;
18
+ hide: boolean;
19
+ warn: boolean;
20
+ }
21
+ export interface ParticleTagHint {
22
+ description: string;
23
+ group: string;
24
+ hide: boolean;
25
+ }
26
+ interface ApplicationInfo {
27
+ Analysis: string;
28
+ DaVinci: string;
29
+ Phys: string;
30
+ }
31
+ interface StrippingInfo {
32
+ full_decays: string[];
33
+ output: string;
34
+ prescale: number;
35
+ stream: string[];
36
+ url: {
37
+ [key: string]: string;
38
+ };
39
+ }
40
+ export interface MetadataContext {
41
+ dataset: string[];
42
+ decays: {
43
+ [key: string]: DecayData;
44
+ };
45
+ embedding: {
46
+ [key: string]: string;
47
+ };
48
+ kgdoc: {
49
+ [key: string]: object;
50
+ };
51
+ lokiVariables: {
52
+ applicationInfo: ApplicationInfo;
53
+ lokiVariables: object;
54
+ };
55
+ particleProperties: {
56
+ [key: string]: Particle;
57
+ };
58
+ stripping: {
59
+ [key: string]: {
60
+ [key: string]: StrippingInfo;
61
+ };
62
+ };
63
+ strippingHints: {
64
+ [key: string]: {
65
+ davinci: string;
66
+ description: string;
67
+ };
68
+ };
69
+ tupleTools: {
70
+ applicationInfo: ApplicationInfo;
71
+ tupleTools: ToolMetadata;
72
+ };
73
+ userHints: {
74
+ decayTags: {
75
+ [key: string]: DecayTagHint;
76
+ };
77
+ particleTags: {
78
+ [key: string]: ParticleTagHint;
79
+ };
80
+ };
81
+ }
82
+ interface Props {
83
+ children: ReactNode;
84
+ }
85
+ export declare function MetadataProvider({ children }: Props): import("react/jsx-runtime").JSX.Element;
86
+ export declare const useMetadata: () => MetadataContext | null;
87
+ export {};
@@ -0,0 +1,50 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /*****************************************************************************\
3
+ * (c) Copyright 2021 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 { createContext, useContext, useEffect, useState } from "react";
13
+ import { config } from "../config.js";
14
+ import pako from "pako";
15
+ const MetadataContextType = createContext(null);
16
+ export function MetadataProvider({ children }) {
17
+ const [error, setError] = useState(null);
18
+ const [metadata, setMetadata] = useState(null);
19
+ async function loadDict(path) {
20
+ const response = await fetch(`${config.metadata_baseurl}${path}.json.gz`);
21
+ if (!response.ok) {
22
+ console.error(response);
23
+ return null;
24
+ }
25
+ const data = await response.arrayBuffer();
26
+ const unzipped = pako.inflate(new Uint8Array(data));
27
+ const decoded = new TextDecoder("utf-8").decode(unzipped);
28
+ return JSON.parse(decoded);
29
+ }
30
+ useEffect(() => {
31
+ const metadataContextKeys = Object.keys(config.metadata_files);
32
+ // Load all metadata files
33
+ const promises = metadataContextKeys.map(async (key) => [key, await loadDict(config.metadata_files[key])]);
34
+ // Build the context object
35
+ Promise.all(promises)
36
+ .then((entries) => {
37
+ setMetadata(Object.fromEntries(entries));
38
+ })
39
+ .catch(setError);
40
+ }, []);
41
+ if (error) {
42
+ return _jsxs("h1", { children: ["Oops: ", `${error}`] });
43
+ }
44
+ else {
45
+ return _jsx(MetadataContextType.Provider, { value: metadata, children: children });
46
+ }
47
+ }
48
+ export const useMetadata = () => {
49
+ return useContext(MetadataContextType);
50
+ };