Esempio n. 1
0
		/// <summary>
		/// Displays statistics
		/// </summary>
		/// <param name="sourceUrl"> URL to use for the back link. Can be null. </param>
		/// <param name="history"> What type of history to display. Can be null. </param>
		private void DisplayStats(string sourceUrl, string history)
		{
			DisplayPageHeader("Code review statistics");

			if (sourceUrl != null)
			{
				ActivePage.Controls.Add(
					CreateLinkButton("Back...", Server.UrlDecode(sourceUrl)));

				AddLabel("<br><br>");
			}

			int activeReviews;
			int totalReviews;
			int totalFiles;
			int comments;

			int sourceControlId = GetSourceControlId();
			if (sourceControlId == -1)
			{
				activeReviews = (from cl in DataContext.ChangeLists where cl.Stage == 0 select cl).Count();
				totalReviews = (from cl in DataContext.ChangeLists select cl).Count();
				totalFiles = (from fl in DataContext.ChangeFiles select fl).Count();
				comments = (from cm in DataContext.Comments select cm).Count();
			}
			else
			{
				activeReviews = (from cl in DataContext.ChangeLists
								 where cl.SourceControlId == sourceControlId && cl.Stage == 0
								 select cl).Count();
				totalReviews = (from cl in DataContext.ChangeLists
								where cl.SourceControlId == sourceControlId
								select cl).Count();
				totalFiles = (from fl in DataContext.ChangeFiles
							  join cl in DataContext.ChangeLists on fl.ChangeListId equals cl.Id
							  where cl.SourceControlId == sourceControlId
							  select fl).Count();
				comments = (from cm in DataContext.Comments
							join rv in DataContext.Reviews on cm.ReviewId equals rv.Id
							join cl in DataContext.ChangeLists on rv.ChangeListId equals cl.Id
							where cl.SourceControlId == sourceControlId
							select cm).Count();
			}

			Table header = new Table();
			ActivePage.Controls.Add(header);

			TableRow row = new TableRow();
			header.Rows.Add(row);

			TableCell cell = new TableCell();
			cell.AppendCSSClass("CssStatsHeaderCell");
			row.Cells.Add(cell);
			cell.Text =
				"<b>Database stats:</b><br><br>" +
				"Active Code reviews: " + activeReviews + "<br>" +
				"Total Code reviews: " + totalReviews + "<br>" +
				"Files reviewed: " + totalFiles + "<br>" +
				"Comments submitted: " + comments;

			cell = new TableCell();
			cell.AppendCSSClass("CssStatsHeaderCell");
			row.Cells.Add(cell);

			var topSubmitters = sourceControlId == -1 ?
				DataContext.ExecuteQuery<StatQueryData>(
					"SELECT TOP(5) * FROM (SELECT UserName, COUNT(*) AS Freq FROM ChangeList GROUP BY UserName) AS t " +
					"ORDER BY Freq DESC") :
				DataContext.ExecuteQuery<StatQueryData>(
					"SELECT TOP(5) * FROM (SELECT UserName, COUNT(*) AS Freq FROM ChangeList " +
					"WHERE SourceControlId = {0} GROUP BY UserName) AS t ORDER BY Freq DESC", sourceControlId);

			StringBuilder sb = new StringBuilder("<b>Most CLs:</b><br><br>");
			foreach (StatQueryData stat in topSubmitters)
				sb.Append(stat.UserName + ": " + stat.Freq + "<br>");

			cell.Text = sb.ToString();

			cell = new TableCell();
			cell.AppendCSSClass("CssStatsHeaderCell");
			row.Cells.Add(cell);

			var topReviewers = sourceControlId == -1 ?
				DataContext.ExecuteQuery<StatQueryData>(
					"SELECT TOP(5) * FROM (SELECT Review.UserName, COUNT(*) AS Freq FROM Review " +
					"INNER JOIN ChangeList ON Review.ChangeListId = ChangeList.Id " +
					"WHERE ChangeList.UserName <> Review.UserName " +
					"GROUP BY Review.UserName) AS t " +
					"ORDER BY Freq DESC") :
				DataContext.ExecuteQuery<StatQueryData>(
					"SELECT TOP(5) * FROM (SELECT Review.UserName, COUNT(*) AS Freq FROM Review " +
					"INNER JOIN ChangeList ON Review.ChangeListId = ChangeList.Id " +
					"WHERE ChangeList.SourceControlId = {0} AND ChangeList.UserName <> Review.UserName " +
					"GROUP BY Review.UserName) AS t " +
					"ORDER BY Freq DESC", sourceControlId);

			sb = new StringBuilder("<b>Most reviews:</b><br><br>");
			foreach (StatQueryData stat in topReviewers)
				sb.Append(stat.UserName + ": " + stat.Freq + "<br>");

			cell.Text = sb.ToString();

			Table reviewsHeader = new Table();
			ActivePage.Controls.Add(reviewsHeader);
			TableRow reviewsHeaderRow = new TableRow();
			reviewsHeader.Rows.Add(reviewsHeaderRow);

			TableCell firstCell = new TableCell();
			reviewsHeaderRow.Cells.Add(firstCell);
			firstCell.AppendCSSClass("CssStatsHistoryHeader");
			TableCell secondCell = new TableCell();
			reviewsHeaderRow.Cells.Add(secondCell);
			secondCell.AppendCSSClass("CssStatsHistoryHeader");
			TableCell thirdCell = new TableCell();
			reviewsHeaderRow.Cells.Add(thirdCell);
			thirdCell.AppendCSSClass("CssStatsHistoryHeader");
			HyperLink second = new HyperLink();
			secondCell.Controls.Add(second);
			HyperLink third = new HyperLink();
			thirdCell.Controls.Add(third);

			DateTime? date = null;
			if ("lastweek".Equals(history))
			{
				date = DateTime.Now.AddDays(-7);

				firstCell.Text = "Reviews last week";

				second.Text = "Last month...";
				second.NavigateUrl = Request.FilePath + "?action=stats&history=lastmonth" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");

				third.Text = "Active...";
				third.NavigateUrl = Request.FilePath + "?action=stats" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");
			}
			else if ("lastmonth".Equals(history))
			{
				date = DateTime.Now.AddDays(-30);

				firstCell.Text = "Reviews last month";

				second.Text = "Last week...";
				second.NavigateUrl = Request.FilePath + "?action=stats&history=lastweek" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");

				third.Text = "Active...";
				third.NavigateUrl = Request.FilePath + "?action=stats" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");
			}
			else
			{
				firstCell.Text = "Active reviews";

				second.Text = "Last week...";
				second.NavigateUrl = Request.FilePath + "?action=stats&history=lastweek" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");

				third.Text = "Last month...";
				third.NavigateUrl = Request.FilePath + "?action=stats&history=lastmonth" +
					(sourceUrl != null ? "&sourceUrl=" + Server.UrlEncode(sourceUrl) : "");
			}

			Table page = new Table();
			ActivePage.Controls.Add(page);
			page.EnableViewState = false;

			var allChangesQuery = date == null ?
				(sourceControlId != -1 ?
				from mc in DataContext.ChangeLists
				where mc.Stage == 0 && mc.SourceControlId == sourceControlId
				select mc :
				from mc in DataContext.ChangeLists
				where mc.Stage == 0
				orderby mc.TimeStamp descending
				select mc) :
				(sourceControlId != -1 ?
				from mc in DataContext.ChangeLists
				where mc.SourceControlId == sourceControlId && mc.TimeStamp > date
				select mc :
				from mc in DataContext.ChangeLists
				where mc.TimeStamp > date
				orderby mc.TimeStamp descending
				select mc);

			ActivePage.Controls.Add(CreateReviewsSectionCommon(null, allChangesQuery, true));

			ApplyRowBackgroundCssStyle(page);

			if (sourceUrl != null)
			{
				AddLabel("<br>");
				ActivePage.Controls.Add(
					CreateLinkButton("Back...", Server.UrlDecode(sourceUrl)));
			}
		}
