Inheritance: WebServiceResponse
		public FrontPageResponse GetFrontPageDataWithTags (WebServiceLogin login, int page_size, int page, string [] lanes, int [] lane_ids, int latest_days, string[] tags)
		{
			FrontPageResponse response = new FrontPageResponse ();
			List<DBLane> Lanes;
			List<DBHost> Hosts;
			List<DBHostLane> HostLanes;
			List<int> TaggedLaneIds = null;

			log.DebugFormat ("GetFrontPageDataWithTags (page_size: {0} page: {1} lanes: {2} lane_ids: {3} latest_days: {4} tags: {5})",
				page_size, page, lanes == null ? "null" : lanes.Length.ToString (), lane_ids == null ? "null" : lane_ids.Length.ToString (), latest_days, tags == null ? "null" : tags.Length.ToString ());

			page_size = Math.Min (page_size, 500);

			string single_lane = string.Empty;
			if ((lanes != null && lanes.Length == 1))
				single_lane = lanes [0];

			using (DB db = new DB ()) {
				Authenticate (db, login, response, true);

				using (IDbCommand cmd = db.CreateCommand ()) {
					var latest_only = latest_days != 0;
					var last_month = string.Empty;

					if (!string.IsNullOrEmpty (single_lane)) {
						// this will ignore the @afterdate condition below if the selected lane is not a parent of other lanes
						last_month = " AND (NOT EXISTS (SELECT id FROM Lane WHERE parent_lane_id = (SELECT id FROM Lane WHERE lane = @single_lane)) OR \n";
						DB.CreateParameter (cmd, "single_lane", single_lane);
					} else {
						last_month = " AND (";
					}

					last_month += @"
		(
			EXISTS (SELECT id FROM Revision WHERE date > @afterdate AND lane_id = Lane.id)
				OR
			EXISTS (SELECT id FROM RevisionWork WHERE lane_id = Lane.id AND ((completed = TRUE AND endtime > @afterdate) OR (state <> 9 AND state <> 11 AND completed = FALSE)))
				OR
			NOT EXISTS (SELECT id FROM Revision WHERE lane_id = Lane.id)
				OR
			EXISTS (SELECT id FROM Lane AS ParentLane WHERE ParentLane.parent_lane_id = Lane.id)
		))";
					/*
					 */

					cmd.CommandText = "SELECT * FROM Lane WHERE enabled = TRUE";
					if (latest_only)
						cmd.CommandText += last_month;
					cmd.CommandText += ";\n";
					cmd.CommandText += "SELECT * FROM Host;\n";
					cmd.CommandText += @"
SELECT HostLane.*
FROM HostLane
INNER JOIN Lane ON Lane.id = HostLane.lane_id
WHERE hidden = false AND Lane.enabled = TRUE";
					if (latest_only)
						cmd.CommandText += last_month;
					cmd.CommandText += ";\n";
					if (latest_only)
						DB.CreateParameter (cmd, "afterdate", DateTime.Now.AddDays (-latest_days));
					if (tags != null && tags.Length > 0) {
						cmd.CommandText += "SELECT DISTINCT lane_id FROM LaneTag WHERE ";
						for (int i = 0; i < tags.Length; i++) {
							if (i > 0)
								cmd.CommandText += " OR ";
							cmd.CommandText += " tag = @tag" + i.ToString ();
							DB.CreateParameter (cmd, "tag" + i.ToString (), tags [i]);
						}
						cmd.CommandText += ";";
					}
					cmd.CommandText = "SET enable_seqscan = false;\n" + cmd.CommandText + "\nSET enable_seqscan = true;\n";
					using (IDataReader reader = cmd.ExecuteReader ()) {
						Lanes = DBRecord.LoadMany<DBLane> (reader);

						reader.NextResult ();
						Hosts = DBRecord.LoadMany<DBHost> (reader);

						reader.NextResult ();
						HostLanes = DBRecord.LoadMany<DBHostLane> (reader);

						if (tags != null && tags.Length > 0) {
							reader.NextResult ();
							TaggedLaneIds = new List<int> (tags.Length);
							while (reader.Read ())
								TaggedLaneIds.Add (reader.GetInt32 (0));
						}
					}
				}

				// get a list of the lanes to show
				// note that the logic here is slightly different from the usual "string lane, int? lane_id" logic in other methods,
				// where we only use the string parameter if the id parameter isn't provided, here we add everything we can to the 
				// list of selected lanes, so if you provide both a string and an id parameter both are used (assuming they correspond
				// with different lanes of course).
				response.SelectedLanes = Lanes.FindAll (delegate (DBLane l) {
					if (lane_ids != null) {
						for (int i = 0; i < lane_ids.Length; i++) {
							if (lane_ids [i] == l.id)
								return true;
						}
					}
					if (lanes != null) {
						for (int i = 0; i < lanes.Length; i++) {
							if (!string.IsNullOrEmpty (lanes [i]) && lanes [i] == l.lane)
								return true;
						}
					}
					if (TaggedLaneIds != null) {
						if (TaggedLaneIds.Contains (l.id))
							return true;
					}
					return false;
				});

				log.DebugFormat ("We have {0} selected lanes", response.SelectedLanes.Count);

				// backwards compat
				if (response.SelectedLanes.Count == 1)
					response.Lane = response.SelectedLanes [0];

				response.RevisionWorkViews = new List<List<DBRevisionWorkView2>> (HostLanes.Count);
				response.RevisionWorkHostLaneRelation = new List<int> (HostLanes.Count);

				if (HostLanes.Count > 0) {
					using (IDbCommand cmd = db.CreateCommand ()) {
						// FIXME: use this instead: https://gist.github.com/rolfbjarne/cf73bf22209c8a8ef844

						for (int i = 0; i < HostLanes.Count; i++) {
							DBHostLane hl = HostLanes [i];

							var stri = i.ToString ();
							cmd.CommandText += @"SELECT R.* FROM (" + DBRevisionWorkView2.SQL.Replace (';', ' ') + ") AS R WHERE " +
								"R.host_id = @host_id" + stri + " AND R.lane_id = @lane_id" + stri + " LIMIT @limit OFFSET @offset;\n";
							DB.CreateParameter (cmd, "host_id" + stri, hl.host_id);
							DB.CreateParameter (cmd, "lane_id" + stri, hl.lane_id);

							response.RevisionWorkHostLaneRelation.Add (hl.id);
						}

						DB.CreateParameter (cmd, "limit", page_size);
						DB.CreateParameter (cmd, "offset", page * page_size);

						using (IDataReader reader = cmd.ExecuteReader ()) {
							do {
								response.RevisionWorkViews.Add (DBRecord.LoadMany<DBRevisionWorkView2> (reader));
							} while (reader.NextResult ());
						}
					}
				}

				// Create a list of all the lanes which have hostlanes
				var enabled_set = new HashSet<int> ();
				foreach (DBHostLane hl in HostLanes) {
					if (enabled_set.Contains (hl.lane_id))
						continue;
					enabled_set.Add (hl.lane_id);

					// Walk up the tree of parent lanes, marking all the parents too
					var l = Lanes.FirstOrDefault ((v) => v.id == hl.lane_id);
					if (l == null) {
						log.WarnFormat ("GetFrontPageDataWithTags: could not find lane {0} for host lane {1}", hl.lane_id, hl.id);
						l = DBLane_Extensions.Create (db, hl.lane_id);
						l.enabled = true; // This will prevent us from having to load the lane manually again.
						l.Save (db);
						Lanes.Add (l);
						continue;
					}
					while (true) {
						if (!l.parent_lane_id.HasValue)
							break;

						if (enabled_set.Contains (l.parent_lane_id.Value))
							break;

						enabled_set.Add (l.parent_lane_id.Value);

						var old_l = l;
						l = Lanes.FirstOrDefault ((v) => v.id == l.parent_lane_id.Value);
						if (l == null) {
							log.WarnFormat ("GetFrontPageDataWithTags: could not find parent lane {0} for lane {1} (host lane {2})", old_l.parent_lane_id.Value, old_l.id, hl.id);
							l = DBLane_Extensions.Create (db, old_l.parent_lane_id.Value);
							l.enabled = true; // This will prevent us from having to load the lane manually again.
							l.Save (db);
							Lanes.Add (l);
							break;
						}
					}
				}

				// Remove the lanes which aren't marked
				for (int i = Lanes.Count - 1; i >= 0; i--) {
					if (!enabled_set.Contains (Lanes [i].id)) {
						Lanes.RemoveAt (i);
					}
				}

				response.Lanes = Lanes;
				response.Hosts = Hosts;
				response.HostLanes = HostLanes;
			}

			return response;
		}
