internal List <TimelineSegment> Analyze(DateTime startTime, DateTime endTime) { Func <Event, DynamicEvent> convertEvent = e => new DynamicEvent(e.StartTime, e.EndTime, e.Data, e.Type); var events = (from e in this.db.Events where e.EndTime >= startTime where e.StartTime <= endTime select e) .AsEnumerable() .Select(convertEvent) .ToList(); var times = new SortedSet <DateTime>(new[] { startTime, endTime }); times.UnionWith(events.Select(e => e.StartTime)); times.UnionWith(events.Select(e => e.EndTime)); times = times.GetViewBetween(startTime, endTime); var spans = times.Zip(times.Skip(1), (start, end) => new { startTime = start, endTime = end }).ToList(); var segments = new List <TimelineSegment>(); var rules = this.db.Rules.ToList(); for (int i = 0; i < spans.Count; i++) { var span = spans[i]; TimelineSegment segment = null; Func <Predicate <DynamicEvent>, DynamicEvent> mostRecent = predicate => { return((from e in events where e.StartTime <= span.startTime orderby e.StartTime descending where predicate(e) select e).FirstOrDefault() ?? (from e in this.db.Events where e.StartTime <= span.startTime orderby e.StartTime descending select e) .AsEnumerable() .Select(convertEvent) .Where(e => predicate(e)) .FirstOrDefault()); }; Func <Predicate <DynamicEvent>, DynamicEvent> current = predicate => { return((from e in events where e.StartTime <= span.startTime where e.EndTime >= span.endTime where predicate(e) orderby e.StartTime descending, e.EndTime ascending select e).FirstOrDefault()); }; foreach (var rule in rules) { var result = RunRule(rule, span.startTime, span.endTime, events, mostRecent, current); if (result != null) { if (result.StartTime > span.startTime) { spans.Add(new { startTime = span.startTime, endTime = result.StartTime }); } if (result.EndTime < span.endTime) { spans.Add(new { startTime = result.EndTime, endTime = span.endTime }); } segment = result; break; } } if (segment == null) { segment = new TimelineSegment { StartTime = span.startTime, EndTime = span.endTime, Description = "Unclassified", Productivity = 0, IsUnclassified = true, }; } segments.Add(segment); } Simplify(segments); return(segments); }
private TimelineSegment RunRule(Rule rule, DateTime startTime, DateTime endTime, IList <DynamicEvent> events, EventFilter mostRecent, EventFilter current) { var ruleFunc = ScriptManager.GetScriptFunc(rule.Expression); dynamic result; try { result = ruleFunc(startTime, endTime, events, mostRecent, current); } catch (TargetInvocationException) { // TODO: This should be bubbled up, so that the user can be notified of which rule cause the error. return(null); } if (result == null) { return(null); } else if (result is bool) { return(!result ? null : new TimelineSegment { StartTime = startTime, EndTime = endTime, Description = rule.Description, Productivity = rule.Productivity, }); } else if (result is string) { return(new TimelineSegment { StartTime = startTime, EndTime = endTime, Description = result as string, Productivity = rule.Productivity, }); } else { var ruleResult = new TimelineSegment(); ruleResult.Description = RetrieveField(result, "Description", rule.Description); ruleResult.Productivity = RetrieveField(result, "Productivity", rule.Productivity); ruleResult.StartTime = RetrieveField(result, "StartTime", startTime); ruleResult.EndTime = RetrieveField(result, "EndTime", endTime); ruleResult.StartTime = ruleResult.StartTime.Clamp(startTime, endTime); ruleResult.EndTime = ruleResult.EndTime.Clamp(startTime, endTime); ruleResult.Description = ruleResult.Description ?? ""; if (ruleResult.Productivity.HasValue) { ruleResult.Productivity = ruleResult.Productivity.Value.Clamp(0, 100); } if (ruleResult.StartTime >= ruleResult.EndTime) { return(null); } return(ruleResult); } }