diff --git a/SearchFrontend/search_ui/.prettierrc b/SearchFrontend/search_ui/.prettierrc index 222861c..3d6a845 100644 --- a/SearchFrontend/search_ui/.prettierrc +++ b/SearchFrontend/search_ui/.prettierrc @@ -1,4 +1,5 @@ { - "tabWidth": 2, - "useTabs": false + "tabWidth": 4, + "useTabs": false, + "experimentalOperatorPosition": "start" } diff --git a/SearchFrontend/search_ui/package-lock.json b/SearchFrontend/search_ui/package-lock.json index f85c2bf..a4f3059 100644 --- a/SearchFrontend/search_ui/package-lock.json +++ b/SearchFrontend/search_ui/package-lock.json @@ -1753,9 +1753,9 @@ "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==" }, "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "requires": { "is-arrayish": "^0.2.1" } @@ -2561,6 +2561,11 @@ "prop-types": "^15.5.4" } }, + "react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==" + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/SearchFrontend/search_ui/package.json b/SearchFrontend/search_ui/package.json index 993bd53..f8f70e0 100644 --- a/SearchFrontend/search_ui/package.json +++ b/SearchFrontend/search_ui/package.json @@ -29,6 +29,7 @@ "react-dom": "^19.1.1", "react-flexbox-grid": "^2.1.2", "react-split-pane": "^0.1.92", + "react-table": "^7.8.0", "rsuite": "^5.83.3", "timelines-chart": "^2.14.2", "uplot": "^1.6.32", diff --git a/SearchFrontend/search_ui/src/App.jsx b/SearchFrontend/search_ui/src/App.jsx index 25b91b7..5c22c88 100644 --- a/SearchFrontend/search_ui/src/App.jsx +++ b/SearchFrontend/search_ui/src/App.jsx @@ -9,283 +9,334 @@ import "./App.css"; import StatusesDisplayHUD from "./components/StatusDisplay"; function App() { - const original_data = useRef(null); - const chartRef = useRef(null); - const [dataResults, setDataResults] = useState(null); - const [statusMessages, setStatusMessages] = useState([]); - const [markerTime, setMarkerTime] = useState(0); - const playerRef = useRef(null); - const playerInstanceRef = useRef(null); - // State for the values - window.chartRef = chartRef; - window.playerRef = playerRef; - window.playerInstanceRef = playerInstanceRef; - // Slider states + const original_data = useRef(null); + const chartRef = useRef(null); + const [dataResults, setDataResults] = useState(null); + const [statusMessages, setStatusMessages] = useState([]); + const [markerTime, setMarkerTime] = useState(0); + const playerRef = useRef(null); + const playerInstanceRef = useRef(null); + // State for the values + window.chartRef = chartRef; + window.playerRef = playerRef; + window.playerInstanceRef = playerInstanceRef; + // Slider states - const [sliderMin, setSliderMin] = useState(0.0); - const [sliderMax, setSliderMax] = useState(1.0); - // Date range states - // + const [sliderMin, setSliderMin] = useState(0.0); + const [sliderMax, setSliderMax] = useState(1.0); + // Date range states + // - const [startRange, setStartRange] = useState( - new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000) - ); - const [endRange, setEndRange] = useState(new Date()); - // const [endRange, setEndRange] = useState(new Date(new Date().getTime() - 6 * 24 * 60 * 60 * 1000)); - const [queryText, setQueryText] = useState("A clouded leopard and a human"); - const [sliderValue, setSliderValue] = useState(0); - - // State to track last submitted values - const [lastSubmitted, setLastSubmitted] = useState({ - startRange, - endRange, - sliderValue, - queryText, - }); - - // Check if any value has changed - const hasChanged = - startRange !== lastSubmitted.startRange || - endRange !== lastSubmitted.endRange || - sliderValue !== lastSubmitted.sliderValue || - queryText !== lastSubmitted.queryText; - - // Function to resubmit fetch - const handleResubmit = () => { - // Start streaming status updates - fetch("api/return_status") - .then((response) => { - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; // Accumulate partial text - - function read() { - reader.read().then(({ done, value }) => { - if (done) { - if (buffer) { - // console.log("Status:", buffer); // Log any remaining text - } - setStatusMessages([]); - // console.log("Status stream finished"); - - return; - } - // Decode only the new chunk - buffer += decoder.decode(value, { stream: true }); - - // If your server sends lines, split and log only complete lines: - let lines = buffer.split("\n"); - buffer = lines.pop(); // Save incomplete line for next chunk - - for (const line of lines) { - if (line.trim()) { - // console.log("Status:", line); - console.log(line) - setStatusMessages((msgs) => [...msgs, JSON.parse(line)]); - } - } - - read(); - }); - } - read(); - }) - .catch((error) => { - console.error("Error while streaming status:", error); - }); - - const params = new URLSearchParams(); - params.append("startRange", startRange.toISOString()); - params.append("endRange", endRange.toISOString()); - params.append("threshold", 0.0); - params.append("query", queryText); - setDataResults({ videos: [], breaks: [] }); - - fetch("api/videos.json?" + params.toString()) - .then((res) => res.json()) - .then((data) => { - const max_value = Math.max( - ...data["videos"].map((vid) => vid["embed_scores"]["score"][1]) - ); - setSliderMax(max_value); - original_data.current = data; - window.original_data = original_data; - setDataResults(data); - }); - - setLastSubmitted({ startRange, endRange, sliderValue, queryText }); - }; - - function updateDataAndValue(newValue) { - const floatValue = parseFloat(newValue); - setSliderValue(floatValue); - var newData = JSON.parse(JSON.stringify(original_data.current)); - newData["videos"] = newData["videos"].filter( - (vid) => vid["embed_scores"]["score"][1] >= floatValue + const [startRange, setStartRange] = useState( + new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000) ); - setDataResults(newData); - } + const [endRange, setEndRange] = useState(new Date()); + // const [endRange, setEndRange] = useState(new Date(new Date().getTime() - 6 * 24 * 60 * 60 * 1000)); + const [queryText, setQueryText] = useState("A clouded leopard and a human"); + const [sliderValue, setSliderValue] = useState(0); - function setMarkerValueNonReactive(inputValue) { - let chart = chartRef.current.getEchartsInstance(); - let options = chart.getOption(); - let mappers = options["mappers"]; + // State to track last submitted values + const [lastSubmitted, setLastSubmitted] = useState({ + startRange, + endRange, + sliderValue, + queryText, + }); - let vv = { - xAxis: mappers["real_to_virtual"](new Date(inputValue)), - lineStyle: { type: "solid", color: "#FF0000", width: 2 }, - label: { - show: false, - formatter: "Break", - position: "bottom", - color: "#888", - fontSize: 10, - }, + // Check if any value has changed + const hasChanged = + startRange !== lastSubmitted.startRange || + endRange !== lastSubmitted.endRange || + sliderValue !== lastSubmitted.sliderValue || + queryText !== lastSubmitted.queryText; + + const streamComputeStatus = () => { + fetch("api/return_status") + .then((response) => { + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; // Accumulate partial text + + function read() { + reader.read().then(({ done, value }) => { + if (done) { + if (buffer) { + } + setStatusMessages([]); + + return; + } + // Decode only the new chunk + buffer += decoder.decode(value, { stream: true }); + + // If your server sends lines, split and log only complete lines: + let lines = buffer.split("\n"); + buffer = lines.pop(); // Save incomplete line for next chunk + + for (const line of lines) { + if (line.trim()) { + let c_line = JSON.parse(line); + + if (c_line["task"] !== "DONE_QUIT") { + console.log(c_line); + setStatusMessages((msgs) => [ + ...msgs, + c_line, + ]); + } + } + } + + read(); + }); + } + read(); + }) + .catch((error) => { + console.error("Error while streaming status:", error); + }); + }; + // Function to resubmit fetch + const handleResubmit = (doTestMode = false) => { + console.log("startRange, endRange:", startRange, endRange); + console.log("test mode:", doTestMode); + let startRangeUse; + let endRangeUse; + if (doTestMode == true) { + startRangeUse = new Date( + new Date().getTime() - 2 * 24 * 60 * 60 * 1000 + ); + endRangeUse = new Date( + new Date().getTime() - 1 * 24 * 60 * 60 * 1000 + ); + } else { + startRangeUse = startRange; + endRangeUse = endRange; + } + + console.log("Using date range:", startRangeUse, endRangeUse); + setStartRange(startRangeUse); + setEndRange(endRangeUse); + + const params = new URLSearchParams(); + params.append("startRange", startRangeUse.toISOString()); + params.append("endRange", endRangeUse.toISOString()); + params.append("threshold", 0.0); + params.append("query", queryText); + setDataResults({ videos: [], breaks: [] }); + + fetch("api/videos.json?" + params.toString()) + .then((res) => res.json()) + .then((data) => { + streamComputeStatus(); + // Don't setDataResults here, since it's just {"status": ...} + pollForResult(); // Start polling for the real result + }); + + setLastSubmitted({ + startRangeUse, + endRangeUse, + sliderValue, + queryText, + }); }; - let markLine = { - symbol: ["none", "none"], - data: [vv], - lineStyle: { type: "dashed", color: "#FF0000", width: 2 }, - silent: true, - animation: false, - }; - - // if ("markLine" in options["series"][1]) { - if (false) { - let vv_new = { - xAxis: mappers["real_to_virtual"](new Date(inputValue)), - }; - let markLine_new = { - data: [vv_new], - }; - - chart.setOption( - { - series: [{}, { markLine: { data: [vv_new] } }], - }, - false, - ["series.markLine"] - ); - } else { - chart.setOption( - { - series: [{}, { markLine: markLine }], - }, - false, - ["series.markLine"] - ); + function updateDataAndValue(newValue) { + const floatValue = parseFloat(newValue); + setSliderValue(floatValue); + var newData = JSON.parse(JSON.stringify(original_data.current)); + newData["videos"] = newData["videos"].filter( + (vid) => vid["embed_scores"]["score"][1] >= floatValue + ); + setDataResults(newData); } - } - // Memoize the timeline click handler - const handleTimelineClick = useCallback( - (path, timeoffset) => { - console.log("Timeline clicked:", path, timeoffset); + function setMarkerValueNonReactive(inputValue) { + let chart = chartRef.current.getEchartsInstance(); + let options = chart.getOption(); + let mappers = options["mappers"]; - if (playerRef.current && playerInstanceRef.current) { - console.log("Seeking video player to:", path, timeoffset); - playerInstanceRef.current.src({ - src: "api/" + path, - type: "video/mp4", - }); - playerInstanceRef.current.on("loadedmetadata", () => { - playerInstanceRef.current.currentTime(timeoffset); - }); - } - }, - [] // Empty dependency array since it only uses playerRef - ); + let vv = { + xAxis: mappers["real_to_virtual"](new Date(inputValue)), + lineStyle: { type: "solid", color: "#FF0000", width: 2 }, + label: { + show: false, + formatter: "Break", + position: "bottom", + color: "#888", + fontSize: 10, + }, + }; - useEffect(() => { - const params = new URLSearchParams(window.location.search); // id=123 + let markLine = { + symbol: ["none", "none"], + data: [vv], + lineStyle: { type: "dashed", color: "#FF0000", width: 2 }, + silent: true, + animation: false, + }; - if (params.get("test_mode") == "true") { - setStartRange(new Date(new Date().getTime() - 2 * 24 * 60 * 60 * 1000)); - setEndRange(new Date(new Date().getTime() - 1 * 24 * 60 * 60 * 1000)); + // if ("markLine" in options["series"][1]) { + if (false) { + let vv_new = { + xAxis: mappers["real_to_virtual"](new Date(inputValue)), + }; + let markLine_new = { + data: [vv_new], + }; + + chart.setOption( + { + series: [{}, { markLine: { data: [vv_new] } }], + }, + false, + ["series.markLine"] + ); + } else { + chart.setOption( + { + series: [{}, { markLine: markLine }], + }, + false, + ["series.markLine"] + ); + } } - handleResubmit(); - }, []); - return ( -
| + {column.render("Header")} + | + ))} +
|---|
| + {cell.render("Cell")} + | + ))} +