Esempio n. 2
0
		/// <summary>
		/// Displays the change list composition. This is called when the main table shows the details of one
		/// change list.
		/// </summary>
		/// <param name="cid"> Change Id. This is relative to the database, not source control. </param>
		/// <param name="userName"> User name. </param>
		private void DisplayChange(int cid, string userName)
		{
			var changeQuery = from ch in DataContext.ChangeLists where ch.Id == cid select ch;
			if (changeQuery.Count() != 1)
			{
				ErrorOut("Could not find this change in the system!");
				return;
			}

			HintsData.InChangeView = true;

			ChangeList changeList = changeQuery.Single();

			DisplayPageHeader("Change list " + changeList.CL);

			Table table = new Table();
			table.AppendCSSClass("CssChangeListDetail");

			ActivePage.Controls.Add(table);

			table.Rows.Add(GetChangeDescriptionRow("Date:", WrapTimeStamp(changeList.TimeStamp)));
			if (changeList.UserClient != null && changeList.UserClient != String.Empty)
				table.Rows.Add(GetChangeDescriptionRow("Client:", changeList.UserClient));
			var userRow = GetChangeDescriptionRow("User:"******"Status:", changeList.Stage == 0 ? "Pending" : "Submitted"));
			if (changeList.Description != null && changeList.Description != String.Empty)
				table.Rows.Add(GetChangeDescriptionRow("Description:", Server.HtmlEncode(changeList.Description)));
			table.Rows.Add(GetChangeDescriptionRow("Files:", ""));

			var latestReview = GetLatestUserReviewForChangeList(userName, cid);
			foreach (ChangeFile file in
				(from fl in DataContext.ChangeFiles where fl.ChangeListId == cid select fl))
			{
				var versions = GetVersionsAbstract(file.Id);
				bool hasTextBody = (from ver in versions where ver.HasTextBody select ver).Count() != 0;
				table.Rows.Add(GetChangeFileRow(file, versions.LastOrDefault(), hasTextBody, latestReview));
			}

			var attachments = (from ll in DataContext.Attachments 
							   where ll.ChangeListId == cid
							   orderby ll.TimeStamp
							   select ll);

			if (attachments.Count() > 0)
			{
				table.Rows.Add(GetChangeDescriptionRow("Links:", ""));

				foreach (Attachment a in attachments)
					AddAttachmentRow(table, a);
			}

			AddLabel("<h3>Review history</h3>");
			Table reviewResults = new Table();

			ActivePage.Controls.Add(reviewResults);

			reviewResults.AppendCSSClass("CssChangeListReviewHistory");

			var allReviewsQuery = from rr in DataContext.Reviews
								  where rr.ChangeListId == cid && rr.IsSubmitted
								  orderby rr.TimeStamp
								  select rr;

			foreach (Review review in allReviewsQuery)
			{
				TableRow row = new TableRow();
				reviewResults.Rows.Add(row);

				row.AppendCSSClass("CssTopAligned");

				TableCell dateCell = new TableCell();
				row.Cells.Add(dateCell);

				dateCell.AppendCSSClass("CssDate");
				dateCell.Text = WrapTimeStamp(review.TimeStamp);

				TableCell nameCell = new TableCell();
				row.Cells.Add(nameCell);

				nameCell.AppendCSSClass("CssName");
				nameCell.Text = review.UserName;

				TableCell verdictCell = new TableCell();
				row.Cells.Add(verdictCell);

				verdictCell.AppendCSSClass("CssScore");

				HyperLink reviewTarget = new HyperLink();
				verdictCell.Controls.Add(reviewTarget);

				if (review.OverallStatus == 0)
					HintsData.HaveNeedsWorkVotes = true;

				reviewTarget.Text = Malevich.Util.CommonUtils.ReviewStatusToString(review.OverallStatus);
				reviewTarget.NavigateUrl = Request.FilePath + "?rid=" + review.Id;

				if (review.CommentText != null)
				{
					TableCell abbreviatedCommentCell = new TableCell();
					row.Cells.Add(abbreviatedCommentCell);
					abbreviatedCommentCell.AppendCSSClass("CssComment");
					abbreviatedCommentCell.Text = AbbreviateToOneLine(review.CommentText, MaxReviewCommentLength);
				}
			}

			//Todo: modify stage logic, stage 1 is pending...
			if (changeList.Stage != 0)
			{
				HintsData.IsChangeInactive = true;

				AddLabel(String.Format("<br>This review has been {0}.", (changeList.Stage == 2 ? "closed" : "deleted")));
				return;
			}

			AddLabel("<h3>Vote so far</h3>");

			TableGen.Table reviewerVote = new TableGen.Table(2) { CssClass = "CssChangeListVoteHistory" };
			ActivePage.Controls.Add(reviewerVote);

			var reviewerQuery = from rv in DataContext.Reviewers where rv.ChangeListId == cid select rv;
			Reviewer[] reviewers = reviewerQuery.ToArray();
			bool iAmAReviewer = false;
			foreach (Reviewer reviewer in reviewers)
			{
				var row = reviewerVote.CreateRow();
				reviewerVote.AddItem(row);

				row[0].Text = reviewer.ReviewerAlias;

				if (userName.EqualsIgnoreCase(reviewer.ReviewerAlias))
				{
					DropDownList list = new DropDownList() { ID = "verdictlist" };
					row[1].Add(list);

					list.Items.Add(new ListItem() { Text = "Needs work" });				
					list.Items.Add(new ListItem() { Text = "LGTM with minor tweaks" });
					list.Items.Add(new ListItem() { Text = "LGTM" });
					list.Items.Add(new ListItem() { Text = "Non-scoring comment" });

					iAmAReviewer = true;
				}
				else if (!Page.IsPostBack)
				{
					Review review = GetLastReview(cid, reviewer.ReviewerAlias);
					if (review == null)
					{
						row[1].Text = "Have not looked";
					}
					else
					{
						row[1].Add(new HyperLink()
						{
							Text = Malevich.Util.CommonUtils.ReviewStatusToString(review.OverallStatus),
							NavigateUrl = Request.FilePath + "?rid=" + review.Id,
						});
					}
				}
			}

			bool iOwnTheChange = changeList.UserName.EqualsIgnoreCase(userName);
			if (!iAmAReviewer && !iOwnTheChange)
			{
				AddLabel("<br>");
				AddLink("I would like to review this change.", "?cid=" + changeList.Id + "&action=makemereviewer")
					.AppendCSSClass("button");
			}

			if (iOwnTheChange && changeList.Stage != 2 && /* 2 = closed */
				GetChangeListStatus(changeList) == ChangeListStatus.Closable)
			{
				AddLabel("<br>");
				AddLink("Close Review", "?cid=" + changeList.Id + "&action=close")
					.AppendCSSClass("button");
			}

			if (iOwnTheChange)
			{
				HintsData.IsChangeAuthor = true;
				AddLabel("<h3>My response</h3>");
			}
			else
			{
				HintsData.IsChangeReviewer = true;
				AddLabel("<h3>My comments</h3>");
			}

			TextBox commentTextBox = new TextBox();
			ActivePage.Controls.Add(commentTextBox);
			commentTextBox.TextMode = TextBoxMode.MultiLine;
			commentTextBox.AppendCSSClass("CssGeneralCommentInputBox");
			commentTextBox.ID = "reviewcommenttextbox";

			AddLabel("<br>");

			Button submitReviewButton =
				new Button() { ID = "submitreviewbutton", Text = "Submit", CssClass = "button" };
			ActivePage.Controls.Add(submitReviewButton.As(HtmlTextWriterTag.P));
			submitReviewButton.Click += new EventHandler(
				delegate(object sender, EventArgs e) { submitReview_Clicked(cid, sender, e); });

			AddLabel("<br>");

			int myReviewId = GetBaseReviewId(userName, cid);
			if (myReviewId == 0)
				return;

			DisplayReviewLineComments(myReviewId);
		}
