123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using Microsoft.TeamFoundation.VersionControl.Client;
- using Microsoft.TeamFoundation.WorkItemTracking.Client;
- namespace pulse
- {
-
-
-
- public class Dashboard
- {
-
-
-
-
-
-
- public Uri Server { get; set; }
-
-
-
-
-
-
- public int SelectedItem { get; set; }
-
-
-
-
-
-
- public DateTime? DateRefresh { get; set; }
-
-
-
-
-
-
- private IDictionary<string, double> Scores { get; set; }
-
-
-
-
-
-
- private IList<string> HighlightedScores { get; set; }
-
-
-
-
-
-
- private string CacheFile
- {
- get
- {
- if (this.Server == null)
- {
- return "$cache";
- }
- return string.Format(
- "{0}_{1}_{2}.cache",
- this.Server.Scheme,
- this.Server.Host,
- this.Server.Port);
- }
- }
-
-
-
-
-
-
- private int Commits { get; set; }
-
-
-
-
-
-
- private int Projects { get; set; }
-
-
-
-
-
-
- private int WorkItems { get; set; }
-
-
-
-
-
-
- private int Height
- {
- get
- {
- return Console.WindowHeight - 2;
- }
- }
-
-
-
-
-
-
- private int Width
- {
- get
- {
- return Console.WindowWidth;
- }
- }
-
-
-
-
- public Dashboard(Uri server)
- {
- this.Commits = 0;
- this.Projects = 0;
- this.WorkItems = 0;
- this.DateRefresh = null;
- this.Server = server;
- this.SelectedItem = 1;
- this.Scores = new Dictionary<string, double>();
- this.HighlightedScores = new List<string>();
- var cacheFile = new FileInfo(this.CacheFile);
- if (cacheFile.Exists)
- {
- using (var reader = new StreamReader(cacheFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read)))
- {
- while (!reader.EndOfStream)
- {
- var line = reader.ReadLine();
- var fields = line.Split(new char[] { ';' });
- switch (fields[0].ToLower())
- {
- case "refreshed":
- if (fields.Count() > 1 && !string.IsNullOrWhiteSpace(fields[1]))
- {
- this.DateRefresh = Convert.ToDateTime(fields[1]);
- }
- break;
- case "projects":
- this.Projects = Convert.ToInt32(fields[1]);
- break;
- case "work":
- this.WorkItems = Convert.ToInt32(fields[1]);
- break;
- case "commits":
- this.Commits = Convert.ToInt32(fields[1]);
- break;
- case "score":
- var points = Convert.ToDouble(fields[2]);
- if (!this.Scores.ContainsKey(fields[1]))
- {
- this.Scores.Add(fields[1], points);
- }
- else
- {
- this.Scores[fields[1]] = points;
- }
- break;
- }
- }
- }
- }
- }
-
-
-
-
- public void AddCommits(IEnumerable<Changeset> commits)
- {
- foreach (var commit in commits)
- {
-
- this.Commits++;
- var personOfInterest = commit.CommitterDisplayName;
- this.AddHighlightedScore(personOfInterest);
- if (!this.Scores.Any(x => x.Key.Equals(personOfInterest, StringComparison.InvariantCultureIgnoreCase)))
- {
- this.Scores.Add(personOfInterest, 0);
- }
-
- this.Scores[personOfInterest] += Rewards.Participation;
-
- if (string.IsNullOrWhiteSpace(commit.Comment))
- {
- this.Scores[personOfInterest] += Penalties.LackingVerbosity;
- }
- else
- {
- this.Scores[personOfInterest] += Rewards.Verbosity;
- }
-
- if (commit.AssociatedWorkItems.Count() == 0)
- {
- this.Scores[personOfInterest] += Penalties.LackOfPurpose;
- }
- else
- {
- this.Scores[personOfInterest] += Rewards.Collaboration;
- }
- }
- }
-
-
-
-
- public void AddProjects(IEnumerable<object> projects)
- {
- this.Projects = projects.Count();
- }
-
-
-
-
- public void AddWorkItems(IEnumerable<WorkItem> workItems)
- {
- foreach (var item in workItems)
- {
-
- this.WorkItems++;
- if (!string.IsNullOrWhiteSpace(item.CreatedBy))
- {
- if (!this.Scores.Any(x => x.Key.Equals(item.CreatedBy, StringComparison.InvariantCultureIgnoreCase)))
- {
- this.Scores.Add(item.CreatedBy, 0);
- }
- }
- if (item.CreatedBy != item.ChangedBy)
- {
- if (!string.IsNullOrWhiteSpace(item.ChangedBy))
- {
- if (!this.Scores.Any(x => x.Key.Equals(item.ChangedBy, StringComparison.InvariantCultureIgnoreCase)))
- {
- this.Scores.Add(item.ChangedBy, 0);
- }
-
- this.Scores[item.ChangedBy] += Rewards.Collaboration;
- }
- }
- else
- {
- if (!string.IsNullOrWhiteSpace(item.CreatedBy))
- {
-
- this.Scores[item.CreatedBy] += Rewards.Participation;
- }
- }
- var personOfInterest = item.CreatedDate < item.ChangedDate
- ? item.ChangedBy
- : item.CreatedBy;
- this.AddHighlightedScore(personOfInterest);
- if (!string.IsNullOrWhiteSpace(personOfInterest))
- {
- switch (item.Type.Name.ToLower())
- {
- case "product backlog item":
- this.Scores[personOfInterest] += Rewards.Participation;
- break;
- case "user story":
- this.Scores[personOfInterest] += Rewards.Participation;
- break;
- case "feature":
- this.Scores[personOfInterest] += Rewards.Participation;
- break;
- case "task":
- this.Scores[personOfInterest] += Rewards.CreatedWork;
- break;
- case "impediment":
- case "issue":
- case "bug":
- this.Scores[personOfInterest] += Rewards.BugReport;
- break;
- case "test case":
- case "shared steps":
- this.Scores[personOfInterest] += Rewards.CreatedWork;
- break;
- default:
- Debug.WriteLine("Did not handle work item type [" + item.Type.Name + "]");
- break;
- }
- switch (item.State.ToLower())
- {
- case "new":
- case "design":
- case "to do":
- case "open":
- case "approved":
- case "active":
- case "in progress":
- this.Scores[personOfInterest] += Rewards.TakingOwnership;
- break;
- case "done":
- case "committed":
- case "resolved":
- case "closed":
- this.Scores[personOfInterest] += Rewards.FinishWork;
- break;
- case "removed":
- this.Scores[personOfInterest] += Rewards.HouseCleaning;
- break;
- default:
- Debug.WriteLine("Did not handle work item state [" + item.State + "]");
- break;
- }
- }
- }
- }
-
-
-
- public void Cache()
- {
- var cacheFile = new FileInfo(this.CacheFile);
- using (var writer = new StreamWriter(cacheFile.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)))
- {
- writer.WriteLine(string.Format("{0};{1}", "refreshed", this.DateRefresh.HasValue ? this.DateRefresh.Value.ToString() : string.Empty));
- writer.WriteLine(string.Format("{0};{1}", "projects", this.Projects));
- writer.WriteLine(string.Format("{0};{1}", "work", this.WorkItems));
- writer.WriteLine(string.Format("{0};{1}", "commits", this.Commits));
- foreach (var score in this.Scores)
- {
- writer.WriteLine(string.Format("{0};{1};{2}", "score", score.Key, score.Value));
- }
- }
- }
-
-
-
- public void ClearHighlights()
- {
- this.HighlightedScores.Clear();
- }
-
-
-
- public void Update()
- {
-
- var buffer = new List<string>();
- buffer.AddRange(this.Statistics());
-
- buffer.Add(Padding(this.Width, "-"));
-
- var leaderBoard = this.Leadboard();
- var pageSize = this.Height - buffer.Count();
-
- if (this.SelectedItem > leaderBoard.Count())
- {
- this.SelectedItem = leaderBoard.Count();
- }
-
- if (this.SelectedItem > pageSize)
- {
- buffer.AddRange(leaderBoard.Skip(this.SelectedItem % pageSize).Take(pageSize));
- }
- else
- {
- buffer.AddRange(leaderBoard.Take(pageSize));
- }
-
- var heightRemaining = this.Height - buffer.Count();
- for (var i = 0; i < heightRemaining; i++)
- {
- buffer.Add(string.Empty);
- }
- Console.CursorVisible = false;
- Console.SetCursorPosition(0, 0);
- foreach (var line in buffer)
- {
- foreach (var highlight in this.HighlightedScores)
- {
- if (line.Contains(highlight))
- {
- Console.ForegroundColor = ConsoleColor.Green;
- break;
- }
- }
- if (line.StartsWith(string.Format(" {0,3:###}.", this.SelectedItem)))
- {
- Console.BackgroundColor = ConsoleColor.White;
- if (Console.ForegroundColor != ConsoleColor.Green)
- {
- Console.ForegroundColor = ConsoleColor.Black;
- }
- }
- if (line.Length >= this.Width)
- {
- Console.Write(line);
- }
- else
- {
- Console.WriteLine(line);
- }
- Console.ResetColor();
- }
-
- if (this.HighlightedScores.Count() > 0)
- {
- Sound.Queue.Enqueue(Sound.Notify);
- }
- }
-
-
-
-
- private void AddHighlightedScore(string person)
- {
- if (this.Scores.ContainsKey(person) && !this.HighlightedScores.Contains(person))
- {
- this.HighlightedScores.Add(person);
- }
- }
-
-
-
-
- private IEnumerable<string> Statistics()
- {
- var lines = new List<string>();
- var title = "pulse";
- var titlePadding = Padding((this.Width - title.Length) / 2);
- lines.Add(string.Format("{0}{1}{0}", titlePadding, title));
- var stats = string.Format(
- "p: {0} | w: {1} | c: {2} | r: {3:MM/dd/yyyy HH:mm}",
- this.Projects,
- this.WorkItems,
- this.Commits,
- this.DateRefresh.HasValue ? this.DateRefresh.Value : DateTime.Now);
- var statsPadding = Padding((this.Width - stats.Length) / 2);
- lines.Add(string.Format("{0}{1}{0}", statsPadding, stats));
- return lines;
- }
-
-
-
-
- private IEnumerable<string> Leadboard()
- {
- if (this.Scores == null || this.Scores.Count() == 0)
- {
- return new List<string>();
- }
- var rank = 0;
- return this.Scores
- .OrderByDescending(x => x.Value)
- .Select(x =>
- {
- var player = x.Key;
- var points = (int)x.Value;
- if (points < 0)
- {
- points = 0;
- }
- var basic = string.Format(" {0,3:###}. {1}{{0}}{2}", ++rank, player, points);
- return basic.Replace("{0}", Padding(this.Width - basic.Length + 2, " "));
- })
- .ToList();
- }
-
-
-
-
-
-
- private static string Padding(int length, string text = " ")
- {
- var padding = string.Empty;
- for (var i = 0; i < length; i++)
- {
- padding += text;
- }
- return padding;
- }
- }
- }
|