Beispiel #2
0
		private Dictionary<string, object> RevisionWorkToDict(FrontPageResponse data, DBLane l, DBRevisionWorkView2 w) {
			return new Dictionary<string, object> {
				{ "id", w.id },
				{ "author", w.author },
				{ "host_id", w.host_id },
				{ "host", data.Hosts.Find( h => h.id == w.host_id).host },
				{ "revision", w.revision },
				{ "revision_id", w.revision_id },
				{ "completed", w.completed },
				{ "status", w.state.ToString().ToLowerInvariant() },
				{ "endtime", w.endtime },
				{ "date", w.date },
				{ "lane", l.lane },
				{ "repository", l.repository },
				{ "branch", l.max_revision }
			};
		}
Beispiel #3
0
		List<DBRevisionWorkView2> FindRevisionWorkViews (FrontPageResponse data, int hostlane_id)
		{
			for (int k = 0; k < data.RevisionWorkHostLaneRelation.Count; k++) {
				if (data.RevisionWorkHostLaneRelation [k] == hostlane_id)
					return data.RevisionWorkViews [k];
			}

			return null;
		}
Beispiel #4
0
	public string GenerateOverview (FrontPageResponse data)
	{
		StringBuilder matrix = new StringBuilder ();
		LaneTreeNode tree = BuildTree (data);
		List<StringBuilder> header_rows = new List<StringBuilder> ();
		List<int> hostlane_order = new List<int> ();

		if (tree == null)
			return string.Empty;

		WriteLanes (header_rows, tree, 0, tree.Depth);

		matrix.AppendLine ("<table class='buildstatus'>");
		for (int i = 0; i < header_rows.Count; i++) {
			if (header_rows [i].Length == 0)
				continue;

			matrix.Append ("<tr>");
			matrix.Append (header_rows [i]);
			matrix.AppendLine ("</tr>");
		}

		matrix.AppendLine ("<tr>");
		WriteHostLanes (matrix, tree, data.Hosts, hostlane_order);
		matrix.AppendLine ("</tr>");

		int counter = 0;
		int added = 0;
		StringBuilder row = new StringBuilder ();
		do {
			added = 0;
			row.Length = 0;

			for (int i = 0; i < hostlane_order.Count; i++) {
				int hl_id = hostlane_order [i];

				List<DBRevisionWorkView2> rev = null;
				DBRevisionWorkView2 work = null;

				for (int k = 0; k < data.RevisionWorkHostLaneRelation.Count; k++) {
					if (data.RevisionWorkHostLaneRelation [k] == hl_id) {
						rev = data.RevisionWorkViews [k];
						break;
					}
				}

				if (rev != null && rev.Count > counter) {
					work = rev [counter];
					added++;
				}

				if (work != null) {
					string revision = work.revision;
					int lane_id = work.lane_id;
					int host_id = work.host_id;
					int revision_id = work.revision_id;
					DBState state = work.State;
					bool completed = work.completed;
					string state_str = state.ToString ().ToLowerInvariant ();
					bool is_working;
					string str_date = string.Empty;

					if (work.endtime.Year > 2000)
						str_date = "</br>" + TimeDiffToString (work.endtime, DateTime.UtcNow);
					
					switch (state) {
					case DBState.Executing:
						is_working = true;
						break;
					case DBState.NotDone:
					case DBState.Paused:
					case DBState.DependencyNotFulfilled:
					case DBState.Ignore:
						is_working = false;
						break;
					default:
						is_working = !completed;
						break;
					}

					long dummy;
					if (revision.Length > 16 && !long.TryParse (revision, out dummy))
						revision = revision.Substring (0, 8);

					if (is_working) {
						row.AppendFormat (
							@"<td class='{1}'>
								<center>
									<table class='executing'>
										<td>
											<a href='ViewLane.aspx?lane_id={2}&amp;host_id={3}&amp;revision_id={4}' title='{5}'>{0}{6}</a>
										</td>
									</table>
								<center>
							  </td>",
							revision, state_str, lane_id, host_id, revision_id, "", str_date);
					} else {
						row.AppendFormat ("<td class='{1}'><a href='ViewLane.aspx?lane_id={2}&amp;host_id={3}&amp;revision_id={4}' title='{5}'>{0}{6}</a></td>",
							revision, state_str, lane_id, host_id, revision_id, "", str_date);
					}
				} else {
					row.Append ("<td>-</td>");
				}
			}

			if (added > 0 && row.Length > 0) {
				matrix.Append ("<tr>");
				matrix.Append (row.ToString ());
				matrix.Append ("</tr>");
			}

			counter++;
		} while (counter <= limit && added > 0);

		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}