Esempio n. 3
0
		/// <summary>
		/// Returns a row with title and body as two cells.
		/// </summary>
		/// <param name="title"> The text for the first cell (usually, a description). </param>
		/// <param name="body"> The text for the second cell (usually, the information). </param>
		private TableRow GetChangeDescriptionRow(string title, string body)
		{
			TableRow row = new TableRow();
			row.AppendCSSClass("CssTopAligned");

			TableCell cell = new TableCell() { Text = title };
			row.Cells.Add(cell);
			cell.AppendCSSClass("CssTitle");

			cell = new TableCell() { Text = "<pre>" + body + "</pre>" };
			row.Cells.Add(cell);
			cell.AppendCSSClass("CssBody");

			return row;
		}
Esempio n. 4
0
		/// <summary>
		/// Displays files from a change list by adding a row with details regarding the file to a table.
		/// </summary>
		/// <param name="table"> Table where the data goes. </param>
		/// <param name="attachment"> The attachment. </param>
		private void AddAttachmentRow(Table page, Attachment attachment)
		{
			TableRow row = new TableRow();
			page.Rows.Add(row);

			TableCell cell = new TableCell();
			row.Cells.Add(cell);

			cell = new TableCell();
			row.Cells.Add(cell);
			cell.AppendCSSClass("CssAttachment");

			HyperLink link = new HyperLink();
			cell.Controls.Add(link);

			link.NavigateUrl = attachment.Link;
			link.Text = WrapTimeStamp(attachment.TimeStamp) + "&nbsp;&nbsp;" +
				(attachment.Description != null ? attachment.Description : Server.HtmlEncode(attachment.Link));
		}
