1616
1717package org .labkey .testresults ;
1818
19+ import org .labkey .api .data .Container ;
20+ import org .labkey .api .data .DbScope ;
21+ import org .labkey .api .data .SQLFragment ;
22+ import org .labkey .api .data .SqlExecutor ;
23+ import org .labkey .api .data .SqlSelector ;
24+
25+ import java .sql .Timestamp ;
26+ import java .util .ArrayList ;
27+ import java .util .HashMap ;
28+ import java .util .HashSet ;
29+ import java .util .List ;
30+ import java .util .Map ;
31+ import java .util .Set ;
32+
1933/**
2034 * User: Yuval Boss, yuval(at)uw.edu
2135 * Date: 1/14/2015
@@ -33,4 +47,212 @@ public static TestResultsManager get()
3347 {
3448 return _instance ;
3549 }
36- }
50+
51+ /**
52+ * Get distinct user IDs from recent test runs in a container.
53+ */
54+ public List <Integer > getRecentUserIds (Container container , Timestamp cutoffDate )
55+ {
56+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
57+ SQLFragment sql = new SQLFragment ();
58+ sql .append (
59+ "SELECT DISTINCT userid FROM " + TestResultsSchema .getTableInfoTestRuns () +
60+ " WHERE container = ? AND posttime >= ?" );
61+ sql .add (container .getEntityId ());
62+ sql .add (cutoffDate );
63+
64+ List <Integer > userIds = new ArrayList <>();
65+ new SqlSelector (scope , sql ).forEach (rs -> userIds .add (rs .getInt ("userid" )));
66+ return userIds ;
67+ }
68+
69+ /**
70+ * Get trainrun counts per user for a container.
71+ */
72+ public Map <Integer , Integer > getTrainRunCounts (Container container )
73+ {
74+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
75+ SQLFragment sql = new SQLFragment ();
76+ sql .append (
77+ "SELECT r.userid, COUNT(tr.runid) as traincount" +
78+ " FROM " + TestResultsSchema .getTableInfoTrain () + " tr" +
79+ " JOIN " + TestResultsSchema .getTableInfoTestRuns () + " r ON tr.runid = r.id" +
80+ " WHERE r.container = ?" +
81+ " GROUP BY r.userid" );
82+ sql .add (container .getEntityId ());
83+
84+ Map <Integer , Integer > counts = new HashMap <>();
85+ new SqlSelector (scope , sql ).forEach (rs ->
86+ counts .put (rs .getInt ("userid" ), rs .getInt ("traincount" )));
87+ return counts ;
88+ }
89+
90+ /**
91+ * Delete all trainruns for a container.
92+ */
93+ public void deleteTrainRunsForContainer (Container container )
94+ {
95+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
96+ SQLFragment sql = new SQLFragment ();
97+ sql .append (
98+ "DELETE FROM " + TestResultsSchema .getTableInfoTrain () +
99+ " WHERE runid IN (SELECT id FROM " + TestResultsSchema .getTableInfoTestRuns () +
100+ " WHERE container = ?)" );
101+ sql .add (container .getEntityId ());
102+ new SqlExecutor (scope ).execute (sql );
103+ }
104+
105+ /**
106+ * Delete all userdata for a container.
107+ */
108+ public void deleteUserDataForContainer (Container container )
109+ {
110+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
111+ SQLFragment sql = new SQLFragment ();
112+ sql .append ("DELETE FROM " + TestResultsSchema .getTableInfoUserData () + " WHERE container = ?" );
113+ sql .add (container .getEntityId ());
114+ new SqlExecutor (scope ).execute (sql );
115+ }
116+
117+ /**
118+ * Get existing trainrun IDs for a user in a container.
119+ */
120+ public Set <Integer > getExistingTrainRunIds (int userId , Container container )
121+ {
122+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
123+ SQLFragment sql = new SQLFragment ();
124+ sql .append (
125+ "SELECT tr.runid FROM " + TestResultsSchema .getTableInfoTrain () + " tr" +
126+ " JOIN " + TestResultsSchema .getTableInfoTestRuns () + " r ON tr.runid = r.id" +
127+ " WHERE r.userid = ? AND r.container = ?" );
128+ sql .add (userId );
129+ sql .add (container .getEntityId ());
130+
131+ Set <Integer > runIds = new HashSet <>();
132+ new SqlSelector (scope , sql ).forEach (rs -> runIds .add (rs .getInt ("runid" )));
133+ return runIds ;
134+ }
135+
136+ /**
137+ * Get clean run IDs for a user within lookback period.
138+ * Clean = 0 failures, 0 leaks, passedtests > 0, not flagged, full duration, no hangs.
139+ */
140+ public List <Integer > getCleanRunIds (int userId , Container container , Timestamp cutoffDate , int expectedDuration )
141+ {
142+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
143+ SQLFragment sql = new SQLFragment ();
144+ sql .append (
145+ "SELECT tr.id FROM " + TestResultsSchema .getTableInfoTestRuns () + " tr" +
146+ " WHERE tr.userid = ? AND tr.container = ?" +
147+ " AND tr.posttime >= ?" +
148+ " AND tr.failedtests = 0 AND tr.leakedtests = 0" +
149+ " AND tr.passedtests > 0 AND tr.flagged = false" +
150+ " AND tr.duration >= ?" +
151+ " AND NOT EXISTS (SELECT 1 FROM " + TestResultsSchema .getTableInfoHangs () +
152+ " h WHERE h.testrunid = tr.id)" +
153+ " ORDER BY tr.posttime DESC" );
154+ sql .add (userId );
155+ sql .add (container .getEntityId ());
156+ sql .add (cutoffDate );
157+ sql .add (expectedDuration );
158+
159+ List <Integer > runIds = new ArrayList <>();
160+ new SqlSelector (scope , sql ).forEach (rs -> runIds .add (rs .getInt ("id" )));
161+ return runIds ;
162+ }
163+
164+ /**
165+ * Get combined candidate run IDs (existing + recent) sorted by posttime, limited to maxRuns.
166+ */
167+ public List <Integer > getCandidateRunIds (int userId , Container container ,
168+ Set <Integer > existingIds , List <Integer > recentIds , int maxRuns )
169+ {
170+ if (existingIds .isEmpty () && recentIds .isEmpty ())
171+ return new ArrayList <>();
172+
173+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
174+ SQLFragment sql = new SQLFragment ();
175+ sql .append (
176+ "SELECT id FROM " + TestResultsSchema .getTableInfoTestRuns () +
177+ " WHERE userid = ? AND container = ?" +
178+ " AND (id = ANY(?) OR id = ANY(?))" +
179+ " ORDER BY posttime DESC" );
180+ sql .add (userId );
181+ sql .add (container .getEntityId ());
182+ sql .add (existingIds .toArray (new Integer [0 ]));
183+ sql .add (recentIds .toArray (new Integer [0 ]));
184+
185+ List <Integer > result = new ArrayList <>();
186+ new SqlSelector (scope , sql ).forEach (rs -> {
187+ if (result .size () < maxRuns )
188+ result .add (rs .getInt ("id" ));
189+ });
190+ return result ;
191+ }
192+
193+ /**
194+ * Delete trainruns by run IDs.
195+ */
196+ public void removeTrainRuns (List <Integer > runIds )
197+ {
198+ if (runIds .isEmpty ())
199+ return ;
200+
201+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
202+ for (int runId : runIds )
203+ {
204+ SQLFragment sql = new SQLFragment ();
205+ sql .append ("DELETE FROM " + TestResultsSchema .getTableInfoTrain () + " WHERE runid = ?" );
206+ sql .add (runId );
207+ new SqlExecutor (scope ).execute (sql );
208+ }
209+ }
210+
211+ /**
212+ * Insert trainruns by run IDs.
213+ */
214+ public void addTrainRuns (List <Integer > runIds )
215+ {
216+ if (runIds .isEmpty ())
217+ return ;
218+
219+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
220+ for (int runId : runIds )
221+ {
222+ SQLFragment sql = new SQLFragment ();
223+ sql .append ("INSERT INTO " + TestResultsSchema .getTableInfoTrain () + " (runid) VALUES (?)" );
224+ sql .add (runId );
225+ new SqlExecutor (scope ).execute (sql );
226+ }
227+ }
228+
229+ /**
230+ * Upsert userdata with calculated stats from specified run IDs.
231+ */
232+ public void upsertUserData (int userId , Container container , List <Integer > runIds , boolean active )
233+ {
234+ if (runIds .isEmpty ())
235+ return ;
236+
237+ DbScope scope = TestResultsSchema .getSchema ().getScope ();
238+ SQLFragment sql = new SQLFragment ();
239+ sql .append (
240+ "INSERT INTO " + TestResultsSchema .getTableInfoUserData () +
241+ " (userid, container, meantestsrun, meanmemory, stddevtestsrun, stddevmemory, active)" +
242+ " SELECT ?, ?, avg(passedtests), avg(averagemem)," +
243+ " stddev_pop(passedtests), stddev_pop(averagemem), ?" +
244+ " FROM " + TestResultsSchema .getTableInfoTestRuns () +
245+ " WHERE id = ANY(?)" +
246+ " ON CONFLICT(userid, container) DO UPDATE SET" +
247+ " meantestsrun = excluded.meantestsrun," +
248+ " meanmemory = excluded.meanmemory," +
249+ " stddevtestsrun = excluded.stddevtestsrun," +
250+ " stddevmemory = excluded.stddevmemory," +
251+ " active = excluded.active" );
252+ sql .add (userId );
253+ sql .add (container .getEntityId ());
254+ sql .add (active );
255+ sql .add (runIds .toArray (new Integer [0 ]));
256+ new SqlExecutor (scope ).execute (sql );
257+ }
258+ }
0 commit comments