Beispiel #5
0
		private List<Dictionary<string, object>> RenderHostList(FrontPageResponse data, DBLane l, DBHostLane hl) {
			return FindRevisionWorkViews(data, hl.id).Select(w => RevisionWorkToDict(data, l, w)).ToList();
		}
Beispiel #6
0
	private LaneTreeNode BuildTree (FrontPageResponse data)
	{
		LaneTreeNode result = LaneTreeNode.BuildTree (data.Lanes, data.HostLanes);
		if (data.Lane != null) {
			result = result.Find (v => v.Lane != null && v.Lane.id == data.Lane.id);
		} else if (data.SelectedLanes.Count > 1) {
			for (int i = result.Children.Count - 1; i >= 0; i--) {
				LaneTreeNode ltn = result.Children [i];
				if (!data.SelectedLanes.Exists ((DBLane l) => l.id == ltn.Lane.id)) {
					result.Children.RemoveAt (i);
				}
			}
		}
		return result;
	}
Beispiel #7
0
	public string GenerateOverview (FrontPageResponse data)
	{
		StringBuilder matrix = new StringBuilder ();
		LaneTreeNode tree = BuildTree (data);
		List<StringBuilder> header_rows = new List<StringBuilder> ();
		List<int> hostlane_order = new List<int> ();

		if (tree == null)
			return string.Empty;
		
		// This renders all the host header lanes 
		WriteLanes (header_rows, tree, 0, tree.Depth); 

		matrix.AppendLine ("<table class='buildstatus'>");
		for (int i = 0; i < header_rows.Count; i++) {
			if (header_rows [i].Length == 0)
				continue;

			matrix.Append ("<tr>");
			matrix.Append (header_rows [i]);
			matrix.AppendLine ("</tr>");
		}

		// Renders all the hosts
		matrix.AppendLine ("<tr>");
		WriteHostLanes (matrix, tree, data.Hosts, hostlane_order); 
		matrix.AppendLine ("</tr>");

		// Renders all the builds
		int counter = 0;
		int added = 0;
		StringBuilder row = new StringBuilder ();
		do {
			added = 0;
			row.Length = 0;

			for (int i = 0; i < hostlane_order.Count; i++) {
				int hl_id = hostlane_order [i];

				var rev = FindRevisionWorkViews (data, hl_id);
				DBRevisionWorkView2 work = null;

				if (rev != null && rev.Count > counter) {
					work = rev [counter];
					added++;
				}

				WriteWorkCell (row, work);
			}

			if (added > 0 && row.Length > 0) {
				matrix.Append ("<tr>");
				matrix.Append (row.ToString ());
				matrix.Append ("</tr>");
			}

			counter++;
		} while (counter <= limit && added > 0);

		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}