Esempio n. 5
0
		/// <summary>
		/// Displays files from a change list by adding a row with details regarding the file to a table.
		/// </summary>
		/// <param name="file"> The top-level file data (names). </param>
		/// <param name="lastVersion"> The last version of the file. </param>
		/// <param name="hasTextBody"> Whether to display the file as a hyperlink </param>
		/// <param name="latestReview">The latest review that the user has submitted for this changelist. May be null.</param>
		private TableRow GetChangeFileRow(
			DataModel.ChangeFile file,
			AbstractedFileVersion lastVersion,
			bool hasTextBody,
			Review latestReview)
		{
			TableRow row = new TableRow();
			if (!file.IsActive)
				row.AppendCSSClass("CssInactiveFile");

			TableCell cell = new TableCell();
			// If the latest version of this file has a timestamp greater than the latest review,
			// then present a "new!" icon in the list to allow the user to quickly determine what
			// has changed since they last reviewed this change.
			if (latestReview != null &&
				lastVersion.TimeStamp != null &&
				latestReview.TimeStamp.CompareTo(lastVersion.TimeStamp) < 0)
			{
				cell.AppendCSSClass("CssNewIcon");
				cell.Controls.Add(new Image()
				{
					ImageUrl = "~/images/new_icon.png",
					AlternateText = "This file has changed since your last review submission."
				});
			}
			row.Cells.Add(cell);

			cell = new TableCell();
			row.Cells.Add(cell);

			string moniker = null;
			if (lastVersion == null)
			{
				moniker = file.ServerFileName + "#" + " (no versions found)";
			}
			else if (lastVersion.Action == SourceControlAction.DELETE)
			{
				moniker = file.ServerFileName + "#" + lastVersion.Revision + " DELETE";
			}
			else if (lastVersion.Action == SourceControlAction.BRANCH)
			{
				moniker = file.ServerFileName + "#" + lastVersion.Revision + " BRANCH";
			}
			else if (lastVersion.Action == SourceControlAction.INTEGRATE)
			{
				moniker = file.ServerFileName + "#" + lastVersion.Revision + " INTEGRATE";
			}
			else if (lastVersion.Action == SourceControlAction.RENAME)
			{
				moniker = file.ServerFileName + "#" + lastVersion.Revision + " RENAME";
			}
			else
			{
				moniker = file.ServerFileName + "#" + lastVersion.Revision +
					((lastVersion.Action == SourceControlAction.ADD) ? " ADD" : " EDIT");
			}

			if (hasTextBody)
			{
				HyperLink link = new HyperLink();
				cell.Controls.Add(link);
				link.NavigateUrl = Request.FilePath + "?fid=" + file.Id;
				link.Text = moniker;
			}
			else
			{
				cell.Text = moniker;
			}

			return row;
		}