diff --git a/.resources/922aad3d7bf2a6c848e29ddd5536cebb239be00fa9eaad659d9e14fee5d8db97 b/.resources/922aad3d7bf2a6c848e29ddd5536cebb239be00fa9eaad659d9e14fee5d8db97 deleted file mode 100644 index f19895a4..00000000 Binary files a/.resources/922aad3d7bf2a6c848e29ddd5536cebb239be00fa9eaad659d9e14fee5d8db97 and /dev/null differ diff --git a/.resources/99a73f815dda61d236296c3fb56af43e21b0d406dbddd200f11866170306ddec b/.resources/99a73f815dda61d236296c3fb56af43e21b0d406dbddd200f11866170306ddec new file mode 100644 index 00000000..11cb226d Binary files /dev/null and b/.resources/99a73f815dda61d236296c3fb56af43e21b0d406dbddd200f11866170306ddec differ diff --git a/.resources/4bc725abbffede84edf5fd949e9689c7ed78d612da2e1970e2c743ef5ef72101 b/.resources/de7dea6b0bac354930e91556c1a0267062a02c23062fbd900539c777e3ef9fc2 similarity index 98% rename from .resources/4bc725abbffede84edf5fd949e9689c7ed78d612da2e1970e2c743ef5ef72101 rename to .resources/de7dea6b0bac354930e91556c1a0267062a02c23062fbd900539c777e3ef9fc2 index 7749b0f3..b9f0edbb 100644 --- a/.resources/4bc725abbffede84edf5fd949e9689c7ed78d612da2e1970e2c743ef5ef72101 +++ b/.resources/de7dea6b0bac354930e91556c1a0267062a02c23062fbd900539c777e3ef9fc2 @@ -12,7 +12,7 @@ } }, "params": { - "Tab_ID": 4, + "Tab_ID": 10, "Table": "Statistics" }, "propConfig": { @@ -1593,7 +1593,7 @@ { "appearance": { "font": { - "size": 10, + "size": 15, "weight": 500 }, "grid": { @@ -3084,7 +3084,7 @@ { "appearance": { "font": { - "size": 10, + "size": 15, "weight": 500 }, "grid": { @@ -68233,7 +68233,7 @@ "color": "", "horizontalCenter": "middle", "opacity": 1, - "rotation": 90, + "rotation": 60, "verticalCenter": "middle" }, "opposite": false @@ -68708,7 +68708,7 @@ "color": "", "horizontalCenter": "middle", "opacity": 1, - "rotation": 90, + "rotation": 60, "verticalCenter": "middle" }, "opposite": false @@ -68892,7 +68892,7 @@ "props.currentTabIndex": { "onChange": { "enabled": null, - "script": "\tdropdown \u003d self.parent.getChild(\"Aggregation_Mode\").getChild(\"Dropdown_Aggregation_mode\")\n\tval \u003d currentValue.value\n\t\n\t# Default options\n\toptions \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"},\n\t {\"value\": \"Rate\", \"label\": \"Rate\"}\n\t]\n\tvalue \u003d \"Count\"\n\t\n\t# Customize options based on current value\n\tif val in (4, 5, 6, 7, 9):\n\t options \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"}\n\t ]\n\t\n\t# Apply changes\n\tdropdown.props.options \u003d options\n\tdropdown.props.value \u003d value" + "script": "\tdropdown \u003d self.parent.getChild(\"Aggregation_Mode\").getChild(\"Dropdown_Aggregation_mode\")\n\tval \u003d currentValue.value\n\t\n\t# Default options\n\toptions \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"},\n\t {\"value\": \"Rate\", \"label\": \"Rate\"}\n\t]\n\tvalue \u003d \"Count\"\n\t\n\t# Customize options based on current value\n\tif val in (4, 5, 6, 7, 9, 10):\n\t options \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"}\n\t ]\n\t\n\t# Apply changes\n\tdropdown.props.options \u003d options\n\tdropdown.props.value \u003d value" } }, "props.tabs": { @@ -68900,7 +68900,7 @@ } }, "props": { - "currentTabIndex": 4, + "currentTabIndex": 10, "menuStyle": { "backgroundColor": "#FFFFFFBD", "fontSize": "1.0vmin", @@ -68990,7 +68990,7 @@ "style": { "fontSize": "1.5vmin" }, - "value": "Count" + "value": "Percentage" }, "type": "ia.input.dropdown" } @@ -69010,6 +69010,7 @@ "config": { "expression": "{../Statistics.props.currentTabIndex} !\u003d10" }, + "enabled": false, "type": "expr" } } @@ -72218,7 +72219,7 @@ "component": { "onActionPerformed": { "config": { - "script": "\tdef exportIfVisible(displayFlag, dataset, filename\u003d\"Export.csv\"):\n\t import system\n\t \n\t if not displayFlag:\n\t return\n\t \n\t if dataset is None or dataset.getRowCount() \u003d\u003d 0:\n\t return\n\t \n\t colNames \u003d list(dataset.getColumnNames())\n\t newRows \u003d []\n\t \n\t for rowIndex in range(dataset.getRowCount()):\n\t rowVals \u003d []\n\t for colName in colNames:\n\t val \u003d dataset.getValueAt(rowIndex, colName)\n\t \n\t # If the column name contains “perc” (case-insensitive), multiply by 100\n\t if \"perc\" in colName.lower() and val is not None:\n\t try:\n\t val \u003d float(val) * 100\n\t except Exception:\n\t pass\n\t \n\t # Format Startstamp/Endtstamp if needed (you already have this logic)\n\t if colName in (\"Startstamp\", \"Endtstamp\") and val is not None:\n\t try:\n\t if isinstance(val, (int, long, float)):\n\t dt \u003d system.date.fromMillis(val)\n\t else:\n\t dt \u003d val\n\t val \u003d system.date.format(dt, \"yyyy-MM-dd HH:mm:ss\")\n\t except Exception:\n\t pass\n\t \n\t rowVals.append(val)\n\t newRows.append(rowVals)\n\t \n\t formattedDs \u003d system.dataset.toDataSet(colNames, newRows)\n\t csvString \u003d system.dataset.toCSV(formattedDs)\n\t system.perspective.download(filename, csvString, \"Comma Separated Values\")\n\t# Read the current tab index property\n\tstats \u003d self.parent.getChild(\"Statistics\")\n\tcurrentTabIndex \u003d stats.props.currentTabIndex\n\t\n\t# Induct Details\n\tinductDetails \u003d stats.getChild(\"Induct Details\")\n\tcountView \u003d inductDetails.getChild(\"Induct Details Count\")\n\tpercView \u003d inductDetails.getChild(\"Induct Details Perc\")\n\trateView \u003d inductDetails.getChild(\"Induct Details Rate\")\n\t\n\t# Scanner Details\n\tscannerDetails \u003d stats.getChild(\"Scanner_Details\")\n\tscannerCountView \u003d scannerDetails.getChild(\"Scanner Details\")\n\tscannerPercentageView \u003d scannerDetails.getChild(\"Scanner Details Perc\")\n\tscannerRateView \u003d scannerDetails.getChild(\"Scanner Details Rate\")\n\t\n\t# Sorter Details\n\tsorterDetails \u003d stats.getChild(\"Sorter Details\")\n\tsorterCountView \u003d sorterDetails.getChild(\"Sorter Details\")\n\tsorterPercentageView \u003d sorterDetails.getChild(\"Sorter Details Perc\")\n\tsorterRateView \u003d sorterDetails.getChild(\"Sorter Details Rate\")\n\t\n\t# Lane Details\n\tlaneDetails \u003d stats.getChild(\"Lane Details\")\n\tlaneCountView \u003d laneDetails.getChild(\"Lane Details\")\n\tlanePercView \u003d laneDetails.getChild(\"Lane Details Perc\")\n\tlaneRateView \u003d laneDetails.getChild(\"Lane Details Rate\")\n\t\n\t# Hourly Induct\n\thourlyInduct \u003d stats.getChild(\"Hourly_Induct\")\n\thourlyInductCountView \u003d hourlyInduct.getChild(\"Hourly Induct Count\")\n\thourlyInductPercView \u003d hourlyInduct.getChild(\"Hourly Induct Perc\")\n\t\n\t# Hourly Scanner\n\thourlyScanner \u003d stats.getChild(\"Hourly_Scanner\")\n\thourlyScannerCountView \u003d hourlyScanner.getChild(\"Hourly Scanner Count\")\n\thourlyScannerPercView \u003d hourlyScanner.getChild(\"Hourly Scanner Perc\")\n\thourlyScannerRateView \u003d hourlyScanner.getChild(\"Hourly Scanner Rate\")\n\t\n\t# Hourly Sorter Details\n\thourlySorterDetails \u003d stats.getChild(\"Hourly_Sorter_Details\")\n\thourlySorterDetailsCountView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Count\")\n\thourlySorterDetailsPercView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Perc\")\n\thourlySorterDetailsRateView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Rate\")\n\t\n\t# Hourly Lane\n\thourlyLane \u003d stats.getChild(\"Hourly_Lane\")\n\thourlyLaneCountView \u003d hourlyLane.getChild(\"Hourly Lane Count\")\n\thourlyLanePercView \u003d hourlyLane.getChild(\"Hourly Lane Perc\")\n\thourlyLaneRateView \u003d hourlyLane.getChild(\"Hourly Lane Rate\")\n\t\n\t# Lane Total Full\n\tlaneTotalFull \u003d stats.getChild(\"Total Full\")\n\tlaneTotalFullCountView \u003d laneTotalFull.getChild(\"Total Full Count\")\n\tlaneTotalFullPercView \u003d laneTotalFull.getChild(\"Total Full Perc\")\n\tlaneTotalFullRateView \u003d laneTotalFull.getChild(\"Total Full Rate\")\n\t\t\n\t# Jam By Area\n\tjamByArea \u003d stats.getChild(\"Jam Area\")\n\tjamByAreaCountView \u003d jamByArea.getChild(\"Jam Area Count\")\n\tjamByArealPercView \u003d jamByArea.getChild(\"Jam Area Percentage\")\n\t\t\n\t# Daily Jam Frequency\n\tdailyJamFrequency \u003d stats.getChild(\"Daily Jam Frequency\")\n\tdailyJamFrequencyCountView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Count\")\n\tdailyJamFrequencyPercView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Perc\")\n\n\tif currentTabIndex \u003d\u003d 0:\n\t\texportIfVisible(rateView.position.display, rateView.props.data, \"InductDetails_Rate.csv\")\n\t\texportIfVisible(percView.position.display, percView.props.data, \"InductDetails_Percentage.csv\")\n\t\texportIfVisible(countView.position.display, countView.props.data, \"InductDetails_Count.csv\")\n\telif currentTabIndex \u003d\u003d 1:\n\t\texportIfVisible(scannerCountView.position.display, scannerCountView.props.data, \"ScannerDetails_Count.csv\")\n\t\texportIfVisible(scannerPercentageView.position.display, scannerPercentageView.props.data, \"ScannerDetails_Percentage.csv\")\n\t\texportIfVisible(scannerRateView.position.display, scannerRateView.props.data, \"ScannerDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 2:\n\t\texportIfVisible(sorterCountView.position.display, sorterCountView.props.data, \"SorterDetails_Count.csv\")\n\t\texportIfVisible(sorterPercentageView.position.display, sorterPercentageView.props.data, \"SorterDetails_Percentage.csv\")\n\t\texportIfVisible(sorterRateView.position.display, sorterRateView.props.data, \"SorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 3:\n\t\texportIfVisible(laneCountView.position.display, laneCountView.props.data, \"LaneDetails_Count.csv\")\n\t\texportIfVisible(lanePercView.position.display, lanePercView.props.data, \"LaneDetails_Percentage.csv\")\n\t\texportIfVisible(laneRateView.position.display, laneRateView.props.data, \"LaneDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 4:\n\t\texportIfVisible(hourlyInductCountView.position.display, hourlyInductCountView.props.data, \"HourlyInduct_Count.csv\")\n\t\texportIfVisible(hourlyInductPercView.position.display, hourlyInductPercView.props.data, \"HourlyInduct_Perc.csv\")\n\telif currentTabIndex \u003d\u003d 5:\n\t\texportIfVisible(hourlyScannerCountView.position.display, hourlyScannerCountView.props.data, \"HourlyScanner_Count.csv\")\n\t\texportIfVisible(hourlyScannerPercView.position.display, hourlyScannerPercView.props.data, \"HourlyScanner_Percentage.csv\")\n\t\texportIfVisible(hourlyScannerRateView.position.display, hourlyScannerRateView.props.data, \"HourlyScanner_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 6:\n\t\texportIfVisible(hourlySorterDetailsCountView.position.display, hourlySorterDetailsCountView.props.data, \"HourlySorterDetails_Count.csv\")\n\t\texportIfVisible(hourlySorterDetailsPercView.position.display, hourlySorterDetailsPercView.props.data, \"HourlySorterDetails_Percentage.csv\")\n\t\texportIfVisible(hourlySorterDetailsRateView.position.display, hourlySorterDetailsRateView.props.data, \"HourlySorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 7:\n\t\texportIfVisible(hourlyLaneCountView.position.display, hourlyLaneCountView.props.data, \"HourlyLane_Count.csv\")\n\t\texportIfVisible(hourlyLanePercView.position.display, hourlyLanePercView.props.data, \"HourlyLane_Percentage.csv\")\n\t\texportIfVisible(hourlyLaneRateView.position.display, hourlyLaneRateView.props.data, \"HourlyLane_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 8:\n\t\texportIfVisible(laneTotalFullCountView.position.display, laneTotalFullCountView.props.data, \"LaneTotalFull_Count.csv\")\n\t\texportIfVisible(laneTotalFullPercView.position.display, laneTotalFullPercView.props.data, \"LaneTotalFull_Percentage.csv\")\n\t\texportIfVisible(laneTotalFullRateView.position.display, laneTotalFullRateView.props.data, \"LaneTotalFull_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 9:\n\t\texportIfVisible(jamByAreaCountView.position.display, jamByAreaCountView.props.data, \"JamByArea_Count.csv\")\n\t\texportIfVisible(jamByArealPercView.position.display, jamByArealPercView.props.data, \"JamByArea_Percentage.csv\")\n\telif currentTabIndex \u003d\u003d 10:\n\t\texportIfVisible(dailyJamFrequencyCountView.position.display, dailyJamFrequencyCountView.props.dataSources.example, \"DailyJamFrequency_Count.csv\")\n\t\texportIfVisible(dailyJamFrequencyPercView.position.display, dailyJamFrequencyPercView.props.dataSources.example, \"DailyJamFrequency_Percentage.csv\")\n\t\texportIfVisible(dailyJamFrequencyRateView.position.display, dailyJamFrequencyRateView.props.dataSources.example, \"DailyJamFrequency_Rate.csv\")\n\telse:\n\t\tpass" + "script": "\tdef exportIfVisible(displayFlag, dataset, filename\u003d\"Export.csv\", multiply \u003d True):\n\t import system\n\t \n\t if not displayFlag:\n\t return\n\t \n\t if dataset is None or dataset.getRowCount() \u003d\u003d 0:\n\t return\n\t \n\t colNames \u003d list(dataset.getColumnNames())\n\t newRows \u003d []\n\t \n\t for rowIndex in range(dataset.getRowCount()):\n\t rowVals \u003d []\n\t for colName in colNames:\n\t val \u003d dataset.getValueAt(rowIndex, colName)\n\t \n\t # If the column name contains “perc” (case-insensitive), multiply by 100\n\t if \"perc\" in colName.lower() and val is not None:\n\t try: \t\n\t if multiply:\n\t \t val \u003d float(val) * 100\n\t \n\t except Exception:\n\t pass\n\t \n\t # Format Startstamp/Endtstamp if needed (you already have this logic)\n\t if colName in (\"Startstamp\", \"Endtstamp\") and val is not None:\n\t try:\n\t if isinstance(val, (int, long, float)):\n\t dt \u003d system.date.fromMillis(val)\n\t else:\n\t dt \u003d val\n\t val \u003d system.date.format(dt, \"yyyy-MM-dd HH:mm:ss\")\n\t except Exception:\n\t pass\n\t \n\t rowVals.append(val)\n\t newRows.append(rowVals)\n\t \n\t formattedDs \u003d system.dataset.toDataSet(colNames, newRows)\n\t csvString \u003d system.dataset.toCSV(formattedDs)\n\t system.perspective.download(filename, csvString, \"Comma Separated Values\")\n\t# Read the current tab index property\n\tstats \u003d self.parent.getChild(\"Statistics\")\n\tcurrentTabIndex \u003d stats.props.currentTabIndex\n\t\n\t# Induct Details\n\tinductDetails \u003d stats.getChild(\"Induct Details\")\n\tcountView \u003d inductDetails.getChild(\"Induct Details Count\")\n\tpercView \u003d inductDetails.getChild(\"Induct Details Perc\")\n\trateView \u003d inductDetails.getChild(\"Induct Details Rate\")\n\t\n\t# Scanner Details\n\tscannerDetails \u003d stats.getChild(\"Scanner_Details\")\n\tscannerCountView \u003d scannerDetails.getChild(\"Scanner Details\")\n\tscannerPercentageView \u003d scannerDetails.getChild(\"Scanner Details Perc\")\n\tscannerRateView \u003d scannerDetails.getChild(\"Scanner Details Rate\")\n\t\n\t# Sorter Details\n\tsorterDetails \u003d stats.getChild(\"Sorter Details\")\n\tsorterCountView \u003d sorterDetails.getChild(\"Sorter Details\")\n\tsorterPercentageView \u003d sorterDetails.getChild(\"Sorter Details Perc\")\n\tsorterRateView \u003d sorterDetails.getChild(\"Sorter Details Rate\")\n\t\n\t# Lane Details\n\tlaneDetails \u003d stats.getChild(\"Lane Details\")\n\tlaneCountView \u003d laneDetails.getChild(\"Lane Details\")\n\tlanePercView \u003d laneDetails.getChild(\"Lane Details Perc\")\n\tlaneRateView \u003d laneDetails.getChild(\"Lane Details Rate\")\n\t\n\t# Hourly Induct\n\thourlyInduct \u003d stats.getChild(\"Hourly_Induct\")\n\thourlyInductCountView \u003d hourlyInduct.getChild(\"Hourly Induct Count\")\n\thourlyInductPercView \u003d hourlyInduct.getChild(\"Hourly Induct Perc\")\n\t\n\t# Hourly Scanner\n\thourlyScanner \u003d stats.getChild(\"Hourly_Scanner\")\n\thourlyScannerCountView \u003d hourlyScanner.getChild(\"Hourly Scanner Count\")\n\thourlyScannerPercView \u003d hourlyScanner.getChild(\"Hourly Scanner Perc\")\n\thourlyScannerRateView \u003d hourlyScanner.getChild(\"Hourly Scanner Rate\")\n\t\n\t# Hourly Sorter Details\n\thourlySorterDetails \u003d stats.getChild(\"Hourly_Sorter_Details\")\n\thourlySorterDetailsCountView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Count\")\n\thourlySorterDetailsPercView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Perc\")\n\thourlySorterDetailsRateView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Rate\")\n\t\n\t# Hourly Lane\n\thourlyLane \u003d stats.getChild(\"Hourly_Lane\")\n\thourlyLaneCountView \u003d hourlyLane.getChild(\"Hourly Lane Count\")\n\thourlyLanePercView \u003d hourlyLane.getChild(\"Hourly Lane Perc\")\n\thourlyLaneRateView \u003d hourlyLane.getChild(\"Hourly Lane Rate\")\n\t\n\t# Lane Total Full\n\tlaneTotalFull \u003d stats.getChild(\"Total Full\")\n\tlaneTotalFullCountView \u003d laneTotalFull.getChild(\"Total Full Count\")\n\tlaneTotalFullPercView \u003d laneTotalFull.getChild(\"Total Full Perc\")\n\tlaneTotalFullRateView \u003d laneTotalFull.getChild(\"Total Full Rate\")\n\t\t\n\t# Jam By Area\n\tjamByArea \u003d stats.getChild(\"Jam Area\")\n\tjamByAreaCountView \u003d jamByArea.getChild(\"Jam Area Count\")\n\tjamByArealPercView \u003d jamByArea.getChild(\"Jam Area Percentage\")\n\t\t\n\t# Daily Jam Frequency\n\tdailyJamFrequency \u003d stats.getChild(\"Daily Jam Frequency\")\n\tdailyJamFrequencyCountView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Count\")\n\tdailyJamFrequencyPercView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Perc\")\n\n\tif currentTabIndex \u003d\u003d 0:\n\t\texportIfVisible(rateView.position.display, rateView.props.data, \"InductDetails_Rate.csv\")\n\t\texportIfVisible(percView.position.display, percView.props.data, \"InductDetails_Percentage.csv\")\n\t\texportIfVisible(countView.position.display, countView.props.data, \"InductDetails_Count.csv\")\n\telif currentTabIndex \u003d\u003d 1:\n\t\texportIfVisible(scannerCountView.position.display, scannerCountView.props.data, \"ScannerDetails_Count.csv\")\n\t\texportIfVisible(scannerPercentageView.position.display, scannerPercentageView.props.data, \"ScannerDetails_Percentage.csv\")\n\t\texportIfVisible(scannerRateView.position.display, scannerRateView.props.data, \"ScannerDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 2:\n\t\texportIfVisible(sorterCountView.position.display, sorterCountView.props.data, \"SorterDetails_Count.csv\")\n\t\texportIfVisible(sorterPercentageView.position.display, sorterPercentageView.props.data, \"SorterDetails_Percentage.csv\")\n\t\texportIfVisible(sorterRateView.position.display, sorterRateView.props.data, \"SorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 3:\n\t\texportIfVisible(laneCountView.position.display, laneCountView.props.data, \"LaneDetails_Count.csv\")\n\t\texportIfVisible(lanePercView.position.display, lanePercView.props.data, \"LaneDetails_Percentage.csv\")\n\t\texportIfVisible(laneRateView.position.display, laneRateView.props.data, \"LaneDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 4:\n\t\texportIfVisible(hourlyInductCountView.position.display, hourlyInductCountView.props.data, \"HourlyInduct_Count.csv\")\n\t\texportIfVisible(hourlyInductPercView.position.display, hourlyInductPercView.props.data, \"HourlyInduct_Perc.csv\")\n\telif currentTabIndex \u003d\u003d 5:\n\t\texportIfVisible(hourlyScannerCountView.position.display, hourlyScannerCountView.props.data, \"HourlyScanner_Count.csv\")\n\t\texportIfVisible(hourlyScannerPercView.position.display, hourlyScannerPercView.props.data, \"HourlyScanner_Percentage.csv\")\n\t\texportIfVisible(hourlyScannerRateView.position.display, hourlyScannerRateView.props.data, \"HourlyScanner_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 6:\n\t\texportIfVisible(hourlySorterDetailsCountView.position.display, hourlySorterDetailsCountView.props.data, \"HourlySorterDetails_Count.csv\")\n\t\texportIfVisible(hourlySorterDetailsPercView.position.display, hourlySorterDetailsPercView.props.data, \"HourlySorterDetails_Percentage.csv\")\n\t\texportIfVisible(hourlySorterDetailsRateView.position.display, hourlySorterDetailsRateView.props.data, \"HourlySorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 7:\n\t\texportIfVisible(hourlyLaneCountView.position.display, hourlyLaneCountView.props.data, \"HourlyLane_Count.csv\")\n\t\texportIfVisible(hourlyLanePercView.position.display, hourlyLanePercView.props.data, \"HourlyLane_Percentage.csv\")\n\t\texportIfVisible(hourlyLaneRateView.position.display, hourlyLaneRateView.props.data, \"HourlyLane_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 8:\n\t\texportIfVisible(laneTotalFullCountView.position.display, laneTotalFullCountView.props.data, \"LaneTotalFull_Count.csv\")\n\t\texportIfVisible(laneTotalFullPercView.position.display, laneTotalFullPercView.props.data, \"LaneTotalFull_Percentage.csv\")\n\t\texportIfVisible(laneTotalFullRateView.position.display, laneTotalFullRateView.props.data, \"LaneTotalFull_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 9:\n\t\texportIfVisible(jamByAreaCountView.position.display, jamByAreaCountView.props.data, \"JamByArea_Count.csv\")\n\t\texportIfVisible(jamByArealPercView.position.display, jamByArealPercView.props.data, \"JamByArea_Percentage.csv\")\n\telif currentTabIndex \u003d\u003d 10:\n\t\texportIfVisible(dailyJamFrequencyCountView.position.display, dailyJamFrequencyCountView.props.dataSources.example, \"DailyJamFrequency_Count.csv\")\n\t\texportIfVisible(dailyJamFrequencyPercView.position.display, dailyJamFrequencyPercView.props.dataSources.example, \"DailyJamFrequency_Percentage.csv\", False)\n\telse:\n\t\tpass" }, "scope": "G", "type": "script" @@ -74126,7 +74127,7 @@ "component": { "onActionPerformed": { "config": { - "script": "\t# Ignition Script for Excel Export Button\n\t\n\timport datetime\n\tfrom java.io import ByteArrayOutputStream\n\tfrom org.apache.poi.ss.usermodel import IndexedColors, FillPatternType, BorderStyle, HorizontalAlignment, VerticalAlignment\n\tfrom org.apache.poi.xssf.usermodel import XSSFWorkbook\n\tfrom org.apache.poi.ss.util import CellRangeAddress\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATE/TIME FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef calculate_report_period():\n\t \"\"\"\n\t Use custom StartDate and EndDate (java.util.Date objects)\n\t and show full timestamps in the \u0027Period\u0027 field.\n\t \"\"\"\n\t import datetime\n\t\n\t # Get java.util.Date objects\n\t start_java \u003d self.getSibling(\"Period_not_Global_0\").custom.StartDate\n\t end_java \u003d self.getSibling(\"Period_not_Global_0\").custom.EndDate\n\t\n\t # Convert java.util.Date → Python datetime\n\t start_dt \u003d datetime.datetime.fromtimestamp(start_java.getTime() / 1000.0)\n\t end_dt \u003d datetime.datetime.fromtimestamp(end_java.getTime() / 1000.0)\n\t\n\t # Format report date (same as always)\n\t formatted_date \u003d datetime.datetime.now().strftime(\u0027%m/%d/%Y\u0027)\n\t\n\t # FULL timestamp format, matching Perspective display\n\t # Example: \"Nov 20, 2025 12:34 PM\"\n\t start_period \u003d start_dt.strftime(\u0027%b %d, %Y %I:%M %p\u0027)\n\t end_period \u003d end_dt.strftime(\u0027%b %d, %Y %I:%M %p\u0027)\n\t\n\t formatted_period \u003d start_period + \" to \" + end_period\n\t\n\t # Convert timestamps for SQL purposes\n\t start_str \u003d start_dt.strftime(\u0027%Y-%m-%d %H:%M:%S\u0027)\n\t end_str \u003d end_dt.strftime(\u0027%Y-%m-%d %H:%M:%S\u0027)\n\t\n\t return start_str, end_str, formatted_date, formatted_period\n\n\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATABASE FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef build_sorter_query(start_time, end_time):\n\t\t\"\"\"\n\t\tBuild the SQL query to fetch sorter metrics.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp in format \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\tend_time (str): End timestamp in format \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\t\n\t\tReturns:\n\t\t\tstr: SQL query string\n\t\t\"\"\"\n\t\tquery \u003d \"\"\"\n\t\tSELECT\n\t\t\tCOUNT(*) AS total_scans,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 0 THEN 1 ELSE 0 END) AS good_diverts,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 4 THEN 1 ELSE 0 END) AS gap_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 21 THEN 1 ELSE 0 END) AS unable_to_divert,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 14 THEN 1 ELSE 0 END) AS failed_to_divert,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 1 THEN 1 ELSE 0 END) AS unknown,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 17 THEN 1 ELSE 0 END) AS lost_container,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 22 THEN 1 ELSE 0 END) AS dest_not_attempted,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 5 THEN 1 ELSE 0 END) AS dest_full,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 12 THEN 1 ELSE 0 END) AS dest_disabled,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 6 THEN 1 ELSE 0 END) AS dest_non_operational,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 2 THEN 1 ELSE 0 END) AS unexpected,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 16 THEN 1 ELSE 0 END) AS no_dest,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 7 THEN 1 ELSE 0 END) AS dest_invalid,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 3 THEN 1 ELSE 0 END) AS tracking_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 13 THEN 1 ELSE 0 END) AS throughput_limit,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 18 THEN 1 ELSE 0 END) AS dimension_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 19 THEN 1 ELSE 0 END) AS weight_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 20 THEN 1 ELSE 0 END) AS container_utilization,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 IN (8, 9, 10) THEN 1 ELSE 0 END) AS scan_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 8 THEN 1 ELSE 0 END) AS no_reads,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 10 THEN 1 ELSE 0 END) AS multi_reads,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 9 THEN 1 ELSE 0 END) AS no_codes\n\t\tFROM item_data\n\t\tWHERE t_stamp \u003e\u003d \u0027%s\u0027 AND t_stamp \u003c \u0027%s\u0027\n\t\t\tAND adiSort_Code_0 NOT IN (11, 15)\n\t\t\tAND sLocation_ID LIKE \u0027%%S03%%\u0027\n\t\t\"\"\" % (start_time, end_time)\n\t\treturn query\n\t\n\t\n\tdef fetch_sorter_metrics(start_time, end_time):\n\t\t\"\"\"\n\t\tExecute the sorter metrics query and return results.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\ttuple: Database row with metrics\n\t\t\"\"\"\n\t\tquery \u003d build_sorter_query(start_time, end_time)\n\t\tdb_result \u003d system.db.runQuery(query)\n\t\treturn db_result[0]\n\t\n\tdef build_alarms_query(start_time, end_time):\n\t\t\"\"\"\n\t\tBuild the SQL query to fetch high-priority alarms.\n\t\t\"\"\"\n\t\tquery \u003d \"\"\"\n\t\tSELECT\n\t\t\tMIN(ae.eventtime) AS FirstTimestamp,\n\t\t\tMAX(ae.eventtime) AS LastTimestamp,\n\t\t\tCOUNT(*) AS ActivationCount,\n\t\t\tTIME_FORMAT(\n\t\t\t\tSEC_TO_TIME(\n\t\t\t\t\tSUM(\n\t\t\t\t\t\tTIMESTAMPDIFF(SECOND, \n\t\t\t\t\t\t\tae.eventtime,\n\t\t\t\t\t\t\t(SELECT MIN(clr.eventtime) \n\t\t\t\t\t\t\t FROM alarm_events clr USE INDEX (idx_alarm_events_eventid)\n\t\t\t\t\t\t\t WHERE clr.eventid \u003d ae.eventid \n\t\t\t\t\t\t\t AND clr.eventtype IN (1, 2)\n\t\t\t\t\t\t\t AND clr.id \u003e ae.id)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t),\n\t\t\t\t\u0027%%H:%%i:%%s\u0027\n\t\t\t) AS Duration,\n\t\t\t\u0027High\u0027 AS Priority,\n\t\t\tIFNULL(loc.strValue, \u0027\u0027) AS Location,\n\t\t\tCONCAT(\n\t\t\t\tIFNULL(ae.displaypath, \u0027Unknown\u0027), \u0027 - \u0027,\n\t\t\t\tSUBSTRING_INDEX(IFNULL(ae.source, \u0027\u0027), \u0027:/alm:\u0027, -1)\n\t\t\t) AS Description,\n\t\t\tIFNULL(tag.strValue, \u0027\u0027) AS Tag\n\t\tFROM alarm_events ae FORCE INDEX (idx_alarm_events_grouping)\n\t\tLEFT JOIN alarm_event_data tag FORCE INDEX (idx_alarm_event_data_lookup)\n\t\t\tON tag.id \u003d ae.id AND tag.propname \u003d \u0027myTag\u0027\n\t\tLEFT JOIN alarm_event_data loc FORCE INDEX (idx_alarm_event_data_lookup)\n\t\t\tON loc.id \u003d ae.id AND loc.propname \u003d \u0027myLocation\u0027\n\t\tWHERE \n\t\t\tae.eventtype \u003d 0\n\t\t\tAND ae.priority \u003d 3\n\t\t\tAND ae.displaypath NOT LIKE \u0027%%System Startup%%\u0027\n\t\t\tAND ae.source NOT LIKE \u0027%%System Startup%%\u0027\n\t\t\tAND ae.displaypath NOT LIKE \u0027%%System Shutdown%%\u0027\n\t\t\tAND ae.source NOT LIKE \u0027%%System Shutdown%%\u0027\n\t\t\tAND ae.eventtime \u003e\u003d \u0027%s\u0027\n\t\t\tAND ae.eventtime \u003c\u003d \u0027%s\u0027\n\t\t\tAND loc.strValue IN (\u0027MCM01\u0027, \u0027MCM02\u0027)\n\t\t\tAND LOWER(\n\t\t\t\tCONCAT(\n\t\t\t\t\tIFNULL(ae.displaypath,\u0027\u0027),\n\t\t\t\t\tIFNULL(ae.source,\u0027\u0027),\n\t\t\t\t\tIFNULL(tag.strValue,\u0027\u0027)\n\t\t\t\t)\n\t\t\t) LIKE \u0027%%jam%%\u0027\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT 1 \n\t\t\t\tFROM alarm_events clr USE INDEX (idx_alarm_events_eventid)\n\t\t\t\tWHERE clr.eventid \u003d ae.eventid \n\t\t\t\t AND clr.eventtype IN (1, 2)\n\t\t\t\t AND clr.id \u003e ae.id\n\t\t\t\tLIMIT 1\n\t\t\t)\n\t\tGROUP BY\n\t\t\tae.source, \n\t\t\tae.displaypath, \n\t\t\tae.priority,\n\t\t\ttag.strValue\n\t\tORDER BY\n\t\t\tActivationCount DESC\n\t\t\"\"\" % (start_time, end_time)\n\t\treturn query\n\n\n\n\t\n\t\n\tdef fetch_high_priority_alarms(start_time, end_time):\n\t\t\"\"\"\n\t\tExecute the alarm query and return results.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\tlist: List of alarm data rows\n\t\t\"\"\"\n\t\tquery \u003d build_alarms_query(start_time, end_time)\n\t\tdb_result \u003d system.db.runQuery(query)\n\t\treturn db_result\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATA TRANSFORMATION FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef transform_metrics_to_report_data(db_row):\n\t\t\"\"\"\n\t\tTransform database metrics into structured report data.\n\t\t\n\t\tArgs:\n\t\t\tdb_row (tuple): Database row containing 24 metrics from query\n\t\t\t\n\t\tReturns:\n\t\t\tlist: List of [section, metric, count, percentage] rows\n\t\t\"\"\"\n\t\t# Calculate total for percentages (avoid division by zero)\n\t\ttotal \u003d db_row[0] if db_row[0] else 1\n\t\t\n\t\t# Calculate good reads: total - no_reads - multi_reads - no_codes\n\t\tgood_reads \u003d (db_row[0] or 0) - (db_row[20] or 0) - (db_row[21] or 0) - (db_row[22] or 0)\n\t\t\n\t\t# Build data structure with sections, metrics, counts, and percentages\n\t\tdata \u003d [\n\t\t\t# Inducted Section\n\t\t\t[\u0027Inducted\u0027, \u0027Total Scans\u0027, db_row[0], None],\n\t\t\t[\u0027Inducted\u0027, \u0027Good Diverts\u0027, db_row[1], round(db_row[1] * 100.0 / total, 2) if db_row[1] else 0],\n\t\t\t\n\t\t\t# Sorter Performance Section (18 metrics)\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Gap Error\u0027, db_row[2], round(db_row[2] * 100.0 / total, 2) if db_row[2] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unable to Divert\u0027, db_row[3], round(db_row[3] * 100.0 / total, 2) if db_row[3] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Failed to Divert\u0027, db_row[4], round(db_row[4] * 100.0 / total, 2) if db_row[4] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unknown\u0027, db_row[5], round(db_row[5] * 100.0 / total, 2) if db_row[5] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Lost Container\u0027, db_row[6], round(db_row[6] * 100.0 / total, 2) if db_row[6] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Not Attempted\u0027, db_row[7], round(db_row[7] * 100.0 / total, 2) if db_row[7] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Full\u0027, db_row[8], round(db_row[8] * 100.0 / total, 2) if db_row[8] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Disabled\u0027, db_row[9], round(db_row[9] * 100.0 / total, 2) if db_row[9] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Non Operational\u0027, db_row[10], round(db_row[10] * 100.0 / total, 2) if db_row[10] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unexpected\u0027, db_row[11], round(db_row[11] * 100.0 / total, 2) if db_row[11] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027No Dest\u0027, db_row[12], round(db_row[12] * 100.0 / total, 2) if db_row[12] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Invalid\u0027, db_row[13], round(db_row[13] * 100.0 / total, 2) if db_row[13] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Tracking Error\u0027, db_row[14], round(db_row[14] * 100.0 / total, 2) if db_row[14] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Throughput Limit\u0027, db_row[15], round(db_row[15] * 100.0 / total, 2) if db_row[15] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dimension Error\u0027, db_row[16], round(db_row[16] * 100.0 / total, 2) if db_row[16] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Weight Error\u0027, db_row[17], round(db_row[17] * 100.0 / total, 2) if db_row[17] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Container Utilization\u0027, db_row[18], round(db_row[18] * 100.0 / total, 2) if db_row[18] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Scan Error\u0027, db_row[19], round(db_row[19] * 100.0 / total, 2) if db_row[19] else 0],\n\t\t\t\n\t\t\t# Scanner Health Section (4 metrics)\n\t\t\t[\u0027Scanner Health\u0027, \u0027Good Reads\u0027, good_reads, round(good_reads * 100.0 / total, 2) if good_reads else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027No Reads\u0027, db_row[20], round(db_row[20] * 100.0 / total, 2) if db_row[20] else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027Multi Reads\u0027, db_row[21], round(db_row[21] * 100.0 / total, 2) if db_row[21] else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027No Codes\u0027, db_row[22], round(db_row[22] * 100.0 / total, 2) if db_row[22] else 0]\n\t\t]\n\t\t\n\t\treturn data\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# EXCEL STYLING FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef create_cell_style(wb, h_align\u003dNone, bold\u003dFalse, size\u003d11, bg_color\u003dNone, borders\u003dNone, fmt\u003dNone):\n\t\t\"\"\"\n\t\tCreate and return a cell style with the specified formatting.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\th_align (str): Horizontal alignment (\u0027center\u0027, \u0027right\u0027, \u0027left\u0027)\n\t\t\tbold (bool): Bold font\n\t\t\tsize (int): Font size in points\n\t\t\tbg_color (str): Background color (\u0027yellow\u0027)\n\t\t\tborders (dict): Border specification {\u0027L\u0027: \u0027M\u0027/\u0027T\u0027, \u0027R\u0027: \u0027M\u0027/\u0027T\u0027, \u0027T\u0027: \u0027M\u0027/\u0027T\u0027, \u0027B\u0027: \u0027M\u0027/\u0027T\u0027}\n\t\t\tfmt (str): Number format string\n\t\t\t\n\t\tReturns:\n\t\t\tCellStyle: Configured cell style\n\t\t\"\"\"\n\t\tstyle \u003d wb.createCellStyle()\n\t\t\n\t\t# Font\n\t\tfont \u003d wb.createFont()\n\t\tfont.setFontName(\u0027Aptos Narrow\u0027)\n\t\tfont.setFontHeightInPoints(size)\n\t\tfont.setBold(bold)\n\t\tstyle.setFont(font)\n\t\t\n\t\t# Alignment\n\t\tif h_align \u003d\u003d \u0027center\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.CENTER)\n\t\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER)\n\t\telif h_align \u003d\u003d \u0027right\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.RIGHT)\n\t\telif h_align \u003d\u003d \u0027left\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.LEFT)\n\t\t\n\t\t# Background color\n\t\tif bg_color \u003d\u003d \u0027yellow\u0027:\n\t\t\tstyle.setFillForegroundColor(IndexedColors.GOLD.getIndex())\n\t\t\tstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND)\n\t\t\n\t\t# Borders\n\t\tif borders:\n\t\t\tif \u0027L\u0027 in borders:\n\t\t\t\tstyle.setBorderLeft(BorderStyle.MEDIUM if borders[\u0027L\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027R\u0027 in borders:\n\t\t\t\tstyle.setBorderRight(BorderStyle.MEDIUM if borders[\u0027R\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027T\u0027 in borders:\n\t\t\t\tstyle.setBorderTop(BorderStyle.MEDIUM if borders[\u0027T\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027B\u0027 in borders:\n\t\t\t\tstyle.setBorderBottom(BorderStyle.MEDIUM if borders[\u0027B\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\n\t\t# Number format\n\t\tif fmt:\n\t\t\tstyle.setDataFormat(wb.createDataFormat().getFormat(fmt))\n\t\t\n\t\treturn style\n\t\n\t\n\tdef set_cell(row, col_idx, wb, value\u003dNone, h_align\u003dNone, bold\u003dFalse, size\u003d11, bg_color\u003dNone, borders\u003dNone, fmt\u003dNone):\n\t\t\"\"\"\n\t\tCreate a cell and apply value and styling.\n\t\t\n\t\tArgs:\n\t\t\trow: Row object\n\t\t\tcol_idx (int): Column index\n\t\t\twb: Workbook object\n\t\t\tvalue: Cell value (int, float, or string)\n\t\t\th_align (str): Horizontal alignment\n\t\t\tbold (bool): Bold font\n\t\t\tsize (int): Font size\n\t\t\tbg_color (str): Background color\n\t\t\tborders (dict): Border specification\n\t\t\tfmt (str): Number format\n\t\t\t\n\t\tReturns:\n\t\t\tCell: Created and styled cell\n\t\t\"\"\"\n\t\tcell \u003d row.createCell(col_idx)\n\t\t\n\t\t# Set value based on type\n\t\tif value is not None:\n\t\t\tif isinstance(value, (int, long)):\n\t\t\t\tcell.setCellValue(int(value))\n\t\t\telif isinstance(value, float):\n\t\t\t\tcell.setCellValue(float(value))\n\t\t\telse:\n\t\t\t\tcell.setCellValue(str(value))\n\t\t\n\t\t# Apply style\n\t\tstyle \u003d create_cell_style(wb, h_align\u003dh_align, bold\u003dbold, size\u003dsize, bg_color\u003dbg_color, borders\u003dborders, fmt\u003dfmt)\n\t\tcell.setCellStyle(style)\n\t\t\n\t\treturn cell\n\t\n\t\n\tdef setup_worksheet(wb):\n\t\t\"\"\"\n\t\tCreate and configure the worksheet with proper settings and column widths.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\t\n\t\tReturns:\n\t\t\tWorksheet: Configured worksheet\n\t\t\"\"\"\n\t\tws \u003d wb.createSheet(\"Sorter Performance\")\n\t\t\n\t\t# Hide gridlines\n\t\tws.setDisplayGridlines(False)\n\t\t\n\t\t# Set column widths\n\t\tws.setColumnWidth(0, int(3.43 * 256))\n\t\tws.setColumnWidth(1, int(10.71 * 256))\n\t\tws.setColumnWidth(2, int(13.0 * 256))\n\t\tws.setColumnWidth(3, int(13.0 * 256))\n\t\tws.setColumnWidth(4, int(13.0 * 256))\n\t\tws.setColumnWidth(5, int(13.0 * 256))\n\t\tws.setColumnWidth(6, int(13.0 * 256))\n\t\t\n\t\treturn ws\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# EXCEL STRUCTURE FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef create_top_border_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the top border row for the report.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027, \u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027, \u0027T\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_title_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the title row with \"Sortation Report\" heading.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\trow.setHeightInPoints(26.25)\n\t\t\n\t\t# Create all cells first\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, \"Sortation Report\", h_align\u003d\u0027center\u0027, bold\u003dTrue, size\u003d20, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\n\t\t# Merge C3:F3 (columns 2-5)\n\t\tws.addMergedRegion(CellRangeAddress(row_idx, row_idx, 2, 5))\n\t\n\t\n\tdef create_spacing_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate a spacing row with only left and right borders.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_header_rows(ws, wb, start_row_idx, project, sorter, date, period):\n\t\t\"\"\"\n\t\tCreate the header rows with Project, Sorter, Date, and Period information.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\tstart_row_idx (int): Starting row index\n\t\t\tproject (str): Project name\n\t\t\tsorter (str): Sorter name\n\t\t\tdate (str): Report date\n\t\t\tperiod (str): Report period\n\t\t\"\"\"\n\t\theaders \u003d [\n\t\t\t(\u0027Project\u0027, project),\n\t\t\t(\u0027Sorter\u0027, sorter),\n\t\t\t(\u0027Date\u0027, date),\n\t\t\t(\u0027Period\u0027, period)\n\t\t]\n\t\t\n\t\tfor i, (label, value) in enumerate(headers):\n\t\t\trow_idx \u003d start_row_idx + i\n\t\t\trow \u003d ws.createRow(row_idx)\n\t\t\t\n\t\t\t# Create all cells first\n\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\tset_cell(row, 2, wb, label, h_align\u003d\u0027right\u0027, bold\u003dTrue, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 3, wb, value, h_align\u003d\u0027left\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 5, wb, borders\u003d{\u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\n\t\t\t# Merge D:F (columns 3-5)\n\t\t\tws.addMergedRegion(CellRangeAddress(row_idx, row_idx, 3, 5))\n\t\n\t\n\tdef create_data_rows(ws, wb, start_row_idx, data):\n\t\t\"\"\"\n\t\tCreate the data rows with sections and metrics.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\tstart_row_idx (int): Starting row index\n\t\t\tdata (list): List of [section, metric, count, percentage] rows\n\t\t\t\n\t\tReturns:\n\t\t\tint: Next available row index\n\t\t\"\"\"\n\t\tcurrent_row_idx \u003d start_row_idx\n\t\tcurrent_section \u003d None\n\t\t\n\t\tfor row_data in data:\n\t\t\tsection, metric, count, pct \u003d row_data[0], row_data[1], row_data[2], row_data[3]\n\t\t\t\n\t\t\t# Section header\n\t\t\tif section !\u003d current_section:\n\t\t\t\tcurrent_section \u003d section\n\t\t\t\trow \u003d ws.createRow(current_row_idx)\n\t\t\t\t\n\t\t\t\t# Create all cells first\n\t\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\t\tset_cell(row, 2, wb, section, h_align\u003d\u0027center\u0027, bold\u003dTrue, bg_color\u003d\u0027yellow\u0027)\n\t\t\t\tset_cell(row, 3, wb)\n\t\t\t\tset_cell(row, 4, wb)\n\t\t\t\tset_cell(row, 5, wb)\n\t\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\t\n\t\t\t\t# Merge section header\n\t\t\t\tws.addMergedRegion(CellRangeAddress(current_row_idx, current_row_idx, 2, 5))\n\t\t\t\tcurrent_row_idx +\u003d 1\n\t\t\t\n\t\t\t# Data row\n\t\t\trow \u003d ws.createRow(current_row_idx)\n\t\t\t\n\t\t\t# Create all cells first\n\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\tset_cell(row, 2, wb, metric, h_align\u003d\u0027right\u0027)\n\t\t\tset_cell(row, 3, wb)\n\t\t\tset_cell(row, 4, wb, count, h_align\u003d\u0027right\u0027)\n\t\t\t\n\t\t\t# Show percentage for Good Diverts (Inducted section) and all Sorter Performance/Scanner Health metrics\n\t\t\tif pct is not None and (section !\u003d \u0027Inducted\u0027 or metric \u003d\u003d \u0027Good Diverts\u0027):\n\t\t\t\tset_cell(row, 5, wb, float(pct) / 100.0, h_align\u003d\u0027right\u0027, fmt\u003d\u00270.00%\u0027)\n\t\t\telse:\n\t\t\t\tset_cell(row, 5, wb, h_align\u003d\u0027right\u0027, fmt\u003d\u00270.00%\u0027)\n\t\t\t\n\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\n\t\t\t# Merge C:D (columns 2-3)\n\t\t\tws.addMergedRegion(CellRangeAddress(current_row_idx, current_row_idx, 2, 3))\n\t\t\tcurrent_row_idx +\u003d 1\n\t\t\n\t\treturn current_row_idx\n\t\n\t\n\tdef create_bottom_border_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the bottom border row for the report.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\trow.setHeightInPoints(15.75)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027, \u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027, \u0027B\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_system_stability_sheet(wb, start_time, end_time):\n\t\t\"\"\"\n\t\tCreate the System Stability worksheet with alarm data.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\tWorksheet: Created and populated worksheet\n\t\t\"\"\"\n\t\t# Create worksheet\n\t\tws \u003d wb.createSheet(\"System Stability\")\n\t\tws.setDisplayGridlines(False)\n\t\t\n\t\t# Set column widths for 9 columns (A-I)\n\t\t# Column widths: First Timestamp, Last Timestamp, Activation Count, Duration, Priority, Location, Description, Tag, Comments/Actions Taken\n\t\tws.setColumnWidth(0, int(22 * 256)) # First Timestamp\n\t\tws.setColumnWidth(1, int(22 * 256)) # Last Timestamp\n\t\tws.setColumnWidth(2, int(20 * 256)) # Activation Count\n\t\tws.setColumnWidth(3, int(15 * 256)) # Duration\n\t\tws.setColumnWidth(4, int(15 * 256)) # Priority\n\t\tws.setColumnWidth(5, int(15 * 256)) # Location\n\t\tws.setColumnWidth(6, int(60 * 256)) # Description (doubled)\n\t\tws.setColumnWidth(7, int(60 * 256)) # Tag (doubled)\n\t\tws.setColumnWidth(8, int(25 * 256)) # Comments/Actions Taken\n\t\t\n\t\t# Create header row (row 0)\n\t\theader_row \u003d ws.createRow(0)\n\t\theaders \u003d [\n\t\t\t\u0027First Timestamp\u0027,\n\t\t\t\u0027Last Timestamp\u0027,\n\t\t\t\u0027Activation Count\u0027,\n\t\t\t\u0027Duration\u0027,\n\t\t\t\u0027Priority\u0027,\n\t\t\t\u0027Location\u0027,\n\t\t\t\u0027Description\u0027,\n\t\t\t\u0027Tag\u0027,\n\t\t\t\u0027Comments/Actions Taken\u0027\n\t\t]\n\t\t\n\t\tfor col_idx, header_text in enumerate(headers):\n\t\t\tset_cell(header_row, col_idx, wb, header_text, h_align\u003d\u0027center\u0027, bold\u003dTrue, bg_color\u003d\u0027yellow\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\n\t\t# Fetch alarm data\n\t\talarm_data \u003d fetch_high_priority_alarms(start_time, end_time)\n\t\t\n\t\t# Add auto-filter to header row (will set range after adding data)\n\t\tnum_rows \u003d len(alarm_data) if alarm_data else 0\n\t\t\n\t\t# Add data rows\n\t\tfor row_idx, alarm_row in enumerate(alarm_data, start\u003d1):\n\t\t\tdata_row \u003d ws.createRow(row_idx)\n\t\t\t\n\t\t\t# Map columns: FirstTimestamp, LastTimestamp, ActivationCount, Duration, Priority, Location, Description, Tag\n\t\t\t# alarm_row[0] \u003d FirstTimestamp\n\t\t\t# alarm_row[1] \u003d LastTimestamp\n\t\t\t# alarm_row[2] \u003d ActivationCount\n\t\t\t# alarm_row[3] \u003d Duration\n\t\t\t# alarm_row[4] \u003d Priority\n\t\t\t# alarm_row[5] \u003d Location\n\t\t\t# alarm_row[6] \u003d Description\n\t\t\t# alarm_row[7] \u003d Tag\n\t\t\t\n\t\t\t# Format timestamps without milliseconds (Java Timestamp objects)\n\t\t\t# Convert to string and remove milliseconds if present\n\t\t\tfirst_ts \u003d str(alarm_row[0])[:19] if alarm_row[0] else \u0027\u0027 # Takes first 19 chars: \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\tlast_ts \u003d str(alarm_row[1])[:19] if alarm_row[1] else \u0027\u0027\n\t\t\t\n\t\t\tset_cell(data_row, 0, wb, first_ts, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 1, wb, last_ts, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 2, wb, alarm_row[2] if alarm_row[2] else 0, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 3, wb, str(alarm_row[3]) if alarm_row[3] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 4, wb, str(alarm_row[4]) if alarm_row[4] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 5, wb, str(alarm_row[5]) if alarm_row[5] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 6, wb, str(alarm_row[6]) if alarm_row[6] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 7, wb, str(alarm_row[7]) if alarm_row[7] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 8, wb, \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027}) # Empty Comments/Actions Taken\n\t\t\n\t\t# Add auto-filter to all columns (from A1 to I[last_row])\n\t\tif num_rows \u003e 0:\n\t\t\tws.setAutoFilter(CellRangeAddress(0, num_rows, 0, 8))\n\t\t\n\t\treturn ws\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# MAIN ORCHESTRATION FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef generate_sorter_report_workbook():\n\t\t\"\"\"\n\t\tMain function to generate the complete sorter report workbook.\n\t\t\n\t\tReturns:\n\t\t\tXSSFWorkbook: Complete workbook with all data and formatting\n\t\t\"\"\"\n\t\t# Calculate report period\n\t\tyesterday_4am, today_4am, formatted_date, formatted_period \u003d calculate_report_period()\n\t\t\n\t\t# Fetch data from database\n\t\tdb_row \u003d fetch_sorter_metrics(yesterday_4am, today_4am)\n\t\t\n\t\t# Transform data for report\n\t\treport_data \u003d transform_metrics_to_report_data(db_row)\n\t\t\n\t\t# Create workbook and worksheet\n\t\twb \u003d XSSFWorkbook()\n\t\tws \u003d setup_worksheet(wb)\n\t\t\n\t\t# Build report structure\n\t\t# Row 2 - Top border\n\t\tcreate_top_border_row(ws, wb, 1)\n\t\t\n\t\t# Row 3 - Title\n\t\tcreate_title_row(ws, wb, 2)\n\t\t\n\t\t# Row 4 - Spacing\n\t\tcreate_spacing_row(ws, wb, 3)\n\t\t\n\t\t# Rows 5-8 - Header information\n\t\tcreate_header_rows(ws, wb, 4, \u0027Amazon BNA8\u0027, \u0027S03\u0027, formatted_date, formatted_period)\n\t\t\n\t\t# Row 9 - Spacing\n\t\tcreate_spacing_row(ws, wb, 8)\n\t\t\n\t\t# Data rows starting at row 10\n\t\tnext_row \u003d create_data_rows(ws, wb, 9, report_data)\n\t\t\n\t\t# Fill empty rows until row 40 (to accommodate expanded data)\n\t\twhile next_row \u003c\u003d 39:\n\t\t\tcreate_spacing_row(ws, wb, next_row)\n\t\t\tnext_row +\u003d 1\n\t\t\n\t\t# Row 41 - Bottom border\n\t\tcreate_bottom_border_row(ws, wb, 40)\n\t\t\n\t\t# Create System Stability sheet with alarm data\n\t\tcreate_system_stability_sheet(wb, yesterday_4am, today_4am)\n\t\t\n\t\treturn wb\n\t\n\t\n\tdef export_workbook_to_bytes(wb):\n\t\t\"\"\"\n\t\tConvert workbook to byte array.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\t\n\t\tReturns:\n\t\t\tbytearray: Excel file as byte array\n\t\t\"\"\"\n\t\toutput \u003d ByteArrayOutputStream()\n\t\ttry:\n\t\t\twb.write(output)\n\t\t\twb.close()\n\t\t\texcel_bytes \u003d output.toByteArray()\n\t\t\toutput.close()\n\t\t\treturn excel_bytes\n\t\texcept Exception as e:\n\t\t\tif output:\n\t\t\t\toutput.close()\n\t\t\traise e\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# ENTRY POINT\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\t\n\t\"\"\"\n\tMain entry point for the Ignition button action.\n\tCalled when the Excel export button is clicked.\n\t\n\tArgs:\n\t\tself: Component reference\n\t\tevent: Action event\n\t\"\"\"\n\ttry:\n\t\t# Generate the workbook\n\t\twb \u003d generate_sorter_report_workbook()\n\t\t\n\t\t# Convert to bytes\n\t\texcel_bytes \u003d export_workbook_to_bytes(wb)\n\t\t\n\t\t# Download with proper filename format\n\t\tcurrent_datetime \u003d datetime.datetime.now()\n\t\tfilename \u003d \u0027BNA8 Sort Report \u0027 + current_datetime.strftime(\u0027%Y%m%d\u0027) + \u0027.xlsx\u0027\n\t\tsystem.perspective.download(filename\u003dfilename, data\u003dexcel_bytes)\n\t\t\n\texcept Exception as e:\n\t\tsystem.perspective.print(\"Error generating Excel: \" + str(e))\n\t\traise" + "script": "\t# Ignition Script for Excel Export Button\n\t\n\timport datetime\n\tfrom java.io import ByteArrayOutputStream\n\tfrom org.apache.poi.ss.usermodel import IndexedColors, FillPatternType, BorderStyle, HorizontalAlignment, VerticalAlignment\n\tfrom org.apache.poi.xssf.usermodel import XSSFWorkbook\n\tfrom org.apache.poi.ss.util import CellRangeAddress\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATE/TIME FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef calculate_report_period():\n\t \"\"\"\n\t Use custom StartDate and EndDate (java.util.Date objects)\n\t and show full timestamps.\n\t \"\"\"\n\t import datetime\n\t\n\t start_java \u003d self.getSibling(\"Period_not_Global_0\").custom.StartDate\n\t end_java \u003d self.getSibling(\"Period_not_Global_0\").custom.EndDate\n\t\n\t start_dt \u003d datetime.datetime.fromtimestamp(start_java.getTime() / 1000.0)\n\t end_dt \u003d datetime.datetime.fromtimestamp(end_java.getTime() / 1000.0)\n\t\n\t # FULL timestamp for the Date row\n\t formatted_date \u003d datetime.datetime.now().strftime(\u0027%b %d, %Y %I:%M %p\u0027)\n\t\n\t # FULL timestamp for the Period row\n\t start_period \u003d start_dt.strftime(\u0027%b %d, %Y %I:%M %p\u0027)\n\t end_period \u003d end_dt.strftime(\u0027%b %d, %Y %I:%M %p\u0027)\n\t formatted_period \u003d start_period + \" to \" + end_period\n\t\n\t start_str \u003d start_dt.strftime(\u0027%Y-%m-%d %H:%M:%S\u0027)\n\t end_str \u003d end_dt.strftime(\u0027%Y-%m-%d %H:%M:%S\u0027)\n\t\n\t return start_str, end_str, formatted_date, formatted_period\n\n\n\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATABASE FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef build_sorter_query(start_time, end_time):\n\t\t\"\"\"\n\t\tBuild the SQL query to fetch sorter metrics.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp in format \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\tend_time (str): End timestamp in format \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\t\n\t\tReturns:\n\t\t\tstr: SQL query string\n\t\t\"\"\"\n\t\tquery \u003d \"\"\"\n\t\tSELECT\n\t\t\tCOUNT(*) AS total_scans,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 0 THEN 1 ELSE 0 END) AS good_diverts,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 4 THEN 1 ELSE 0 END) AS gap_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 21 THEN 1 ELSE 0 END) AS unable_to_divert,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 14 THEN 1 ELSE 0 END) AS failed_to_divert,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 1 THEN 1 ELSE 0 END) AS unknown,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 17 THEN 1 ELSE 0 END) AS lost_container,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 22 THEN 1 ELSE 0 END) AS dest_not_attempted,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 5 THEN 1 ELSE 0 END) AS dest_full,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 12 THEN 1 ELSE 0 END) AS dest_disabled,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 6 THEN 1 ELSE 0 END) AS dest_non_operational,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 2 THEN 1 ELSE 0 END) AS unexpected,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 16 THEN 1 ELSE 0 END) AS no_dest,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 7 THEN 1 ELSE 0 END) AS dest_invalid,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 3 THEN 1 ELSE 0 END) AS tracking_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 13 THEN 1 ELSE 0 END) AS throughput_limit,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 18 THEN 1 ELSE 0 END) AS dimension_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 19 THEN 1 ELSE 0 END) AS weight_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 20 THEN 1 ELSE 0 END) AS container_utilization,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 IN (8, 9, 10) THEN 1 ELSE 0 END) AS scan_error,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 8 THEN 1 ELSE 0 END) AS no_reads,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 10 THEN 1 ELSE 0 END) AS multi_reads,\n\t\t\tSUM(CASE WHEN adiSort_Code_0 \u003d 9 THEN 1 ELSE 0 END) AS no_codes\n\t\tFROM item_data\n\t\tWHERE t_stamp \u003e\u003d \u0027%s\u0027 AND t_stamp \u003c \u0027%s\u0027\n\t\t\tAND adiSort_Code_0 NOT IN (11, 15)\n\t\t\tAND sLocation_ID LIKE \u0027%%S03%%\u0027\n\t\t\"\"\" % (start_time, end_time)\n\t\treturn query\n\t\n\t\n\tdef fetch_sorter_metrics(start_time, end_time):\n\t\t\"\"\"\n\t\tExecute the sorter metrics query and return results.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\ttuple: Database row with metrics\n\t\t\"\"\"\n\t\tquery \u003d build_sorter_query(start_time, end_time)\n\t\tdb_result \u003d system.db.runQuery(query)\n\t\treturn db_result[0]\n\t\n\tdef build_alarms_query(start_time, end_time):\n\t\t\"\"\"\n\t\tBuild the SQL query to fetch high-priority alarms.\n\t\t\"\"\"\n\t\tquery \u003d \"\"\"\n\t\tSELECT\n\t\t\tMIN(ae.eventtime) AS FirstTimestamp,\n\t\t\tMAX(ae.eventtime) AS LastTimestamp,\n\t\t\tCOUNT(*) AS ActivationCount,\n\t\t\tTIME_FORMAT(\n\t\t\t\tSEC_TO_TIME(\n\t\t\t\t\tSUM(\n\t\t\t\t\t\tTIMESTAMPDIFF(SECOND, \n\t\t\t\t\t\t\tae.eventtime,\n\t\t\t\t\t\t\t(SELECT MIN(clr.eventtime) \n\t\t\t\t\t\t\t FROM alarm_events clr USE INDEX (idx_alarm_events_eventid)\n\t\t\t\t\t\t\t WHERE clr.eventid \u003d ae.eventid \n\t\t\t\t\t\t\t AND clr.eventtype IN (1, 2)\n\t\t\t\t\t\t\t AND clr.id \u003e ae.id)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t),\n\t\t\t\t\u0027%%H:%%i:%%s\u0027\n\t\t\t) AS Duration,\n\t\t\t\u0027High\u0027 AS Priority,\n\t\t\tIFNULL(loc.strValue, \u0027\u0027) AS Location,\n\t\t\tCONCAT(\n\t\t\t\tIFNULL(ae.displaypath, \u0027Unknown\u0027), \u0027 - \u0027,\n\t\t\t\tSUBSTRING_INDEX(IFNULL(ae.source, \u0027\u0027), \u0027:/alm:\u0027, -1)\n\t\t\t) AS Description,\n\t\t\tIFNULL(tag.strValue, \u0027\u0027) AS Tag\n\t\tFROM alarm_events ae FORCE INDEX (idx_alarm_events_grouping)\n\t\tLEFT JOIN alarm_event_data tag FORCE INDEX (idx_alarm_event_data_lookup)\n\t\t\tON tag.id \u003d ae.id AND tag.propname \u003d \u0027myTag\u0027\n\t\tLEFT JOIN alarm_event_data loc FORCE INDEX (idx_alarm_event_data_lookup)\n\t\t\tON loc.id \u003d ae.id AND loc.propname \u003d \u0027myLocation\u0027\n\t\tWHERE \n\t\t\tae.eventtype \u003d 0\n\t\t\tAND ae.priority \u003d 3\n\t\t\tAND ae.displaypath NOT LIKE \u0027%%System Startup%%\u0027\n\t\t\tAND ae.source NOT LIKE \u0027%%System Startup%%\u0027\n\t\t\tAND ae.displaypath NOT LIKE \u0027%%System Shutdown%%\u0027\n\t\t\tAND ae.source NOT LIKE \u0027%%System Shutdown%%\u0027\n\t\t\tAND ae.eventtime \u003e\u003d \u0027%s\u0027\n\t\t\tAND ae.eventtime \u003c\u003d \u0027%s\u0027\n\t\t\tAND loc.strValue IN (\u0027MCM01\u0027, \u0027MCM02\u0027)\n\t\t\tAND LOWER(\n\t\t\t\tCONCAT(\n\t\t\t\t\tIFNULL(ae.displaypath,\u0027\u0027),\n\t\t\t\t\tIFNULL(ae.source,\u0027\u0027),\n\t\t\t\t\tIFNULL(tag.strValue,\u0027\u0027)\n\t\t\t\t)\n\t\t\t) LIKE \u0027%%jam%%\u0027\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT 1 \n\t\t\t\tFROM alarm_events clr USE INDEX (idx_alarm_events_eventid)\n\t\t\t\tWHERE clr.eventid \u003d ae.eventid \n\t\t\t\t AND clr.eventtype IN (1, 2)\n\t\t\t\t AND clr.id \u003e ae.id\n\t\t\t\tLIMIT 1\n\t\t\t)\n\t\tGROUP BY\n\t\t\tae.source, \n\t\t\tae.displaypath, \n\t\t\tae.priority,\n\t\t\ttag.strValue\n\t\tORDER BY\n\t\t\tActivationCount DESC\n\t\t\"\"\" % (start_time, end_time)\n\t\treturn query\n\n\n\n\t\n\t\n\tdef fetch_high_priority_alarms(start_time, end_time):\n\t\t\"\"\"\n\t\tExecute the alarm query and return results.\n\t\t\n\t\tArgs:\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\tlist: List of alarm data rows\n\t\t\"\"\"\n\t\tquery \u003d build_alarms_query(start_time, end_time)\n\t\tdb_result \u003d system.db.runQuery(query)\n\t\treturn db_result\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# DATA TRANSFORMATION FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef transform_metrics_to_report_data(db_row):\n\t\t\"\"\"\n\t\tTransform database metrics into structured report data.\n\t\t\n\t\tArgs:\n\t\t\tdb_row (tuple): Database row containing 24 metrics from query\n\t\t\t\n\t\tReturns:\n\t\t\tlist: List of [section, metric, count, percentage] rows\n\t\t\"\"\"\n\t\t# Calculate total for percentages (avoid division by zero)\n\t\ttotal \u003d db_row[0] if db_row[0] else 1\n\t\t\n\t\t# Calculate good reads: total - no_reads - multi_reads - no_codes\n\t\tgood_reads \u003d (db_row[0] or 0) - (db_row[20] or 0) - (db_row[21] or 0) - (db_row[22] or 0)\n\t\t\n\t\t# Build data structure with sections, metrics, counts, and percentages\n\t\tdata \u003d [\n\t\t\t# Inducted Section\n\t\t\t[\u0027Inducted\u0027, \u0027Total Scans\u0027, db_row[0], None],\n\t\t\t[\u0027Inducted\u0027, \u0027Good Diverts\u0027, db_row[1], round(db_row[1] * 100.0 / total, 2) if db_row[1] else 0],\n\t\t\t\n\t\t\t# Sorter Performance Section (18 metrics)\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Gap Error\u0027, db_row[2], round(db_row[2] * 100.0 / total, 2) if db_row[2] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unable to Divert\u0027, db_row[3], round(db_row[3] * 100.0 / total, 2) if db_row[3] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Failed to Divert\u0027, db_row[4], round(db_row[4] * 100.0 / total, 2) if db_row[4] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unknown\u0027, db_row[5], round(db_row[5] * 100.0 / total, 2) if db_row[5] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Lost Container\u0027, db_row[6], round(db_row[6] * 100.0 / total, 2) if db_row[6] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Not Attempted\u0027, db_row[7], round(db_row[7] * 100.0 / total, 2) if db_row[7] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Full\u0027, db_row[8], round(db_row[8] * 100.0 / total, 2) if db_row[8] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Disabled\u0027, db_row[9], round(db_row[9] * 100.0 / total, 2) if db_row[9] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Non Operational\u0027, db_row[10], round(db_row[10] * 100.0 / total, 2) if db_row[10] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Unexpected\u0027, db_row[11], round(db_row[11] * 100.0 / total, 2) if db_row[11] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027No Dest\u0027, db_row[12], round(db_row[12] * 100.0 / total, 2) if db_row[12] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dest Invalid\u0027, db_row[13], round(db_row[13] * 100.0 / total, 2) if db_row[13] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Tracking Error\u0027, db_row[14], round(db_row[14] * 100.0 / total, 2) if db_row[14] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Throughput Limit\u0027, db_row[15], round(db_row[15] * 100.0 / total, 2) if db_row[15] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Dimension Error\u0027, db_row[16], round(db_row[16] * 100.0 / total, 2) if db_row[16] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Weight Error\u0027, db_row[17], round(db_row[17] * 100.0 / total, 2) if db_row[17] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Container Utilization\u0027, db_row[18], round(db_row[18] * 100.0 / total, 2) if db_row[18] else 0],\n\t\t\t[\u0027Sorter Performance\u0027, \u0027Scan Error\u0027, db_row[19], round(db_row[19] * 100.0 / total, 2) if db_row[19] else 0],\n\t\t\t\n\t\t\t# Scanner Health Section (4 metrics)\n\t\t\t[\u0027Scanner Health\u0027, \u0027Good Reads\u0027, good_reads, round(good_reads * 100.0 / total, 2) if good_reads else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027No Reads\u0027, db_row[20], round(db_row[20] * 100.0 / total, 2) if db_row[20] else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027Multi Reads\u0027, db_row[21], round(db_row[21] * 100.0 / total, 2) if db_row[21] else 0],\n\t\t\t[\u0027Scanner Health\u0027, \u0027No Codes\u0027, db_row[22], round(db_row[22] * 100.0 / total, 2) if db_row[22] else 0]\n\t\t]\n\t\t\n\t\treturn data\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# EXCEL STYLING FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef create_cell_style(wb, h_align\u003dNone, bold\u003dFalse, size\u003d11, bg_color\u003dNone, borders\u003dNone, fmt\u003dNone):\n\t\t\"\"\"\n\t\tCreate and return a cell style with the specified formatting.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\th_align (str): Horizontal alignment (\u0027center\u0027, \u0027right\u0027, \u0027left\u0027)\n\t\t\tbold (bool): Bold font\n\t\t\tsize (int): Font size in points\n\t\t\tbg_color (str): Background color (\u0027yellow\u0027)\n\t\t\tborders (dict): Border specification {\u0027L\u0027: \u0027M\u0027/\u0027T\u0027, \u0027R\u0027: \u0027M\u0027/\u0027T\u0027, \u0027T\u0027: \u0027M\u0027/\u0027T\u0027, \u0027B\u0027: \u0027M\u0027/\u0027T\u0027}\n\t\t\tfmt (str): Number format string\n\t\t\t\n\t\tReturns:\n\t\t\tCellStyle: Configured cell style\n\t\t\"\"\"\n\t\tstyle \u003d wb.createCellStyle()\n\t\t\n\t\t# Font\n\t\tfont \u003d wb.createFont()\n\t\tfont.setFontName(\u0027Aptos Narrow\u0027)\n\t\tfont.setFontHeightInPoints(size)\n\t\tfont.setBold(bold)\n\t\tstyle.setFont(font)\n\t\t\n\t\t# Alignment\n\t\tif h_align \u003d\u003d \u0027center\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.CENTER)\n\t\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER)\n\t\telif h_align \u003d\u003d \u0027right\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.RIGHT)\n\t\telif h_align \u003d\u003d \u0027left\u0027:\n\t\t\tstyle.setAlignment(HorizontalAlignment.LEFT)\n\t\t\n\t\t# Background color\n\t\tif bg_color \u003d\u003d \u0027yellow\u0027:\n\t\t\tstyle.setFillForegroundColor(IndexedColors.GOLD.getIndex())\n\t\t\tstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND)\n\t\t\n\t\t# Borders\n\t\tif borders:\n\t\t\tif \u0027L\u0027 in borders:\n\t\t\t\tstyle.setBorderLeft(BorderStyle.MEDIUM if borders[\u0027L\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027R\u0027 in borders:\n\t\t\t\tstyle.setBorderRight(BorderStyle.MEDIUM if borders[\u0027R\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027T\u0027 in borders:\n\t\t\t\tstyle.setBorderTop(BorderStyle.MEDIUM if borders[\u0027T\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\tif \u0027B\u0027 in borders:\n\t\t\t\tstyle.setBorderBottom(BorderStyle.MEDIUM if borders[\u0027B\u0027] \u003d\u003d \u0027M\u0027 else BorderStyle.THIN)\n\t\t\n\t\t# Number format\n\t\tif fmt:\n\t\t\tstyle.setDataFormat(wb.createDataFormat().getFormat(fmt))\n\t\t\n\t\treturn style\n\t\n\t\n\tdef set_cell(row, col_idx, wb, value\u003dNone, h_align\u003dNone, bold\u003dFalse, size\u003d11, bg_color\u003dNone, borders\u003dNone, fmt\u003dNone):\n\t\t\"\"\"\n\t\tCreate a cell and apply value and styling.\n\t\t\n\t\tArgs:\n\t\t\trow: Row object\n\t\t\tcol_idx (int): Column index\n\t\t\twb: Workbook object\n\t\t\tvalue: Cell value (int, float, or string)\n\t\t\th_align (str): Horizontal alignment\n\t\t\tbold (bool): Bold font\n\t\t\tsize (int): Font size\n\t\t\tbg_color (str): Background color\n\t\t\tborders (dict): Border specification\n\t\t\tfmt (str): Number format\n\t\t\t\n\t\tReturns:\n\t\t\tCell: Created and styled cell\n\t\t\"\"\"\n\t\tcell \u003d row.createCell(col_idx)\n\t\t\n\t\t# Set value based on type\n\t\tif value is not None:\n\t\t\tif isinstance(value, (int, long)):\n\t\t\t\tcell.setCellValue(int(value))\n\t\t\telif isinstance(value, float):\n\t\t\t\tcell.setCellValue(float(value))\n\t\t\telse:\n\t\t\t\tcell.setCellValue(str(value))\n\t\t\n\t\t# Apply style\n\t\tstyle \u003d create_cell_style(wb, h_align\u003dh_align, bold\u003dbold, size\u003dsize, bg_color\u003dbg_color, borders\u003dborders, fmt\u003dfmt)\n\t\tcell.setCellStyle(style)\n\t\t\n\t\treturn cell\n\t\n\t\n\tdef setup_worksheet(wb):\n\t\t\"\"\"\n\t\tCreate and configure the worksheet with proper settings and column widths.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\t\n\t\tReturns:\n\t\t\tWorksheet: Configured worksheet\n\t\t\"\"\"\n\t\tws \u003d wb.createSheet(\"Sorter Performance\")\n\t\t\n\t\t# Hide gridlines\n\t\tws.setDisplayGridlines(False)\n\t\t\n\t\t# Set column widths\n\t\tws.setColumnWidth(0, int(3.43 * 256))\n\t\tws.setColumnWidth(1, int(10.71 * 256))\n\t\tws.setColumnWidth(2, int(13.0 * 256))\n\t\tws.setColumnWidth(3, int(13.0 * 256))\n\t\tws.setColumnWidth(4, int(13.0 * 256))\n\t\tws.setColumnWidth(5, int(13.0 * 256))\n\t\tws.setColumnWidth(6, int(13.0 * 256))\n\t\t\n\t\treturn ws\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# EXCEL STRUCTURE FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef create_top_border_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the top border row for the report.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027, \u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027T\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027, \u0027T\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_title_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the title row with \"Sortation Report\" heading.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\trow.setHeightInPoints(26.25)\n\t\t\n\t\t# Create all cells first\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, \"Sortation Report\", h_align\u003d\u0027center\u0027, bold\u003dTrue, size\u003d20, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\n\t\t# Merge C3:F3 (columns 2-5)\n\t\tws.addMergedRegion(CellRangeAddress(row_idx, row_idx, 2, 5))\n\t\n\t\n\tdef create_spacing_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate a spacing row with only left and right borders.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_header_rows(ws, wb, start_row_idx, project, sorter, date, period):\n\t\t\"\"\"\n\t\tCreate the header rows with Project, Sorter, Date, and Period information.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\tstart_row_idx (int): Starting row index\n\t\t\tproject (str): Project name\n\t\t\tsorter (str): Sorter name\n\t\t\tdate (str): Report date\n\t\t\tperiod (str): Report period\n\t\t\"\"\"\n\t\theaders \u003d [\n\t\t\t(\u0027Project\u0027, project),\n\t\t\t(\u0027Sorter\u0027, sorter),\n\t\t\t(\u0027Date\u0027, date),\n\t\t\t(\u0027Period\u0027, period)\n\t\t]\n\t\t\n\t\tfor i, (label, value) in enumerate(headers):\n\t\t\trow_idx \u003d start_row_idx + i\n\t\t\trow \u003d ws.createRow(row_idx)\n\t\t\t\n\t\t\t# Create all cells first\n\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\tset_cell(row, 2, wb, label, h_align\u003d\u0027right\u0027, bold\u003dTrue, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 3, wb, value, h_align\u003d\u0027left\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 4, wb, borders\u003d{\u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 5, wb, borders\u003d{\u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\n\t\t\t# Merge D:F (columns 3-5)\n\t\t\tws.addMergedRegion(CellRangeAddress(row_idx, row_idx, 3, 5))\n\t\n\t\n\tdef create_data_rows(ws, wb, start_row_idx, data):\n\t\t\"\"\"\n\t\tCreate the data rows with sections and metrics.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\tstart_row_idx (int): Starting row index\n\t\t\tdata (list): List of [section, metric, count, percentage] rows\n\t\t\t\n\t\tReturns:\n\t\t\tint: Next available row index\n\t\t\"\"\"\n\t\tcurrent_row_idx \u003d start_row_idx\n\t\tcurrent_section \u003d None\n\t\t\n\t\tfor row_data in data:\n\t\t\tsection, metric, count, pct \u003d row_data[0], row_data[1], row_data[2], row_data[3]\n\t\t\t\n\t\t\t# Section header\n\t\t\tif section !\u003d current_section:\n\t\t\t\tcurrent_section \u003d section\n\t\t\t\trow \u003d ws.createRow(current_row_idx)\n\t\t\t\t\n\t\t\t\t# Create all cells first\n\t\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\t\tset_cell(row, 2, wb, section, h_align\u003d\u0027center\u0027, bold\u003dTrue, bg_color\u003d\u0027yellow\u0027)\n\t\t\t\tset_cell(row, 3, wb)\n\t\t\t\tset_cell(row, 4, wb)\n\t\t\t\tset_cell(row, 5, wb)\n\t\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\t\n\t\t\t\t# Merge section header\n\t\t\t\tws.addMergedRegion(CellRangeAddress(current_row_idx, current_row_idx, 2, 5))\n\t\t\t\tcurrent_row_idx +\u003d 1\n\t\t\t\n\t\t\t# Data row\n\t\t\trow \u003d ws.createRow(current_row_idx)\n\t\t\t\n\t\t\t# Create all cells first\n\t\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027})\n\t\t\tset_cell(row, 2, wb, metric, h_align\u003d\u0027right\u0027)\n\t\t\tset_cell(row, 3, wb)\n\t\t\tset_cell(row, 4, wb, count, h_align\u003d\u0027right\u0027)\n\t\t\t\n\t\t\t# Show percentage for Good Diverts (Inducted section) and all Sorter Performance/Scanner Health metrics\n\t\t\tif pct is not None and (section !\u003d \u0027Inducted\u0027 or metric \u003d\u003d \u0027Good Diverts\u0027):\n\t\t\t\tset_cell(row, 5, wb, float(pct) / 100.0, h_align\u003d\u0027right\u0027, fmt\u003d\u00270.00%\u0027)\n\t\t\telse:\n\t\t\t\tset_cell(row, 5, wb, h_align\u003d\u0027right\u0027, fmt\u003d\u00270.00%\u0027)\n\t\t\t\n\t\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027})\n\t\t\t\n\t\t\t# Merge C:D (columns 2-3)\n\t\t\tws.addMergedRegion(CellRangeAddress(current_row_idx, current_row_idx, 2, 3))\n\t\t\tcurrent_row_idx +\u003d 1\n\t\t\n\t\treturn current_row_idx\n\t\n\t\n\tdef create_bottom_border_row(ws, wb, row_idx):\n\t\t\"\"\"\n\t\tCreate the bottom border row for the report.\n\t\t\n\t\tArgs:\n\t\t\tws: Worksheet object\n\t\t\twb: Workbook object\n\t\t\trow_idx (int): Row index to create\n\t\t\"\"\"\n\t\trow \u003d ws.createRow(row_idx)\n\t\trow.setHeightInPoints(15.75)\n\t\tset_cell(row, 1, wb, borders\u003d{\u0027L\u0027:\u0027M\u0027, \u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 2, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 3, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 4, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 5, wb, borders\u003d{\u0027B\u0027:\u0027M\u0027})\n\t\tset_cell(row, 6, wb, borders\u003d{\u0027R\u0027:\u0027M\u0027, \u0027B\u0027:\u0027M\u0027})\n\t\n\t\n\tdef create_system_stability_sheet(wb, start_time, end_time):\n\t\t\"\"\"\n\t\tCreate the System Stability worksheet with alarm data.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\tstart_time (str): Start timestamp\n\t\t\tend_time (str): End timestamp\n\t\t\t\n\t\tReturns:\n\t\t\tWorksheet: Created and populated worksheet\n\t\t\"\"\"\n\t\t# Create worksheet\n\t\tws \u003d wb.createSheet(\"System Stability\")\n\t\tws.setDisplayGridlines(False)\n\t\t\n\t\t# Set column widths for 9 columns (A-I)\n\t\t# Column widths: First Timestamp, Last Timestamp, Activation Count, Duration, Priority, Location, Description, Tag, Comments/Actions Taken\n\t\tws.setColumnWidth(0, int(22 * 256)) # First Timestamp\n\t\tws.setColumnWidth(1, int(22 * 256)) # Last Timestamp\n\t\tws.setColumnWidth(2, int(20 * 256)) # Activation Count\n\t\tws.setColumnWidth(3, int(15 * 256)) # Duration\n\t\tws.setColumnWidth(4, int(15 * 256)) # Priority\n\t\tws.setColumnWidth(5, int(15 * 256)) # Location\n\t\tws.setColumnWidth(6, int(60 * 256)) # Description (doubled)\n\t\tws.setColumnWidth(7, int(60 * 256)) # Tag (doubled)\n\t\tws.setColumnWidth(8, int(25 * 256)) # Comments/Actions Taken\n\t\t\n\t\t# Create header row (row 0)\n\t\theader_row \u003d ws.createRow(0)\n\t\theaders \u003d [\n\t\t\t\u0027First Timestamp\u0027,\n\t\t\t\u0027Last Timestamp\u0027,\n\t\t\t\u0027Activation Count\u0027,\n\t\t\t\u0027Duration\u0027,\n\t\t\t\u0027Priority\u0027,\n\t\t\t\u0027Location\u0027,\n\t\t\t\u0027Description\u0027,\n\t\t\t\u0027Tag\u0027,\n\t\t\t\u0027Comments/Actions Taken\u0027\n\t\t]\n\t\t\n\t\tfor col_idx, header_text in enumerate(headers):\n\t\t\tset_cell(header_row, col_idx, wb, header_text, h_align\u003d\u0027center\u0027, bold\u003dTrue, bg_color\u003d\u0027yellow\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\n\t\t# Fetch alarm data\n\t\talarm_data \u003d fetch_high_priority_alarms(start_time, end_time)\n\t\t\n\t\t# Add auto-filter to header row (will set range after adding data)\n\t\tnum_rows \u003d len(alarm_data) if alarm_data else 0\n\t\t\n\t\t# Add data rows\n\t\tfor row_idx, alarm_row in enumerate(alarm_data, start\u003d1):\n\t\t\tdata_row \u003d ws.createRow(row_idx)\n\t\t\t\n\t\t\t# Map columns: FirstTimestamp, LastTimestamp, ActivationCount, Duration, Priority, Location, Description, Tag\n\t\t\t# alarm_row[0] \u003d FirstTimestamp\n\t\t\t# alarm_row[1] \u003d LastTimestamp\n\t\t\t# alarm_row[2] \u003d ActivationCount\n\t\t\t# alarm_row[3] \u003d Duration\n\t\t\t# alarm_row[4] \u003d Priority\n\t\t\t# alarm_row[5] \u003d Location\n\t\t\t# alarm_row[6] \u003d Description\n\t\t\t# alarm_row[7] \u003d Tag\n\t\t\t\n\t\t\t# Format timestamps without milliseconds (Java Timestamp objects)\n\t\t\t# Convert to string and remove milliseconds if present\n\t\t\tfirst_ts \u003d str(alarm_row[0])[:19] if alarm_row[0] else \u0027\u0027 # Takes first 19 chars: \u0027YYYY-MM-DD HH:MM:SS\u0027\n\t\t\tlast_ts \u003d str(alarm_row[1])[:19] if alarm_row[1] else \u0027\u0027\n\t\t\t\n\t\t\tset_cell(data_row, 0, wb, first_ts, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 1, wb, last_ts, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 2, wb, alarm_row[2] if alarm_row[2] else 0, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 3, wb, str(alarm_row[3]) if alarm_row[3] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 4, wb, str(alarm_row[4]) if alarm_row[4] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 5, wb, str(alarm_row[5]) if alarm_row[5] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 6, wb, str(alarm_row[6]) if alarm_row[6] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 7, wb, str(alarm_row[7]) if alarm_row[7] else \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027})\n\t\t\tset_cell(data_row, 8, wb, \u0027\u0027, h_align\u003d\u0027center\u0027, borders\u003d{\u0027L\u0027:\u0027T\u0027, \u0027R\u0027:\u0027T\u0027, \u0027T\u0027:\u0027T\u0027, \u0027B\u0027:\u0027T\u0027}) # Empty Comments/Actions Taken\n\t\t\n\t\t# Add auto-filter to all columns (from A1 to I[last_row])\n\t\tif num_rows \u003e 0:\n\t\t\tws.setAutoFilter(CellRangeAddress(0, num_rows, 0, 8))\n\t\t\n\t\treturn ws\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# MAIN ORCHESTRATION FUNCTIONS\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\tdef generate_sorter_report_workbook():\n\t\t\"\"\"\n\t\tMain function to generate the complete sorter report workbook.\n\t\t\n\t\tReturns:\n\t\t\tXSSFWorkbook: Complete workbook with all data and formatting\n\t\t\"\"\"\n\t\t# Calculate report period\n\t\tyesterday_4am, today_4am, formatted_date, formatted_period \u003d calculate_report_period()\n\t\t\n\t\t# Fetch data from database\n\t\tdb_row \u003d fetch_sorter_metrics(yesterday_4am, today_4am)\n\t\t\n\t\t# Transform data for report\n\t\treport_data \u003d transform_metrics_to_report_data(db_row)\n\t\t\n\t\t# Create workbook and worksheet\n\t\twb \u003d XSSFWorkbook()\n\t\tws \u003d setup_worksheet(wb)\n\t\t\n\t\t# Build report structure\n\t\t# Row 2 - Top border\n\t\tcreate_top_border_row(ws, wb, 1)\n\t\t\n\t\t# Row 3 - Title\n\t\tcreate_title_row(ws, wb, 2)\n\t\t\n\t\t# Row 4 - Spacing\n\t\tcreate_spacing_row(ws, wb, 3)\n\t\t\n\t\t# Rows 5-8 - Header information\n\t\tcreate_header_rows(ws, wb, 4, \u0027Amazon BNA8\u0027, \u0027S03\u0027, formatted_date, formatted_period)\n\t\t\n\t\t# Row 9 - Spacing\n\t\tcreate_spacing_row(ws, wb, 8)\n\t\t\n\t\t# Data rows starting at row 10\n\t\tnext_row \u003d create_data_rows(ws, wb, 9, report_data)\n\t\t\n\t\t# Fill empty rows until row 40 (to accommodate expanded data)\n\t\twhile next_row \u003c\u003d 39:\n\t\t\tcreate_spacing_row(ws, wb, next_row)\n\t\t\tnext_row +\u003d 1\n\t\t\n\t\t# Row 41 - Bottom border\n\t\tcreate_bottom_border_row(ws, wb, 40)\n\t\t\n\t\t# Create System Stability sheet with alarm data\n\t\tcreate_system_stability_sheet(wb, yesterday_4am, today_4am)\n\t\t\n\t\treturn wb\n\t\n\t\n\tdef export_workbook_to_bytes(wb):\n\t\t\"\"\"\n\t\tConvert workbook to byte array.\n\t\t\n\t\tArgs:\n\t\t\twb: Workbook object\n\t\t\t\n\t\tReturns:\n\t\t\tbytearray: Excel file as byte array\n\t\t\"\"\"\n\t\toutput \u003d ByteArrayOutputStream()\n\t\ttry:\n\t\t\twb.write(output)\n\t\t\twb.close()\n\t\t\texcel_bytes \u003d output.toByteArray()\n\t\t\toutput.close()\n\t\t\treturn excel_bytes\n\t\texcept Exception as e:\n\t\t\tif output:\n\t\t\t\toutput.close()\n\t\t\traise e\n\t\n\t\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t# ENTRY POINT\n\t# \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\t\n\t\n\t\"\"\"\n\tMain entry point for the Ignition button action.\n\tCalled when the Excel export button is clicked.\n\t\n\tArgs:\n\t\tself: Component reference\n\t\tevent: Action event\n\t\"\"\"\n\ttry:\n\t\t# Generate the workbook\n\t\twb \u003d generate_sorter_report_workbook()\n\t\t\n\t\t# Convert to bytes\n\t\texcel_bytes \u003d export_workbook_to_bytes(wb)\n\t\t\n\t\t# Download with proper filename format\n\t\tcurrent_datetime \u003d datetime.datetime.now()\n\t\tfilename \u003d \u0027BNA8 Sort Report \u0027 + current_datetime.strftime(\u0027%Y%m%d\u0027) + \u0027.xlsx\u0027\n\t\tsystem.perspective.download(filename\u003dfilename, data\u003dexcel_bytes)\n\t\t\n\texcept Exception as e:\n\t\tsystem.perspective.print(\"Error generating Excel: \" + str(e))\n\t\traise" }, "scope": "G", "type": "script" diff --git a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/resource.json b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/resource.json index 3f445b28..a2969abd 100644 --- a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/resource.json +++ b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/resource.json @@ -10,8 +10,8 @@ "attributes": { "lastModification": { "actor": "admin", - "timestamp": "2025-11-25T09:10:52Z" + "timestamp": "2025-11-25T10:35:05Z" }, - "lastModificationSignature": "3b14d68609a98f6a51ffc4e3f6619dc9ce39dcd8602eba40e89b4f61ad95150a" + "lastModificationSignature": "84e845324e74392edc232768d97dc551b2425b6f2f5c96d56051b98eb12cb93b" } } \ No newline at end of file diff --git a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/thumbnail.png b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/thumbnail.png index d3054559..11cb226d 100644 Binary files a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/thumbnail.png and b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/thumbnail.png differ diff --git a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/view.json b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/view.json index 3ef036e4..b9f0edbb 100644 --- a/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/view.json +++ b/BNA8_autStand/com.inductiveautomation.perspective/views/autStand/Windows/Statistics/view.json @@ -12,7 +12,7 @@ } }, "params": { - "Tab_ID": 4, + "Tab_ID": 10, "Table": "Statistics" }, "propConfig": { @@ -1593,7 +1593,7 @@ { "appearance": { "font": { - "size": 10, + "size": 15, "weight": 500 }, "grid": { @@ -3084,7 +3084,7 @@ { "appearance": { "font": { - "size": 10, + "size": 15, "weight": 500 }, "grid": { @@ -68233,7 +68233,7 @@ "color": "", "horizontalCenter": "middle", "opacity": 1, - "rotation": 90, + "rotation": 60, "verticalCenter": "middle" }, "opposite": false @@ -68708,7 +68708,7 @@ "color": "", "horizontalCenter": "middle", "opacity": 1, - "rotation": 90, + "rotation": 60, "verticalCenter": "middle" }, "opposite": false @@ -68892,7 +68892,7 @@ "props.currentTabIndex": { "onChange": { "enabled": null, - "script": "\tdropdown \u003d self.parent.getChild(\"Aggregation_Mode\").getChild(\"Dropdown_Aggregation_mode\")\n\tval \u003d currentValue.value\n\t\n\t# Default options\n\toptions \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"},\n\t {\"value\": \"Rate\", \"label\": \"Rate\"}\n\t]\n\tvalue \u003d \"Count\"\n\t\n\t# Customize options based on current value\n\tif val in (4, 5, 6, 7, 9):\n\t options \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"}\n\t ]\n\t\n\t# Apply changes\n\tdropdown.props.options \u003d options\n\tdropdown.props.value \u003d value" + "script": "\tdropdown \u003d self.parent.getChild(\"Aggregation_Mode\").getChild(\"Dropdown_Aggregation_mode\")\n\tval \u003d currentValue.value\n\t\n\t# Default options\n\toptions \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"},\n\t {\"value\": \"Rate\", \"label\": \"Rate\"}\n\t]\n\tvalue \u003d \"Count\"\n\t\n\t# Customize options based on current value\n\tif val in (4, 5, 6, 7, 9, 10):\n\t options \u003d [\n\t {\"value\": \"Count\", \"label\": \"Count\"},\n\t {\"value\": \"Percentage\", \"label\": \"Percentage\"}\n\t ]\n\t\n\t# Apply changes\n\tdropdown.props.options \u003d options\n\tdropdown.props.value \u003d value" } }, "props.tabs": { @@ -68900,7 +68900,7 @@ } }, "props": { - "currentTabIndex": 4, + "currentTabIndex": 10, "menuStyle": { "backgroundColor": "#FFFFFFBD", "fontSize": "1.0vmin", @@ -68990,7 +68990,7 @@ "style": { "fontSize": "1.5vmin" }, - "value": "Count" + "value": "Percentage" }, "type": "ia.input.dropdown" } @@ -69010,6 +69010,7 @@ "config": { "expression": "{../Statistics.props.currentTabIndex} !\u003d10" }, + "enabled": false, "type": "expr" } } @@ -72218,7 +72219,7 @@ "component": { "onActionPerformed": { "config": { - "script": "\tdef exportIfVisible(displayFlag, dataset, filename\u003d\"Export.csv\"):\n\t import system\n\t \n\t if not displayFlag:\n\t return\n\t \n\t if dataset is None or dataset.getRowCount() \u003d\u003d 0:\n\t return\n\t \n\t colNames \u003d list(dataset.getColumnNames())\n\t newRows \u003d []\n\t \n\t for rowIndex in range(dataset.getRowCount()):\n\t rowVals \u003d []\n\t for colName in colNames:\n\t val \u003d dataset.getValueAt(rowIndex, colName)\n\t \n\t # If the column name contains “perc” (case-insensitive), multiply by 100\n\t if \"perc\" in colName.lower() and val is not None:\n\t try:\n\t val \u003d float(val) * 100\n\t except Exception:\n\t pass\n\t \n\t # Format Startstamp/Endtstamp if needed (you already have this logic)\n\t if colName in (\"Startstamp\", \"Endtstamp\") and val is not None:\n\t try:\n\t if isinstance(val, (int, long, float)):\n\t dt \u003d system.date.fromMillis(val)\n\t else:\n\t dt \u003d val\n\t val \u003d system.date.format(dt, \"yyyy-MM-dd HH:mm:ss\")\n\t except Exception:\n\t pass\n\t \n\t rowVals.append(val)\n\t newRows.append(rowVals)\n\t \n\t formattedDs \u003d system.dataset.toDataSet(colNames, newRows)\n\t csvString \u003d system.dataset.toCSV(formattedDs)\n\t system.perspective.download(filename, csvString, \"Comma Separated Values\")\n\t# Read the current tab index property\n\tstats \u003d self.parent.getChild(\"Statistics\")\n\tcurrentTabIndex \u003d stats.props.currentTabIndex\n\t\n\t# Induct Details\n\tinductDetails \u003d stats.getChild(\"Induct Details\")\n\tcountView \u003d inductDetails.getChild(\"Induct Details Count\")\n\tpercView \u003d inductDetails.getChild(\"Induct Details Perc\")\n\trateView \u003d inductDetails.getChild(\"Induct Details Rate\")\n\t\n\t# Scanner Details\n\tscannerDetails \u003d stats.getChild(\"Scanner_Details\")\n\tscannerCountView \u003d scannerDetails.getChild(\"Scanner Details\")\n\tscannerPercentageView \u003d scannerDetails.getChild(\"Scanner Details Perc\")\n\tscannerRateView \u003d scannerDetails.getChild(\"Scanner Details Rate\")\n\t\n\t# Sorter Details\n\tsorterDetails \u003d stats.getChild(\"Sorter Details\")\n\tsorterCountView \u003d sorterDetails.getChild(\"Sorter Details\")\n\tsorterPercentageView \u003d sorterDetails.getChild(\"Sorter Details Perc\")\n\tsorterRateView \u003d sorterDetails.getChild(\"Sorter Details Rate\")\n\t\n\t# Lane Details\n\tlaneDetails \u003d stats.getChild(\"Lane Details\")\n\tlaneCountView \u003d laneDetails.getChild(\"Lane Details\")\n\tlanePercView \u003d laneDetails.getChild(\"Lane Details Perc\")\n\tlaneRateView \u003d laneDetails.getChild(\"Lane Details Rate\")\n\t\n\t# Hourly Induct\n\thourlyInduct \u003d stats.getChild(\"Hourly_Induct\")\n\thourlyInductCountView \u003d hourlyInduct.getChild(\"Hourly Induct Count\")\n\thourlyInductPercView \u003d hourlyInduct.getChild(\"Hourly Induct Perc\")\n\t\n\t# Hourly Scanner\n\thourlyScanner \u003d stats.getChild(\"Hourly_Scanner\")\n\thourlyScannerCountView \u003d hourlyScanner.getChild(\"Hourly Scanner Count\")\n\thourlyScannerPercView \u003d hourlyScanner.getChild(\"Hourly Scanner Perc\")\n\thourlyScannerRateView \u003d hourlyScanner.getChild(\"Hourly Scanner Rate\")\n\t\n\t# Hourly Sorter Details\n\thourlySorterDetails \u003d stats.getChild(\"Hourly_Sorter_Details\")\n\thourlySorterDetailsCountView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Count\")\n\thourlySorterDetailsPercView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Perc\")\n\thourlySorterDetailsRateView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Rate\")\n\t\n\t# Hourly Lane\n\thourlyLane \u003d stats.getChild(\"Hourly_Lane\")\n\thourlyLaneCountView \u003d hourlyLane.getChild(\"Hourly Lane Count\")\n\thourlyLanePercView \u003d hourlyLane.getChild(\"Hourly Lane Perc\")\n\thourlyLaneRateView \u003d hourlyLane.getChild(\"Hourly Lane Rate\")\n\t\n\t# Lane Total Full\n\tlaneTotalFull \u003d stats.getChild(\"Total Full\")\n\tlaneTotalFullCountView \u003d laneTotalFull.getChild(\"Total Full Count\")\n\tlaneTotalFullPercView \u003d laneTotalFull.getChild(\"Total Full Perc\")\n\tlaneTotalFullRateView \u003d laneTotalFull.getChild(\"Total Full Rate\")\n\t\t\n\t# Jam By Area\n\tjamByArea \u003d stats.getChild(\"Jam Area\")\n\tjamByAreaCountView \u003d jamByArea.getChild(\"Jam Area Count\")\n\tjamByArealPercView \u003d jamByArea.getChild(\"Jam Area Percentage\")\n\t\t\n\t# Daily Jam Frequency\n\tdailyJamFrequency \u003d stats.getChild(\"Daily Jam Frequency\")\n\tdailyJamFrequencyCountView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Count\")\n\tdailyJamFrequencyPercView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Perc\")\n\n\tif currentTabIndex \u003d\u003d 0:\n\t\texportIfVisible(rateView.position.display, rateView.props.data, \"InductDetails_Rate.csv\")\n\t\texportIfVisible(percView.position.display, percView.props.data, \"InductDetails_Percentage.csv\")\n\t\texportIfVisible(countView.position.display, countView.props.data, \"InductDetails_Count.csv\")\n\telif currentTabIndex \u003d\u003d 1:\n\t\texportIfVisible(scannerCountView.position.display, scannerCountView.props.data, \"ScannerDetails_Count.csv\")\n\t\texportIfVisible(scannerPercentageView.position.display, scannerPercentageView.props.data, \"ScannerDetails_Percentage.csv\")\n\t\texportIfVisible(scannerRateView.position.display, scannerRateView.props.data, \"ScannerDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 2:\n\t\texportIfVisible(sorterCountView.position.display, sorterCountView.props.data, \"SorterDetails_Count.csv\")\n\t\texportIfVisible(sorterPercentageView.position.display, sorterPercentageView.props.data, \"SorterDetails_Percentage.csv\")\n\t\texportIfVisible(sorterRateView.position.display, sorterRateView.props.data, \"SorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 3:\n\t\texportIfVisible(laneCountView.position.display, laneCountView.props.data, \"LaneDetails_Count.csv\")\n\t\texportIfVisible(lanePercView.position.display, lanePercView.props.data, \"LaneDetails_Percentage.csv\")\n\t\texportIfVisible(laneRateView.position.display, laneRateView.props.data, \"LaneDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 4:\n\t\texportIfVisible(hourlyInductCountView.position.display, hourlyInductCountView.props.data, \"HourlyInduct_Count.csv\")\n\t\texportIfVisible(hourlyInductPercView.position.display, hourlyInductPercView.props.data, \"HourlyInduct_Perc.csv\")\n\telif currentTabIndex \u003d\u003d 5:\n\t\texportIfVisible(hourlyScannerCountView.position.display, hourlyScannerCountView.props.data, \"HourlyScanner_Count.csv\")\n\t\texportIfVisible(hourlyScannerPercView.position.display, hourlyScannerPercView.props.data, \"HourlyScanner_Percentage.csv\")\n\t\texportIfVisible(hourlyScannerRateView.position.display, hourlyScannerRateView.props.data, \"HourlyScanner_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 6:\n\t\texportIfVisible(hourlySorterDetailsCountView.position.display, hourlySorterDetailsCountView.props.data, \"HourlySorterDetails_Count.csv\")\n\t\texportIfVisible(hourlySorterDetailsPercView.position.display, hourlySorterDetailsPercView.props.data, \"HourlySorterDetails_Percentage.csv\")\n\t\texportIfVisible(hourlySorterDetailsRateView.position.display, hourlySorterDetailsRateView.props.data, \"HourlySorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 7:\n\t\texportIfVisible(hourlyLaneCountView.position.display, hourlyLaneCountView.props.data, \"HourlyLane_Count.csv\")\n\t\texportIfVisible(hourlyLanePercView.position.display, hourlyLanePercView.props.data, \"HourlyLane_Percentage.csv\")\n\t\texportIfVisible(hourlyLaneRateView.position.display, hourlyLaneRateView.props.data, \"HourlyLane_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 8:\n\t\texportIfVisible(laneTotalFullCountView.position.display, laneTotalFullCountView.props.data, \"LaneTotalFull_Count.csv\")\n\t\texportIfVisible(laneTotalFullPercView.position.display, laneTotalFullPercView.props.data, \"LaneTotalFull_Percentage.csv\")\n\t\texportIfVisible(laneTotalFullRateView.position.display, laneTotalFullRateView.props.data, \"LaneTotalFull_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 9:\n\t\texportIfVisible(jamByAreaCountView.position.display, jamByAreaCountView.props.data, \"JamByArea_Count.csv\")\n\t\texportIfVisible(jamByArealPercView.position.display, jamByArealPercView.props.data, \"JamByArea_Percentage.csv\")\n\telif currentTabIndex \u003d\u003d 10:\n\t\texportIfVisible(dailyJamFrequencyCountView.position.display, dailyJamFrequencyCountView.props.dataSources.example, \"DailyJamFrequency_Count.csv\")\n\t\texportIfVisible(dailyJamFrequencyPercView.position.display, dailyJamFrequencyPercView.props.dataSources.example, \"DailyJamFrequency_Percentage.csv\")\n\t\texportIfVisible(dailyJamFrequencyRateView.position.display, dailyJamFrequencyRateView.props.dataSources.example, \"DailyJamFrequency_Rate.csv\")\n\telse:\n\t\tpass" + "script": "\tdef exportIfVisible(displayFlag, dataset, filename\u003d\"Export.csv\", multiply \u003d True):\n\t import system\n\t \n\t if not displayFlag:\n\t return\n\t \n\t if dataset is None or dataset.getRowCount() \u003d\u003d 0:\n\t return\n\t \n\t colNames \u003d list(dataset.getColumnNames())\n\t newRows \u003d []\n\t \n\t for rowIndex in range(dataset.getRowCount()):\n\t rowVals \u003d []\n\t for colName in colNames:\n\t val \u003d dataset.getValueAt(rowIndex, colName)\n\t \n\t # If the column name contains “perc” (case-insensitive), multiply by 100\n\t if \"perc\" in colName.lower() and val is not None:\n\t try: \t\n\t if multiply:\n\t \t val \u003d float(val) * 100\n\t \n\t except Exception:\n\t pass\n\t \n\t # Format Startstamp/Endtstamp if needed (you already have this logic)\n\t if colName in (\"Startstamp\", \"Endtstamp\") and val is not None:\n\t try:\n\t if isinstance(val, (int, long, float)):\n\t dt \u003d system.date.fromMillis(val)\n\t else:\n\t dt \u003d val\n\t val \u003d system.date.format(dt, \"yyyy-MM-dd HH:mm:ss\")\n\t except Exception:\n\t pass\n\t \n\t rowVals.append(val)\n\t newRows.append(rowVals)\n\t \n\t formattedDs \u003d system.dataset.toDataSet(colNames, newRows)\n\t csvString \u003d system.dataset.toCSV(formattedDs)\n\t system.perspective.download(filename, csvString, \"Comma Separated Values\")\n\t# Read the current tab index property\n\tstats \u003d self.parent.getChild(\"Statistics\")\n\tcurrentTabIndex \u003d stats.props.currentTabIndex\n\t\n\t# Induct Details\n\tinductDetails \u003d stats.getChild(\"Induct Details\")\n\tcountView \u003d inductDetails.getChild(\"Induct Details Count\")\n\tpercView \u003d inductDetails.getChild(\"Induct Details Perc\")\n\trateView \u003d inductDetails.getChild(\"Induct Details Rate\")\n\t\n\t# Scanner Details\n\tscannerDetails \u003d stats.getChild(\"Scanner_Details\")\n\tscannerCountView \u003d scannerDetails.getChild(\"Scanner Details\")\n\tscannerPercentageView \u003d scannerDetails.getChild(\"Scanner Details Perc\")\n\tscannerRateView \u003d scannerDetails.getChild(\"Scanner Details Rate\")\n\t\n\t# Sorter Details\n\tsorterDetails \u003d stats.getChild(\"Sorter Details\")\n\tsorterCountView \u003d sorterDetails.getChild(\"Sorter Details\")\n\tsorterPercentageView \u003d sorterDetails.getChild(\"Sorter Details Perc\")\n\tsorterRateView \u003d sorterDetails.getChild(\"Sorter Details Rate\")\n\t\n\t# Lane Details\n\tlaneDetails \u003d stats.getChild(\"Lane Details\")\n\tlaneCountView \u003d laneDetails.getChild(\"Lane Details\")\n\tlanePercView \u003d laneDetails.getChild(\"Lane Details Perc\")\n\tlaneRateView \u003d laneDetails.getChild(\"Lane Details Rate\")\n\t\n\t# Hourly Induct\n\thourlyInduct \u003d stats.getChild(\"Hourly_Induct\")\n\thourlyInductCountView \u003d hourlyInduct.getChild(\"Hourly Induct Count\")\n\thourlyInductPercView \u003d hourlyInduct.getChild(\"Hourly Induct Perc\")\n\t\n\t# Hourly Scanner\n\thourlyScanner \u003d stats.getChild(\"Hourly_Scanner\")\n\thourlyScannerCountView \u003d hourlyScanner.getChild(\"Hourly Scanner Count\")\n\thourlyScannerPercView \u003d hourlyScanner.getChild(\"Hourly Scanner Perc\")\n\thourlyScannerRateView \u003d hourlyScanner.getChild(\"Hourly Scanner Rate\")\n\t\n\t# Hourly Sorter Details\n\thourlySorterDetails \u003d stats.getChild(\"Hourly_Sorter_Details\")\n\thourlySorterDetailsCountView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Count\")\n\thourlySorterDetailsPercView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Perc\")\n\thourlySorterDetailsRateView \u003d hourlySorterDetails.getChild(\"Hourly Sorter Details Rate\")\n\t\n\t# Hourly Lane\n\thourlyLane \u003d stats.getChild(\"Hourly_Lane\")\n\thourlyLaneCountView \u003d hourlyLane.getChild(\"Hourly Lane Count\")\n\thourlyLanePercView \u003d hourlyLane.getChild(\"Hourly Lane Perc\")\n\thourlyLaneRateView \u003d hourlyLane.getChild(\"Hourly Lane Rate\")\n\t\n\t# Lane Total Full\n\tlaneTotalFull \u003d stats.getChild(\"Total Full\")\n\tlaneTotalFullCountView \u003d laneTotalFull.getChild(\"Total Full Count\")\n\tlaneTotalFullPercView \u003d laneTotalFull.getChild(\"Total Full Perc\")\n\tlaneTotalFullRateView \u003d laneTotalFull.getChild(\"Total Full Rate\")\n\t\t\n\t# Jam By Area\n\tjamByArea \u003d stats.getChild(\"Jam Area\")\n\tjamByAreaCountView \u003d jamByArea.getChild(\"Jam Area Count\")\n\tjamByArealPercView \u003d jamByArea.getChild(\"Jam Area Percentage\")\n\t\t\n\t# Daily Jam Frequency\n\tdailyJamFrequency \u003d stats.getChild(\"Daily Jam Frequency\")\n\tdailyJamFrequencyCountView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Count\")\n\tdailyJamFrequencyPercView \u003d dailyJamFrequency.getChild(\"Daily Jam Frequency Perc\")\n\n\tif currentTabIndex \u003d\u003d 0:\n\t\texportIfVisible(rateView.position.display, rateView.props.data, \"InductDetails_Rate.csv\")\n\t\texportIfVisible(percView.position.display, percView.props.data, \"InductDetails_Percentage.csv\")\n\t\texportIfVisible(countView.position.display, countView.props.data, \"InductDetails_Count.csv\")\n\telif currentTabIndex \u003d\u003d 1:\n\t\texportIfVisible(scannerCountView.position.display, scannerCountView.props.data, \"ScannerDetails_Count.csv\")\n\t\texportIfVisible(scannerPercentageView.position.display, scannerPercentageView.props.data, \"ScannerDetails_Percentage.csv\")\n\t\texportIfVisible(scannerRateView.position.display, scannerRateView.props.data, \"ScannerDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 2:\n\t\texportIfVisible(sorterCountView.position.display, sorterCountView.props.data, \"SorterDetails_Count.csv\")\n\t\texportIfVisible(sorterPercentageView.position.display, sorterPercentageView.props.data, \"SorterDetails_Percentage.csv\")\n\t\texportIfVisible(sorterRateView.position.display, sorterRateView.props.data, \"SorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 3:\n\t\texportIfVisible(laneCountView.position.display, laneCountView.props.data, \"LaneDetails_Count.csv\")\n\t\texportIfVisible(lanePercView.position.display, lanePercView.props.data, \"LaneDetails_Percentage.csv\")\n\t\texportIfVisible(laneRateView.position.display, laneRateView.props.data, \"LaneDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 4:\n\t\texportIfVisible(hourlyInductCountView.position.display, hourlyInductCountView.props.data, \"HourlyInduct_Count.csv\")\n\t\texportIfVisible(hourlyInductPercView.position.display, hourlyInductPercView.props.data, \"HourlyInduct_Perc.csv\")\n\telif currentTabIndex \u003d\u003d 5:\n\t\texportIfVisible(hourlyScannerCountView.position.display, hourlyScannerCountView.props.data, \"HourlyScanner_Count.csv\")\n\t\texportIfVisible(hourlyScannerPercView.position.display, hourlyScannerPercView.props.data, \"HourlyScanner_Percentage.csv\")\n\t\texportIfVisible(hourlyScannerRateView.position.display, hourlyScannerRateView.props.data, \"HourlyScanner_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 6:\n\t\texportIfVisible(hourlySorterDetailsCountView.position.display, hourlySorterDetailsCountView.props.data, \"HourlySorterDetails_Count.csv\")\n\t\texportIfVisible(hourlySorterDetailsPercView.position.display, hourlySorterDetailsPercView.props.data, \"HourlySorterDetails_Percentage.csv\")\n\t\texportIfVisible(hourlySorterDetailsRateView.position.display, hourlySorterDetailsRateView.props.data, \"HourlySorterDetails_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 7:\n\t\texportIfVisible(hourlyLaneCountView.position.display, hourlyLaneCountView.props.data, \"HourlyLane_Count.csv\")\n\t\texportIfVisible(hourlyLanePercView.position.display, hourlyLanePercView.props.data, \"HourlyLane_Percentage.csv\")\n\t\texportIfVisible(hourlyLaneRateView.position.display, hourlyLaneRateView.props.data, \"HourlyLane_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 8:\n\t\texportIfVisible(laneTotalFullCountView.position.display, laneTotalFullCountView.props.data, \"LaneTotalFull_Count.csv\")\n\t\texportIfVisible(laneTotalFullPercView.position.display, laneTotalFullPercView.props.data, \"LaneTotalFull_Percentage.csv\")\n\t\texportIfVisible(laneTotalFullRateView.position.display, laneTotalFullRateView.props.data, \"LaneTotalFull_Rate.csv\")\n\telif currentTabIndex \u003d\u003d 9:\n\t\texportIfVisible(jamByAreaCountView.position.display, jamByAreaCountView.props.data, \"JamByArea_Count.csv\")\n\t\texportIfVisible(jamByArealPercView.position.display, jamByArealPercView.props.data, \"JamByArea_Percentage.csv\")\n\telif currentTabIndex \u003d\u003d 10:\n\t\texportIfVisible(dailyJamFrequencyCountView.position.display, dailyJamFrequencyCountView.props.dataSources.example, \"DailyJamFrequency_Count.csv\")\n\t\texportIfVisible(dailyJamFrequencyPercView.position.display, dailyJamFrequencyPercView.props.dataSources.example, \"DailyJamFrequency_Percentage.csv\", False)\n\telse:\n\t\tpass" }, "scope": "G", "type": "script" diff --git a/BNA8_autStand/ignition/named-query/Statistics/DailyJamFrequenc/Daily Jam Frequency Percentage/resource.json b/BNA8_autStand/ignition/named-query/Statistics/DailyJamFrequenc/Daily Jam Frequency Percentage/resource.json index ee820858..754ba7fd 100644 --- a/BNA8_autStand/ignition/named-query/Statistics/DailyJamFrequenc/Daily Jam Frequency Percentage/resource.json +++ b/BNA8_autStand/ignition/named-query/Statistics/DailyJamFrequenc/Daily Jam Frequency Percentage/resource.json @@ -18,7 +18,7 @@ "cacheEnabled": false, "database": "MariaDB", "fallbackEnabled": false, - "lastModificationSignature": "b728b24a4e557250abe15c6475a9386995340fcd1acd5ef3dd81ec2a983bad07", + "lastModificationSignature": "c5386e2acc8b8fd40948d7ef5d5a75534f3d7487e80af0ebdc94ad99dc5d2c4c", "permissions": [ { "zone": "", @@ -27,7 +27,7 @@ ], "lastModification": { "actor": "admin", - "timestamp": "2025-11-20T13:24:47Z" + "timestamp": "2025-11-25T10:20:37Z" }, "parameters": [ {