Beispiel #8
0
	public string GenerateTaggedOverview (FrontPageResponse data)
	{
		StringBuilder matrix = new StringBuilder ();

		var lane_row = new StringBuilder ();
		var hl_row = new StringBuilder ();
		var rows = new List<StringBuilder> ();

		// First pass to get the total ammount of rows needed.
		for (int i = 0; i < data.SelectedLanes.Count; i++) {
			var lane = data.SelectedLanes [i];
			var hls  = data.HostLanes.FindAll ((hl) => hl.lane_id == lane.id);
			foreach (var hl in hls) {
				var work_views = FindRevisionWorkViews (data, hl.id);
				// Create more rows if needed.
				if (rows.Count < work_views.Count) {
					rows.Capacity = work_views.Count;
					for (int r = rows.Count; r < work_views.Count; r++)
						rows.Add (new StringBuilder ());
				}
			}
		}

		// Render pass
		matrix.AppendLine ("<table class='buildstatus'>");

		for (int i = 0; i < data.SelectedLanes.Count; i++) {
			var lane = data.SelectedLanes [i];
			var hls  = data.HostLanes.FindAll ((hl) => hl.lane_id == lane.id);
			lane_row.AppendFormat ("<td colspan='{1}'>{0}</td>", data.SelectedLanes [i].lane, hls.Count == 0 ? 1 : hls.Count).AppendLine ();
			foreach (var hl in hls) {
				// This renders all the host header lanes 
				WriteHostLane (hl_row, data.Hosts, hl);

				// Find all the builds
				var work_views = FindRevisionWorkViews (data, hl.id);

				// Create more rows if needed.
				if (rows.Count < work_views.Count) {
					rows.Capacity = work_views.Count;
					for (int r = rows.Count; r < work_views.Count; r++)
						rows.Add (new StringBuilder ());
				}

				for (int r = 0; r < work_views.Count; r++) {
					WriteWorkCell (rows [r], work_views [r]);
				}

				// Fix the staggering, add the empty cells.
				if (work_views.Count < rows.Count)
					for (int r = work_views.Count; r < rows.Count; r++) {
						WriteWorkCell (rows [r], null);
					}

			}
		}

		matrix.Append ("<tr>").Append (lane_row).AppendLine ("</tr>");
		matrix.Append ("<tr>").Append (hl_row).AppendLine ("</tr>");
		for (int r = 0; r < rows.Count; r++) {
			matrix.Append ("<tr>").Append (rows [r]).AppendLine ("</tr>");
		}
		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}
		public FrontPageResponse GetFrontPageData2 (WebServiceLogin login, int limit, string [] lanes, int [] lane_ids)
		{
			FrontPageResponse response = new FrontPageResponse ();
			List<DBLane> Lanes = new List<DBLane> ();
			List<DBHost> Hosts = new List<DBHost> ();
			List<DBHostLane> HostLanes = new List<DBHostLane> ();
			List<DBRevisionWorkView2> RevisionWork;

			limit = Math.Min (limit, 500);

			using (DB db = new DB ()) {
				Authenticate (db, login, response, true);

				using (IDbCommand cmd = db.CreateCommand ()) {
					cmd.CommandText = DBLane.TableName;
					cmd.CommandType = CommandType.TableDirect;
					using (IDataReader reader = cmd.ExecuteReader ()) {
						while (reader.Read ())
							Lanes.Add (new DBLane (reader));
					}
				}

				using (IDbCommand cmd = db.CreateCommand ()) {
					cmd.CommandText = DBHost.TableName;
					cmd.CommandType = CommandType.TableDirect;
					using (IDataReader reader = cmd.ExecuteReader ()) {
						while (reader.Read ())
							Hosts.Add (new DBHost (reader));
					}
				}

				// get a list of the lanes to show
				// note that the logic here is slightly different from the usual "string lane, int? lane_id" logic in other methods,
				// where we only use the string parameter if the id parameter isn't provided, here we add everything we can to the 
				// list of selected lanes, so if you provide both a string and an id parameter both are used (assuming they correspond
				// with different lanes of course).
				response.SelectedLanes = Lanes.FindAll (delegate (DBLane l)
				{
					if (lane_ids != null) {
						for (int i = 0; i < lane_ids.Length; i++) {
							if (lane_ids [i] == l.id)
								return true;
						}
					}
					if (lanes != null) {
						for (int i = 0; i < lanes.Length; i++) {
							if (!string.IsNullOrEmpty (lanes [i]) && lanes [i] == l.lane)
								return true;
						}
					}
					return false;
				});

				// backwards compat
				if (response.SelectedLanes.Count == 1)
					response.Lane = response.SelectedLanes [0];

				using (IDbCommand cmd = db.CreateCommand ()) {
					cmd.CommandText = @"
SELECT HostLane.*
FROM HostLane";

					using (IDataReader reader = cmd.ExecuteReader ()) {
						while (reader.Read ())
							HostLanes.Add (new DBHostLane (reader));
					}
				}

				response.RevisionWorkViews = new List<List<DBRevisionWorkView2>> ();
				response.RevisionWorkHostLaneRelation = new List<int> ();

				foreach (DBHostLane hl in HostLanes) {
					RevisionWork = new List<DBRevisionWorkView2> ();
					using (IDbCommand cmd = db.CreateCommand ()) {
						cmd.CommandText = @"SELECT R.* FROM (" + DBRevisionWorkView2.SQL.Replace (';', ' ') + ") AS R WHERE R.host_id = @host_id AND R.lane_id = @lane_id LIMIT @limit";
						DB.CreateParameter (cmd, "host_id", hl.host_id);
						DB.CreateParameter (cmd, "lane_id", hl.lane_id);
						DB.CreateParameter (cmd, "limit", limit);

						using (IDataReader reader = cmd.ExecuteReader ()) {
							while (reader.Read ())
								RevisionWork.Add (new DBRevisionWorkView2 (reader));
						}
					}

					response.RevisionWorkHostLaneRelation.Add (hl.id);
					response.RevisionWorkViews.Add (RevisionWork);
				}

				response.Lanes = Lanes;
				response.Hosts = Hosts;
				response.HostLanes = HostLanes;

				return response;
			}
		}
		public FrontPageResponse GetFrontPageData3 (WebServiceLogin login, int page_size, int page, string [] lanes, int [] lane_ids)
		{
			FrontPageResponse response = new FrontPageResponse ();
			List<DBLane> Lanes = new List<DBLane> ();
			List<DBHost> Hosts = new List<DBHost> ();
			List<DBHostLane> HostLanes = new List<DBHostLane> ();
			List<DBRevisionWorkView2> RevisionWork;

			page_size = Math.Min (page_size, 500);

			try {
				using (DB db = new DB ()) {
					Authenticate (db, login, response, true);

					using (IDbCommand cmd = db.CreateCommand ()) {
						cmd.CommandText = DBLane.TableName;
						cmd.CommandType = CommandType.TableDirect;
						using (IDataReader reader = cmd.ExecuteReader ()) {
							while (reader.Read ())
								Lanes.Add (new DBLane (reader));
						}
					}

					using (IDbCommand cmd = db.CreateCommand ()) {
						cmd.CommandText = DBHost.TableName;
						cmd.CommandType = CommandType.TableDirect;
						using (IDataReader reader = cmd.ExecuteReader ()) {
							while (reader.Read ())
								Hosts.Add (new DBHost (reader));
						}
					}

					// get a list of the lanes to show
					// note that the logic here is slightly different from the usual "string lane, int? lane_id" logic in other methods,
					// where we only use the string parameter if the id parameter isn't provided, here we add everything we can to the 
					// list of selected lanes, so if you provide both a string and an id parameter both are used (assuming they correspond
					// with different lanes of course).
					response.SelectedLanes = Lanes.FindAll (delegate (DBLane l) {
						if (lane_ids != null) {
							for (int i = 0; i < lane_ids.Length; i++) {
								if (lane_ids [i] == l.id)
									return true;
							}
						}
						if (lanes != null) {
							for (int i = 0; i < lanes.Length; i++) {
								if (!string.IsNullOrEmpty (lanes [i]) && lanes [i] == l.lane)
									return true;
							}
						}
						return false;
					});

					// backwards compat
					if (response.SelectedLanes.Count == 1)
						response.Lane = response.SelectedLanes [0];

					using (IDbCommand cmd = db.CreateCommand ()) {
						cmd.CommandText = @"
SELECT HostLane.*
FROM HostLane
WHERE hidden = false";

						using (IDataReader reader = cmd.ExecuteReader ()) {
							while (reader.Read ())
								HostLanes.Add (new DBHostLane (reader));
						}
					}

					response.RevisionWorkViews = new List<List<DBRevisionWorkView2>> ();
					response.RevisionWorkHostLaneRelation = new List<int> ();

					foreach (DBHostLane hl in HostLanes) {
						RevisionWork = new List<DBRevisionWorkView2> ();
						using (IDbCommand cmd = db.CreateCommand ()) {
							cmd.CommandText = @"SELECT R.* FROM (" + DBRevisionWorkView2.SQL.Replace (';', ' ') + ") AS R WHERE R.host_id = @host_id AND R.lane_id = @lane_id LIMIT @limit OFFSET @offset";
							DB.CreateParameter (cmd, "host_id", hl.host_id);
							DB.CreateParameter (cmd, "lane_id", hl.lane_id);
							DB.CreateParameter (cmd, "limit", page_size);
							DB.CreateParameter (cmd, "offset", page * page_size);

							using (IDataReader reader = cmd.ExecuteReader ()) {
								while (reader.Read ())
									RevisionWork.Add (new DBRevisionWorkView2 (reader));
							}
						}

						response.RevisionWorkHostLaneRelation.Add (hl.id);
						response.RevisionWorkViews.Add (RevisionWork);
					}

					// Create a list of all the lanes which have hostlanes
					var enabled_set = new HashSet<int> ();
					foreach (DBHostLane hl in HostLanes) {
						if (enabled_set.Contains (hl.lane_id))
							continue;
						enabled_set.Add (hl.lane_id);

						// Walk up the tree of parent lanes, marking all the parents too
						var l = Lanes.First ((v) => v.id == hl.lane_id);
						while (true) {
							if (!l.parent_lane_id.HasValue)
								break;

							if (enabled_set.Contains (l.parent_lane_id.Value))
								break;

							enabled_set.Add (l.parent_lane_id.Value);

							l = Lanes.First ((v) => v.id == l.parent_lane_id.Value);
						}
					}

					// Remove the lanes which aren't marked
					for (int i = Lanes.Count - 1; i >= 0; i--) {
						if (!enabled_set.Contains (Lanes [i].id)) {
							Lanes.RemoveAt (i);
						}
					}

					response.Lanes = Lanes;
					response.Hosts = Hosts;
					response.HostLanes = HostLanes;
				}
			} catch (Exception ex) {
				response.Exception = new WebServiceException (ex);
			}

			return response;
		}