fix top priority tests
This commit is contained in:
+17
-1
@@ -85,6 +85,8 @@ class CompileScheduleRequest(BaseModel):
|
|||||||
daytime_testing_today: bool = False
|
daytime_testing_today: bool = False
|
||||||
dual_device_weekend_start_enabled: bool = False
|
dual_device_weekend_start_enabled: bool = False
|
||||||
dual_device_weekend_start_dates: list[str] = Field(default_factory=list)
|
dual_device_weekend_start_dates: list[str] = Field(default_factory=list)
|
||||||
|
top_priority_tests_dut: list[str] = Field(default_factory=list)
|
||||||
|
top_priority_tests_ref: list[str] = Field(default_factory=list)
|
||||||
top_priority_tests: list[str] = Field(default_factory=list)
|
top_priority_tests: list[str] = Field(default_factory=list)
|
||||||
lowest_priority_tests: list[str] = Field(default_factory=list)
|
lowest_priority_tests: list[str] = Field(default_factory=list)
|
||||||
|
|
||||||
@@ -394,9 +396,23 @@ def compile_schedule_endpoint(request: CompileScheduleRequest) -> dict[str, Any]
|
|||||||
|
|
||||||
holiday_dates = db.list_holidays(DB_PATH)
|
holiday_dates = db.list_holidays(DB_PATH)
|
||||||
top_priority_pairs: set[tuple[str, str]] = set()
|
top_priority_pairs: set[tuple[str, str]] = set()
|
||||||
for test_id in {item.strip() for item in request.top_priority_tests if item.strip()}:
|
dut_top_priority_ids = {item.strip() for item in request.top_priority_tests_dut if item.strip()}
|
||||||
|
ref_top_priority_ids = {item.strip() for item in request.top_priority_tests_ref if item.strip()}
|
||||||
|
|
||||||
|
# Backward compatibility: if only legacy top_priority_tests was sent,
|
||||||
|
# keep previous behavior by applying IDs to whichever device has that test.
|
||||||
|
if not dut_top_priority_ids and not ref_top_priority_ids and request.top_priority_tests:
|
||||||
|
for test_id in {item.strip() for item in request.top_priority_tests if item.strip()}:
|
||||||
|
if any(t.test_id == test_id and t.device == DUT for t in stored_tests):
|
||||||
|
dut_top_priority_ids.add(test_id)
|
||||||
|
if any(t.test_id == test_id and t.device == REF for t in stored_tests):
|
||||||
|
ref_top_priority_ids.add(test_id)
|
||||||
|
|
||||||
|
for test_id in dut_top_priority_ids:
|
||||||
if any(t.test_id == test_id and t.device == DUT for t in stored_tests):
|
if any(t.test_id == test_id and t.device == DUT for t in stored_tests):
|
||||||
top_priority_pairs.add((test_id, DUT))
|
top_priority_pairs.add((test_id, DUT))
|
||||||
|
|
||||||
|
for test_id in ref_top_priority_ids:
|
||||||
if any(t.test_id == test_id and t.device == REF for t in stored_tests):
|
if any(t.test_id == test_id and t.device == REF for t in stored_tests):
|
||||||
top_priority_pairs.add((test_id, REF))
|
top_priority_pairs.add((test_id, REF))
|
||||||
|
|
||||||
|
|||||||
+37
-7
@@ -4,7 +4,7 @@ import os
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from test_window import get_shift_sequence_with_capacity, get_shift_capacity_for_date, is_off_day, next_window_start_date
|
from test_window import get_shift_sequence_with_capacity, get_shift_capacity_for_date, is_off_day, next_window_start_date
|
||||||
from test_bundle import Test, TestBundle, build_test_bundles, bundle_pair_lookup
|
from test_bundle import BUNDLE_PRIORITY_TOP, Test, TestBundle, build_test_bundles, bundle_pair_lookup
|
||||||
|
|
||||||
DUT = (os.getenv("DUT") or "DUT").strip()
|
DUT = (os.getenv("DUT") or "DUT").strip()
|
||||||
REF = (os.getenv("REF") or "REF").strip()
|
REF = (os.getenv("REF") or "REF").strip()
|
||||||
@@ -84,17 +84,47 @@ class Scheduler:
|
|||||||
window_index = 0
|
window_index = 0
|
||||||
cursor_date = self.start_date
|
cursor_date = self.start_date
|
||||||
|
|
||||||
# Sort TC keys by bundle count (largest first); tie-break by TC name.
|
# Process TCs that include top-priority bundles first.
|
||||||
|
# REF top-priority TCs come before DUT top-priority TCs so that
|
||||||
|
# entering tests in the REF top-priority field always claims tonight's
|
||||||
|
# first window for REF.
|
||||||
|
def tc_sort_key(item: tuple[str | None, list[TestBundle]]) -> tuple[int, int, int, str]:
|
||||||
|
tc, tc_bundles = item
|
||||||
|
has_ref_top = any(bundle.priority == BUNDLE_PRIORITY_TOP and bundle.device == REF for bundle in tc_bundles)
|
||||||
|
has_dut_top = any(bundle.priority == BUNDLE_PRIORITY_TOP and bundle.device == DUT for bundle in tc_bundles)
|
||||||
|
min_priority = min((bundle.priority for bundle in tc_bundles), default=BUNDLE_PRIORITY_TOP + 99)
|
||||||
|
tc_name = tc if tc is not None else "~"
|
||||||
|
if has_dut_top:
|
||||||
|
tier = 0 # DUT top-priority TCs first (wins when both exist)
|
||||||
|
elif has_ref_top:
|
||||||
|
tier = 1 # REF-only top-priority TCs second
|
||||||
|
else:
|
||||||
|
tier = 2 # All other TCs
|
||||||
|
return (tier, min_priority, -len(tc_bundles), tc_name)
|
||||||
|
|
||||||
sorted_tc_items = sorted(
|
sorted_tc_items = sorted(
|
||||||
[(tc, bundles_by_tc[tc]) for tc in bundles_by_tc if tc is not None],
|
[(tc, bundles_by_tc[tc]) for tc in bundles_by_tc],
|
||||||
key=lambda x: (-len(x[1]), x[0]),
|
key=tc_sort_key,
|
||||||
)
|
)
|
||||||
if None in bundles_by_tc:
|
|
||||||
sorted_tc_items.append((None, bundles_by_tc[None]))
|
|
||||||
|
|
||||||
|
|
||||||
for tc, tc_bundles in sorted_tc_items:
|
for tc, tc_bundles in sorted_tc_items:
|
||||||
window_device = DUT
|
tc_has_ref_top_priority = any(
|
||||||
|
bundle.device == REF and bundle.priority == BUNDLE_PRIORITY_TOP
|
||||||
|
for bundle in tc_bundles
|
||||||
|
)
|
||||||
|
tc_has_dut_top_priority = any(
|
||||||
|
bundle.device == DUT and bundle.priority == BUNDLE_PRIORITY_TOP
|
||||||
|
for bundle in tc_bundles
|
||||||
|
)
|
||||||
|
|
||||||
|
if tc_has_ref_top_priority:
|
||||||
|
window_device = REF
|
||||||
|
elif tc_has_dut_top_priority:
|
||||||
|
window_device = DUT
|
||||||
|
else:
|
||||||
|
window_device = DUT
|
||||||
|
|
||||||
dut_unscheduled = [b for b in tc_bundles if b.device == DUT]
|
dut_unscheduled = [b for b in tc_bundles if b.device == DUT]
|
||||||
ref_unscheduled = [b for b in tc_bundles if b.device == REF]
|
ref_unscheduled = [b for b in tc_bundles if b.device == REF]
|
||||||
|
|
||||||
|
|||||||
+27
-9
@@ -165,7 +165,8 @@ export default function App() {
|
|||||||
const [settingsOpen, setSettingsOpen] = useState(false)
|
const [settingsOpen, setSettingsOpen] = useState(false)
|
||||||
const [settings, setSettings] = useState(DEFAULT_SETTINGS)
|
const [settings, setSettings] = useState(DEFAULT_SETTINGS)
|
||||||
const [dualDeviceWeekendWeekSelections, setDualDeviceWeekendWeekSelections] = useState({})
|
const [dualDeviceWeekendWeekSelections, setDualDeviceWeekendWeekSelections] = useState({})
|
||||||
const [topPriority, setTopPriority] = useState('')
|
const [topPriorityDut, setTopPriorityDut] = useState('')
|
||||||
|
const [topPriorityRef, setTopPriorityRef] = useState('')
|
||||||
const [lowestPriority, setLowestPriority] = useState('')
|
const [lowestPriority, setLowestPriority] = useState('')
|
||||||
const [startDateOverride, setStartDateOverride] = useState('')
|
const [startDateOverride, setStartDateOverride] = useState('')
|
||||||
const [failedTests, setFailedTests] = useState([])
|
const [failedTests, setFailedTests] = useState([])
|
||||||
@@ -298,7 +299,8 @@ export default function App() {
|
|||||||
const result = await api.compileSchedule({
|
const result = await api.compileSchedule({
|
||||||
start_date: effectiveStartDate || null,
|
start_date: effectiveStartDate || null,
|
||||||
dual_device_weekend_start_dates: dualDeviceWindowStartDates,
|
dual_device_weekend_start_dates: dualDeviceWindowStartDates,
|
||||||
top_priority_tests: topPriority.split(',').map(s => s.trim()).filter(Boolean),
|
top_priority_tests_dut: topPriorityDut.split(',').map(s => s.trim()).filter(Boolean),
|
||||||
|
top_priority_tests_ref: topPriorityRef.split(',').map(s => s.trim()).filter(Boolean),
|
||||||
lowest_priority_tests: lowestPriority.split(',').map(s => s.trim()).filter(Boolean),
|
lowest_priority_tests: lowestPriority.split(',').map(s => s.trim()).filter(Boolean),
|
||||||
rule: settings.testExclusion ?? '',
|
rule: settings.testExclusion ?? '',
|
||||||
})
|
})
|
||||||
@@ -326,12 +328,26 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleRerunDecision(_rerunDuringDay) {
|
function handleRerunDecision(_rerunDuringDay) {
|
||||||
setTopPriority(prev => {
|
const appendUnique = (currentValue, incomingValues) => {
|
||||||
const existing = prev.split(',').map(s => s.trim()).filter(Boolean)
|
const existing = currentValue.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
const incoming = failedTests.map(t => t.test_id)
|
const merged = [...new Set([...existing, ...incomingValues])]
|
||||||
const merged = [...new Set([...existing, ...incoming])]
|
|
||||||
return merged.join(', ')
|
return merged.join(', ')
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const dutIncoming = failedTests
|
||||||
|
.filter(test => test.device === 'CGW453')
|
||||||
|
.map(test => test.test_id)
|
||||||
|
const refIncoming = failedTests
|
||||||
|
.filter(test => test.device === 'CGW452')
|
||||||
|
.map(test => test.test_id)
|
||||||
|
|
||||||
|
if (dutIncoming.length > 0) {
|
||||||
|
setTopPriorityDut(prev => appendUnique(prev, dutIncoming))
|
||||||
|
}
|
||||||
|
if (refIncoming.length > 0) {
|
||||||
|
setTopPriorityRef(prev => appendUnique(prev, refIncoming))
|
||||||
|
}
|
||||||
|
|
||||||
setFailedTests([])
|
setFailedTests([])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,8 +406,10 @@ export default function App() {
|
|||||||
completionDate={completionDate}
|
completionDate={completionDate}
|
||||||
startDateOverride={startDateOverride}
|
startDateOverride={startDateOverride}
|
||||||
onStartDateOverrideChange={setStartDateOverride}
|
onStartDateOverrideChange={setStartDateOverride}
|
||||||
topPriority={topPriority}
|
topPriorityDut={topPriorityDut}
|
||||||
onTopPriorityChange={setTopPriority}
|
onTopPriorityDutChange={setTopPriorityDut}
|
||||||
|
topPriorityRef={topPriorityRef}
|
||||||
|
onTopPriorityRefChange={setTopPriorityRef}
|
||||||
lowestPriority={lowestPriority}
|
lowestPriority={lowestPriority}
|
||||||
onLowestPriorityChange={setLowestPriority}
|
onLowestPriorityChange={setLowestPriority}
|
||||||
onRemakeSchedule={handleRemakeSchedule}
|
onRemakeSchedule={handleRemakeSchedule}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ export default function RightPanel({
|
|||||||
completionDate,
|
completionDate,
|
||||||
startDateOverride,
|
startDateOverride,
|
||||||
onStartDateOverrideChange,
|
onStartDateOverrideChange,
|
||||||
topPriority,
|
topPriorityDut,
|
||||||
onTopPriorityChange,
|
onTopPriorityDutChange,
|
||||||
|
topPriorityRef,
|
||||||
|
onTopPriorityRefChange,
|
||||||
lowestPriority,
|
lowestPriority,
|
||||||
onLowestPriorityChange,
|
onLowestPriorityChange,
|
||||||
onRemakeSchedule,
|
onRemakeSchedule,
|
||||||
@@ -48,12 +50,25 @@ export default function RightPanel({
|
|||||||
{/* Top priority */}
|
{/* Top priority */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-[11px] font-semibold uppercase tracking-wider text-gray-500 mb-1.5">
|
<label className="block text-[11px] font-semibold uppercase tracking-wider text-gray-500 mb-1.5">
|
||||||
Top Priority Tests
|
Top Priority Tests (DUT - CGW453)
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
rows={3}
|
rows={3}
|
||||||
value={topPriority}
|
value={topPriorityDut}
|
||||||
onChange={(e) => onTopPriorityChange(e.target.value)}
|
onChange={(e) => onTopPriorityDutChange(e.target.value)}
|
||||||
|
placeholder="P2PRXAX001, COERXBE002…"
|
||||||
|
className="w-full bg-gray-900 border border-gray-600 rounded-md px-2.5 py-1.5 text-xs text-gray-200 placeholder-gray-600 focus:outline-none focus:border-blue-500 resize-none font-mono"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-[11px] font-semibold uppercase tracking-wider text-gray-500 mb-1.5">
|
||||||
|
Top Priority Tests (REF - CGW452)
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
rows={3}
|
||||||
|
value={topPriorityRef}
|
||||||
|
onChange={(e) => onTopPriorityRefChange(e.target.value)}
|
||||||
placeholder="P2PRXAX001, COERXBE002…"
|
placeholder="P2PRXAX001, COERXBE002…"
|
||||||
className="w-full bg-gray-900 border border-gray-600 rounded-md px-2.5 py-1.5 text-xs text-gray-200 placeholder-gray-600 focus:outline-none focus:border-blue-500 resize-none font-mono"
|
className="w-full bg-gray-900 border border-gray-600 rounded-md px-2.5 py-1.5 text-xs text-gray-200 placeholder-gray-600 focus:outline-none focus:border-blue-500 resize-none font-mono"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user