111111import java .util .Date ;
112112import java .util .Enumeration ;
113113import java .util .HashMap ;
114+ import java .util .HashSet ;
115+ import java .util .Set ;
114116import java .util .List ;
115117import java .util .Locale ;
116118import java .util .Map ;
@@ -133,6 +135,60 @@ public class TestResultsController extends SpringActionController
133135
134136 private static final DefaultActionResolver _actionResolver = new DefaultActionResolver (TestResultsController .class );
135137
138+ // Tab name constants for menu highlighting
139+ public static class TabNames
140+ {
141+ // Request attribute name
142+ public static final String ACTIVE_TAB_ATTR = "activeTab" ;
143+ // CSS classes
144+ private static final String NAV_TAB_CLASS = "nav-tab" ;
145+ private static final String ACTIVE_CSS_CLASS = " active" ;
146+
147+ // Tab identifiers
148+ public static final String OVERVIEW = "overview" ;
149+ public static final String USER = "user" ;
150+ public static final String RUN = "run" ;
151+ public static final String LONGTERM = "longterm" ;
152+ public static final String FLAGS = "flags" ;
153+ public static final String TRAINING_DATA = "trainingdata" ;
154+ public static final String ERRORS = "errors" ;
155+
156+ /** Returns CSS class for a tab link: "nav-tab" or "nav-tab active" */
157+ public static String getTabClass (String tabName , String activeTab )
158+ {
159+ return NAV_TAB_CLASS + (tabName .equals (activeTab ) ? ACTIVE_CSS_CLASS : "" );
160+ }
161+ }
162+
163+ // Form class for RetrainAllAction
164+ public static class RetrainAllForm
165+ {
166+ private String _mode = "reset" ;
167+ private int _maxRuns = 20 ;
168+ private int _minRuns = 5 ;
169+ private Integer _targetRuns ; // backwards compatibility
170+
171+ public String getMode () { return _mode ; }
172+ public void setMode (String mode ) { _mode = mode ; }
173+
174+ public int getMaxRuns ()
175+ {
176+ // Support legacy targetRuns parameter
177+ if (_targetRuns != null )
178+ return _targetRuns ;
179+ return _maxRuns ;
180+ }
181+ public void setMaxRuns (int maxRuns ) { _maxRuns = maxRuns ; }
182+
183+ public int getMinRuns () { return _minRuns ; }
184+ public void setMinRuns (int minRuns ) { _minRuns = minRuns ; }
185+
186+ public Integer getTargetRuns () { return _targetRuns ; }
187+ public void setTargetRuns (Integer targetRuns ) { _targetRuns = targetRuns ; }
188+
189+ public boolean isIncremental () { return "incremental" .equalsIgnoreCase (_mode ); }
190+ }
191+
136192 public TestResultsController ()
137193 {
138194 setActionResolver (_actionResolver );
@@ -1037,6 +1093,110 @@ public Object execute(Object o, BindException errors)
10371093 }
10381094 }
10391095
1096+ @ RequiresSiteAdmin
1097+ public static class RetrainAllAction extends MutatingApiAction <RetrainAllForm >
1098+ {
1099+ @ Override
1100+ public Object execute (RetrainAllForm form , BindException errors )
1101+ {
1102+ // Validate and clamp parameters
1103+ int maxRuns = Math .max (1 , Math .min (100 , form .getMaxRuns ()));
1104+ int minRuns = Math .max (1 , Math .min (maxRuns , form .getMinRuns ()));
1105+ boolean incremental = form .isIncremental ();
1106+
1107+ Container c = getContainer ();
1108+ String containerPath = c .getPath ();
1109+ int expectedDuration = containerPath .toLowerCase ().contains ("perf" ) ? 720 : 540 ;
1110+
1111+ // Only look back 1.5x maxRuns days to avoid ancient data
1112+ int lookbackDays = (int ) Math .ceil (maxRuns * 1.5 );
1113+ java .sql .Timestamp cutoffDate = new java .sql .Timestamp (
1114+ System .currentTimeMillis () - (long ) lookbackDays * 24 * 60 * 60 * 1000 );
1115+
1116+ TestResultsManager mgr = TestResultsManager .get ();
1117+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
1118+
1119+ try (DbScope .Transaction transaction = scope .ensureTransaction ())
1120+ {
1121+ List <Integer > allUserIds = mgr .getRecentUserIds (c , cutoffDate );
1122+
1123+ if (!incremental )
1124+ {
1125+ mgr .deleteTrainRunsForContainer (c );
1126+ mgr .deleteUserDataForContainer (c );
1127+ }
1128+
1129+ int usersRetrained = 0 ;
1130+ int totalTrainRuns = 0 ;
1131+
1132+ for (int userId : allUserIds )
1133+ {
1134+ Set <Integer > existingRunIds = mgr .getExistingTrainRunIds (userId , c );
1135+ List <Integer > recentCleanRunIds = mgr .getCleanRunIds (userId , c , cutoffDate , expectedDuration );
1136+
1137+ // Determine final set of trainrun IDs
1138+ List <Integer > finalRunIds ;
1139+ if (incremental && !existingRunIds .isEmpty ())
1140+ {
1141+ finalRunIds = mgr .getCandidateRunIds (userId , c , existingRunIds , recentCleanRunIds , maxRuns );
1142+ }
1143+ else
1144+ {
1145+ finalRunIds = recentCleanRunIds .size () > maxRuns
1146+ ? new ArrayList <>(recentCleanRunIds .subList (0 , maxRuns ))
1147+ : new ArrayList <>(recentCleanRunIds );
1148+ }
1149+
1150+ // Skip if total runs is below minimum threshold
1151+ if (finalRunIds .size () < minRuns )
1152+ continue ;
1153+
1154+ // Determine runs to add and remove
1155+ Set <Integer > finalRunSet = new HashSet <>(finalRunIds );
1156+ List <Integer > runsToAdd = new ArrayList <>();
1157+ List <Integer > runsToRemove = new ArrayList <>();
1158+
1159+ for (int runId : finalRunIds )
1160+ {
1161+ if (!existingRunIds .contains (runId ))
1162+ runsToAdd .add (runId );
1163+ }
1164+ for (int runId : existingRunIds )
1165+ {
1166+ if (!finalRunSet .contains (runId ))
1167+ runsToRemove .add (runId );
1168+ }
1169+
1170+ mgr .removeTrainRuns (runsToRemove );
1171+ mgr .addTrainRuns (runsToAdd );
1172+
1173+ boolean isActive = finalRunIds .size () >= maxRuns ;
1174+ mgr .upsertUserData (userId , c , finalRunIds , isActive );
1175+
1176+ usersRetrained ++;
1177+ totalTrainRuns += runsToAdd .size ();
1178+ }
1179+
1180+ transaction .commit ();
1181+
1182+ ApiSimpleResponse response = new ApiSimpleResponse ();
1183+ response .put ("success" , true );
1184+ response .put ("usersRetrained" , usersRetrained );
1185+ response .put ("totalTrainRuns" , totalTrainRuns );
1186+ response .put ("mode" , form .getMode ());
1187+ return response ;
1188+ }
1189+ catch (Exception e )
1190+ {
1191+ _log .error ("Error in RetrainAllAction" , e );
1192+ ApiSimpleResponse response = new ApiSimpleResponse ();
1193+ response .put ("success" , false );
1194+ response .put ("error" , e .getMessage ());
1195+ return response ;
1196+ }
1197+ }
1198+ }
1199+
10401200 /**
10411201 * action for posting test output as an xml file
10421202 */
0 commit comments