<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jmhurst</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jmhurst"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Jmhurst"/>
	<updated>2026-09-06T03:56:14Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138772</id>
		<title>CSC/ECE 517 Spring 2021 - E2111. Refactor github metrics integration</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138772"/>
		<updated>2021-04-30T15:04:19Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Abstract==&lt;br /&gt;
Github repository and pull request links are submitted as part of programming projects in several NC State CSC courses. Instructors would like to compile statistics on these Github links on a per-project, per-team basis, and integrate display of these data within Expertiza (similar to Github Insights). Code was introduced to add this functionality in a [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2018-_Project_E1858._Github_metrics_integration 2018 project, E1858,] which was not merged, and another team re-visited [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_E1983._Refactor_E1858,_Github_metrics_integration_view this project in 2019], which was also not merged. The project team will troubleshoot existing code to eliminate issues that prevented these projects from being merged. Additionally, the team will add new features based on design patterns and MVC principles, including a Metrics model, that will decouple the Github Metrics functionality from other areas of the application and allow for future extensibility. Finally, the team will investigate DRYing the code using existing graphing functionality, and complete an analysis of the automated testing for existing and new code.&lt;br /&gt;
&lt;br /&gt;
==Functionality==&lt;br /&gt;
The Github metrics system, as currently implemented, queries data from Github based on repository and pull request links submitted for the assignment. The commit frequency is displayed in a bar graph. Numeric statistics, as well as Travis CI status, are displayed in a table.&lt;br /&gt;
&lt;br /&gt;
[[File:GithubMetricsExample.png]]&lt;br /&gt;
&lt;br /&gt;
==Methodology==&lt;br /&gt;
The team has synced the revised 2019 project code with the current status of the Expertiza/beta branch and begun analyzing the existing code. Prior customer feedback has advised the team to address inextensibility/coupling issues, documentation issues, and synchronization issues. We are performing our own end-to-end analysis of the code in addition to this feedback, and have so far found numerous action items. We have separated these action items into Primary and Secondary objectives: Primary objectives are &amp;quot;must-haves&amp;quot; that need to be addressed before the existing code is mergeable. Secondary objectives are &amp;quot;nice-to-haves,&amp;quot; or features that were either left out of the original project or would make the integration display more complete.&lt;br /&gt;
&lt;br /&gt;
==Design Patterns==&lt;br /&gt;
In order to achieve the Primary Objectives while making the application more extensible, the team proposes using a more strict implementation of the '''Adapter''' design pattern to decouple the details of the Github API from the proposed Metrics model. Elements of the '''Observer''' design pattern will also come into play to ensure that once Github data is queried and stored, and an assignment has come to a close, the Metric for that Assignment is unsubscribed from the Github API. This will further decouple the dependency on API data while retaining the data for future statistical analysis and/or research studies.&lt;br /&gt;
&lt;br /&gt;
==Team Action Items==&lt;br /&gt;
These action items include both refactoring tasks, and required feature enhancements to be implemented.&lt;br /&gt;
&lt;br /&gt;
====Minor API Changes Breaking Functionality====&lt;br /&gt;
The Github API has had minor changes since 2018 which are causing the previously working code to either not display, or throw a NoMethodError. We have already analyzed and applied bugfixes to address these minor API changes, but would like to further refine how the API calls are set-up to decouple our code from the API and make it more robust.&lt;br /&gt;
&lt;br /&gt;
One key issue is that the Github API no longer permits queries with empty hash keys. This shows up in queries with a single page, where &amp;lt;code&amp;gt; after: ___&amp;lt;/code&amp;gt; has, in the past, been left blank. In order to resume queries, the team now tests whether &amp;lt;code&amp;gt; after: SHA1 &amp;lt;/code&amp;gt; should be included in the query, and if not, we do not add the empty key. This permits data to be pulled from Github where repos have fewer than two pages of commits.&lt;br /&gt;
&lt;br /&gt;
====Refactoring Improvements====&lt;br /&gt;
Several method and variable names do not use good Ruby style, and should be refactored to improve style and clarity. Some examples include get_pull_request_details() and authorize_github(). These shall be refactored for improved clarity, and may be relocated within the application to make the code better fit the MVC system. See Metrics Model section for further details.&lt;br /&gt;
&lt;br /&gt;
====Improvements to Data Time and User Constraints====&lt;br /&gt;
If a team submits a repo containing merged data, with many Expertiza Development Team commits, these commits currently show up as part of the team metrics. Improved logic needs to be implemented to timebox the statistics to only cover commits made during the duration of the project. Additionally, improvements are needed for the way in which commit owners are handled, so that metrics are not stored/displayed/statistically analyzed for Expertiza Development Team commits. An example of a problematic submission is shown below.&lt;br /&gt;
&lt;br /&gt;
[[File:GithubIssue1.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Major Feature: Metrics Model====&lt;br /&gt;
The Expertiza system architect has instructed the team to handle a major shortcoming in the original project as part of this body of work: the creation of a new model to handle local storage of the metric data. This new model should be able to support future extensibility for new metrics, like Travis CI or Danger. This change will bring the Github metrics system in full compliance with MVC principles and allow for local warehousing of data for performance and data retention.&lt;br /&gt;
&lt;br /&gt;
The current implementation works synchronously, and each time a Github integration route is followed, the entire collection of Github data is requested from the Github API and used to render the graphs and statistics. These data are cumbersome, difficult to work with, and nearly impossible to extend to future functionality. Furthermore, this coupling to the Github API means that data is not being warehoused within the system and makes adding new functionality much more difficult. The code uses controller methods to load the data and format it for display.&lt;br /&gt;
&lt;br /&gt;
The team proposes creating a fully realized MVC implementation to separate Github queries, warehousing of data, and displaying of graphs and statistics which are currently handled in a single chain of controller methods. This will require implementation of a new model, and additional database tables to store the data as it is queried. Also, views will need to be uncoupled from the Github API and reconfigured to render data from the local data warehouse. Furthermore, this will allow methods that aren't CRUDlike to be moved from the Github metrics controller into the new model.&lt;br /&gt;
&lt;br /&gt;
'''Diagram of Proposed MVC Architecture for New Metrics Model'''&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:githubModelFlow.png]] &lt;br /&gt;
&lt;br /&gt;
One point of discussion among the team and our mentor will be, should an update query be run each time the page is displayed, or should a burst of queries be run at the expiration of a deadline to populate the database table? Another option may be to create a manual &amp;quot;Github Update&amp;quot; link, but reject all data after a deadline. Yet another option would be to keep all Github data and use the &amp;quot;late data&amp;quot; to call out  number of late submissions on the statistics page.&lt;br /&gt;
&lt;br /&gt;
=====Proposed Schema and Entries Example=====&lt;br /&gt;
User '''JoeCodeMonkey''', on '''team 15''', added 3 commits which were queried from Github. The ''Metrics'' table will keep track of each metric, with the ''Metrics_Data_Points'' table keeping track of each individual data point. The ''Metric_Sources'' and ''Metric_Data_Point_Types'' tables are used as lookup tables to keep track of the types of metric data being stored in ''Metrics'' and ''Metrics_Data_Point''.&lt;br /&gt;
&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;padding-top: 2pt;padding-left: 15pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Table: Metric_Sources&amp;lt;/p&amp;gt;&amp;lt;table style=&amp;quot;border-collapse:collapse;margin-left:13.275pt&amp;quot; cellspacing=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;tr style=&amp;quot;height:30pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;ID(PK)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Source Name&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Description&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Github&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;CVS Hosting&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Travis CI&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Build Tool&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;padding-top: 2pt;padding-left: 15pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Table: Metric_Data_Point_Types&amp;lt;/p&amp;gt;&amp;lt;table style=&amp;quot;border-collapse:collapse;margin-left:13.275pt&amp;quot; cellspacing=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;tr style=&amp;quot;height:56pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;ID(PK)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 10pt;text-align: left;&amp;quot;&amp;gt;Source_Id (FK)&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-top: 1pt;padding-left: 1pt;padding-right: 22pt;text-indent: 0pt;line-height: 108%;text-align: left;&amp;quot;&amp;gt;to Metric Sources Table)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Value_Type&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Name&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:144pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Description&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;Github -0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;string&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;github_user&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:144pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Github user&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;int&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;lines_added&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:144pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Number of lines added&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;int&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;lines_removed&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:144pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Number of lines removed&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;datetime&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;date_committed&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:144pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Date of change&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;padding-top: 2pt;padding-left: 15pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Table: Metrics&amp;lt;/p&amp;gt;&amp;lt;table style=&amp;quot;border-collapse:collapse;margin-left:13.275pt&amp;quot; cellspacing=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;ID(PK)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Source_Id (FK&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;Team_ID (FK)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;0 -Github&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;15 - Team 15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;0 - Github&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;15 - Team 15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;0 - Github&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;15 - Team 15&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p style=&amp;quot;padding-top: 2pt;padding-left: 15pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Table: Metrics_Data_Points&amp;lt;/p&amp;gt;&amp;lt;table style=&amp;quot;border-collapse:collapse;margin-left:13.275pt&amp;quot; cellspacing=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;tr style=&amp;quot;height:45pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Id&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;padding-right: 3pt;text-indent: 0pt;line-height: 108%;text-align: left;&amp;quot;&amp;gt;Metric_Id (FK to metric table)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;padding-right: 1pt;text-indent: 0pt;line-height: 108%;text-align: left;&amp;quot;&amp;gt;Metric_Data_P oint_Type_Id (FK)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p style=&amp;quot;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;s1&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;Value&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:17pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-top: 2pt;padding-right: 1pt;text-indent: 0pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-top: 2pt;padding-left: 3pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;(metric id )0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-top: 2pt;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;0 (github_user)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-top: 2pt;padding-left: 1pt;text-indent: 0pt;text-align: left;&amp;quot;&amp;gt;JoeCodeMonkey&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;1 (lines_added)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;2 (lines_remove&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;3 (date_commit&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;1/2/2017 1600&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;4&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;1 (metric id 1)&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;JoeCodeMonkey&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;8&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;5&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;1/5/2017 1700&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;8&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;0&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;JoeCodeMonkey&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;9&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;7&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;10&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;6&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr style=&amp;quot;height:15pt&amp;quot;&amp;gt;&amp;lt;td style=&amp;quot;width:52pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;11&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:68pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;2&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:73pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-right: 1pt;text-indent: 0pt;line-height: 13pt;text-align: right;&amp;quot;&amp;gt;3&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td style=&amp;quot;width:116pt;border-top-style:solid;border-top-width:1pt;border-left-style:solid;border-left-width:1pt;border-bottom-style:solid;border-bottom-width:1pt;border-right-style:solid;border-right-width:1pt&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;s2&amp;quot; style=&amp;quot;padding-left: 1pt;text-indent: 0pt;line-height: 13pt;text-align: left;&amp;quot;&amp;gt;1/20/2017 1430&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Lack of Comments / Opaque Code====&lt;br /&gt;
The majority of methods added in E1858 are lacking comments describing their purpose and functionality and comments were not modified for any modified methods.&amp;lt;br&amp;gt;&lt;br /&gt;
Comments will be added to the following files&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/auth_controller.rb: New methods oauth_login and github_login need comments, existing method action_allowed? needs a comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/grade_controller.rb: Large amount of new methods need comments, existing method &amp;quot;view&amp;quot; needs modification to existing comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/helpers/grades_helper.rb: The methods display_github_metrics, chart_options, and graph_scales need comments describing functionality&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/assignments/list_submissions.html.erb: New GitHub Metrics column added to table&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_tabbing.html.erb: Class &amp;quot;tmr_tab&amp;quot; will have to be explained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_teammate_reviews_tab.html.erb: Class &amp;quot;github&amp;quot; will have to be explained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view.html.erb: JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view_github_metrics.html.erb : JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;spec/controllers/grades_controller_spec.rb: Comments will have to be added describing the RSpec tests including their purpose and how they achieve their tests&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====DRYing Graphing Code====&lt;br /&gt;
The existing code uses new code to graph the statistical Github data. Code already exists elsewhere in Expertiza (Statistics) to graphically display statistical data, and the team intends to replace the Github Graphing code by re-using the existing graphing code, if possible.&lt;br /&gt;
&lt;br /&gt;
====Properly Timebox Statistics====&lt;br /&gt;
The existing code pulls all commits from a repository or pull request. We propose utilizing the assignment start and end dates to ensure that only commits which apply to the scope of the project are included in the statistics view/analysis.&lt;br /&gt;
&lt;br /&gt;
====Bugfix: Hide Expertiza Development Team====&lt;br /&gt;
At this time, if a team merges in many old commits by the Expertiza Development team, the bar graph statistics may be thrown off by including the development team (&amp;quot;Winbobob&amp;quot; et al). Logic exists in the code to prevent these developer's work from being credited to the project team, but it is not working reliably.  If the data permanence objective is met, creating more complex queries should be easier, and the team would like to explore updating this feature to to &amp;quot;hide known contributors,&amp;quot; and ensure that it is working reliably. This would hide Github data from any user included on the &amp;quot;blacklist&amp;quot; which can be manually curated with the short list of Expertiza developers. While this would not address edge cases where an Expertiza developer later participates in a course using Expertiza, this edge case should be quite rare.&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
The 2018 project wrote numerous tests, several of which are deeply coupled into the grades_controller tests. The team proposes several changes to the testing strategy.&lt;br /&gt;
&lt;br /&gt;
====Decouple Tests / Pass Builds====&lt;br /&gt;
Builds never passed for this project because, in the current code, a user must be logged in and authorized to Github to render grades#view, and this broke several (expected) test cases. As part of decoupling Github from the grades controller, we expect to decouple and revert tests for the grades controller such that the tests become independent, permitting the build to pass if our Github controller tests pass as expected.&lt;br /&gt;
&lt;br /&gt;
====Detailed Analysis of Github Testing====&lt;br /&gt;
The previous team wrote numerous intricate tests for the new functionality. Our team intends to go through these tests line-by-line and ensure that having so many tests is necessary, and the most elegant way to address the issue. We believe that some of the Github refactoring should move methods to become private, which may mean that specific tests for that method shouldn't be used. Alternately, particularly for API authentication which can be tricky, it may be prudent to have discrete tests. Finally, we want to ensure that the tests are actually covering the code -- we are receiving a TravisCI warning about tests without expectations, which needs to be resolved before the code can be merged.&lt;br /&gt;
&lt;br /&gt;
====New Tests for Metrics Model====&lt;br /&gt;
The new model being created, along with associated database tables, will lead to new tests being required for this model. These tests will need to be written first in keeping with TDD principles. Also, some tests may need revision when code is moved from the Github Metrics controller into the new MVC architecture. These tests will include, but not be limited to, testing cases when Github Pull requests exist, and do not exist (for Expertiza and non-Expertiza projects), validation of all data types being stored in the Model / new database tables, appropriate coverage of new and existing graphing helpers, cases when multiple repos and/or pull requests are included in the assignment links, and edge cases when Repos or pull requests were submitted but access rights have not been granted.&lt;br /&gt;
&lt;br /&gt;
==Final Design UML==&lt;br /&gt;
We show the UML for the new classes and model we created and implemented in our final project for a overview. Other than these, we also modified some of the existing classes and method, we will give detailed introduction later.&lt;br /&gt;
&lt;br /&gt;
[[File:E2111UML.png]]&lt;br /&gt;
&lt;br /&gt;
==Documentation==&lt;br /&gt;
===MetricsHelper===&lt;br /&gt;
We have a helper model called MetricsHelper, which contain helper methods for graphing:&lt;br /&gt;
&lt;br /&gt;
====display_github_metrics(parsed_data, authors, dates)====&lt;br /&gt;
This function take parsed Github metrics data, authors in a PR, and dates of all commits as parameter. It Creates the bar graph for the Github metrics data. Links the authors with their Github data and assigns them a color. Currently supports up to 6 different colors and will loop if it goes over.&lt;br /&gt;
&lt;br /&gt;
====display_totals_piechart(parsed_data, authors, dates)====&lt;br /&gt;
This function take parsed Github metrics data, authors in a PR, and dates of all commits as parameter. It Creates the pie chart for the Github metrics data. Links the authors with their Github data and assigns them a color. It is easy for an instructor to tell the ratio of contribution of each team member with a pie chart.&lt;br /&gt;
&lt;br /&gt;
====chart_options====&lt;br /&gt;
This function does not take any parameters. It defines the general settings of the Github metrics chart&lt;br /&gt;
&lt;br /&gt;
====graph_scales====&lt;br /&gt;
This function does not take any parameters. It defines the labels and display of the data on the Github metrics chart&lt;br /&gt;
&lt;br /&gt;
===Metric===&lt;br /&gt;
This is a new model that saves user's Github email as the identifier of their Github account. With this information, we can connect Github metrics with each students. It also saves the team_id, the participant_id, and the cumulative total commits in the submitted PR for each user. We make this model in a way that it is easy for future team to extend.&lt;br /&gt;
&lt;br /&gt;
====pull_query(hyperlink_data, after)====&lt;br /&gt;
This function takes a submitted Github pull request URL and a cursor for pagination in Github GraphQL API as parameter. It Formulate and return the actual query message to send over HTTP request to do the GraphQL query for a Github pull request. For more detailed information, check Github GraphQL API.&lt;br /&gt;
&lt;br /&gt;
====repo_query(hyperlink_data, date, after)====&lt;br /&gt;
This function takes a submitted Github repository URL, the date of the project starting date, and a cursor for pagination in Github GraphQL API as parameter. It Formulate and return the actual query message to send over HTTP request to do the GraphQL query for a Github repository. For more detailed information, check Github GraphQL API.&lt;br /&gt;
&lt;br /&gt;
===MetricsController===&lt;br /&gt;
The Metrics controller contains all the logics for querying Github metrics and present these metrics with the default show method. &lt;br /&gt;
&lt;br /&gt;
====action_allowed?====&lt;br /&gt;
This function does not take any parameters, it checks whether the current user has the required privilege to run certain method.&lt;br /&gt;
&lt;br /&gt;
====create_github_metric(team_id, github_id, total_commits)====&lt;br /&gt;
This function takes team_id, github_id, and total_commits as parameter. It does some validations and logic checking of the input value, then it saves all values to database as fields of the Metric model. &lt;br /&gt;
&lt;br /&gt;
====query_assignment_statistics====&lt;br /&gt;
This function does not take any parameters, it runs a query against all the link submissions for an entire assignment, populating the DB fields that are used by the view_team in grades heatgrid showing user contributions&lt;br /&gt;
&lt;br /&gt;
====show====&lt;br /&gt;
The default show method, which renders the html page that shows all Github metrics with a bar chart and a pie chart.&lt;br /&gt;
&lt;br /&gt;
====authorize_github====&lt;br /&gt;
This function redirect the user to Github authorization endpoint to authorize the current user. An authorized user can use the Github API with 5000 rate limits per hour. Unauthorized user only has 60 rate limits per hour, which may not be enough.&lt;br /&gt;
&lt;br /&gt;
====single_submission_initial_query(id)====&lt;br /&gt;
This function takes the participant id as parameter. It calls several helper methods to query Github metrics information from links that this team submitted and populate all related instance variable, which will be used in frontend presentation.&lt;br /&gt;
&lt;br /&gt;
====retrieve_github_data====&lt;br /&gt;
This function call corresponding method to retrieve Github pull request metrics information or repository information base on different conditions.&lt;br /&gt;
&lt;br /&gt;
====query_all_pull_requests(pull_links)====&lt;br /&gt;
This function take all PR URLs that a team submitted and retrieve all Github metrics.&lt;br /&gt;
&lt;br /&gt;
====pull_request_data(hyperlink_data)====&lt;br /&gt;
This function takes all hyperlink_data includes pull request number, repository name, owner name of a single PR url as parameter and retrieve all Github metrics for a single PR.&lt;br /&gt;
&lt;br /&gt;
====parse_pull_request_data(github_data)====&lt;br /&gt;
This function takes the Github metrics of a PR returned by the query as parameter, parse and store them into corresponding instance variable.&lt;br /&gt;
&lt;br /&gt;
====query_all_merge_statuses====&lt;br /&gt;
This function saves each PR's statuses in a hash. This is done through Github REST API not GraphQL.&lt;br /&gt;
&lt;br /&gt;
====retrieve_repository_data(repo_links)====&lt;br /&gt;
This function take all repo URLs that a team submitted and retrieve all Github metrics.&lt;br /&gt;
&lt;br /&gt;
====parse_repository_data(github_data)====&lt;br /&gt;
This function takes the Github metrics of a repo returned by the query as parameter, parse and store them into corresponding instance variable.&lt;br /&gt;
&lt;br /&gt;
====team_statistics(github_data, data_type)====&lt;br /&gt;
This function takes all Github metrics we queried and a data_type that specify the github_data is for a PR or a repo as parameter. It does the basic accounting and store total additions, total deletions, total files changed, total commits, and merge status in instance variable for future presentation purpose.&lt;br /&gt;
&lt;br /&gt;
====count_github_authors_and_dates(author_name, author_email, commit_date)====&lt;br /&gt;
This function does the accounting for each author in this PR. It calculates how many commits an author has on each date.&lt;br /&gt;
&lt;br /&gt;
====sort_commit_dates====&lt;br /&gt;
This function sort the ruby array that saves the commit information base on date.&lt;br /&gt;
&lt;br /&gt;
====query_commit_statistics(data)====&lt;br /&gt;
This function takes the query message as parameter and makes the actual Github api HTTP request with GraphQL and query message.&lt;br /&gt;
&lt;br /&gt;
====query_pull_request_status(pr_object)====&lt;br /&gt;
This function takes the global GraphQL id of a PR object as parameter and make the actual Github api HTTP request to query the PR's status info.&lt;br /&gt;
&lt;br /&gt;
===Others===&lt;br /&gt;
In addition to the changes mentioned above, we also added a one auth method in auth_controller.rb. We add new show.html.rb page for frontend presentation in app/views/metrics. We also added new testing files.&lt;br /&gt;
&lt;br /&gt;
==Resources==&lt;br /&gt;
=====Key Links=====&lt;br /&gt;
[https://github.com/smdupor/expertiza/tree/E2111GithubMet E2111 Repository]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/expertiza/expertiza/pull/1917 E2111 Pull Request]&lt;br /&gt;
&lt;br /&gt;
[https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2018-_Project_E1858._Github_metrics_integration Original E1858 Documentation]&lt;br /&gt;
&lt;br /&gt;
[https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_E1983._Refactor_E1858,_Github_metrics_integration_view E1983 Revision Documentation]&lt;br /&gt;
&lt;br /&gt;
[https://docs.github.com/en/rest Github API Documentation]&lt;br /&gt;
&lt;br /&gt;
[https://www.youtube.com/watch?v=WX7a4f767QI Demo Video]&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138488</id>
		<title>CSC/ECE 517 Spring 2021 - E2111. Refactor github metrics integration</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138488"/>
		<updated>2021-03-29T19:52:48Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Lack of Comments / Opaque Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Abstract==&lt;br /&gt;
Github repository and pull request links are submitted as part of many projects in several NC State CSC courses. Instructors would like to compile statistics on these Github links on a per-project, per-team basis, and integrate display of these data within Expertiza (similar to Github Insights). Code was introduced to add this functionality in a [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2018-_Project_E1858._Github_metrics_integration 2018 project, E1858,] which was not merged, and another team re-visited [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_E1983._Refactor_E1858,_Github_metrics_integration_view this project in 2019], which was not merged. Our team aims to tackle the issues we've found that prevented these past team's code from being merged.&lt;br /&gt;
&lt;br /&gt;
==Functionality==&lt;br /&gt;
ADD SECTION INTRODUCING THE FUNCTIONALITY (Description and Maybe one screenshot. Past group went overboard with copy-pasting old screenshots.)&lt;br /&gt;
&lt;br /&gt;
TALK ABOUT OMNIAUTH FOR API AND HOW IT ALL WORKS&lt;br /&gt;
&lt;br /&gt;
==Methods==&lt;br /&gt;
The team has synced the original 2018 project code with the current status of the Expertiza/beta branch and begun analyzing the existing code. Prior customer feedback has advised the team to look for coupling, lack of comments/opaque code, and testing issues. We are performing our own end-to-end analysis of the code in addition to this feedback, and have so far found numerous action items. We have separated these action items into Primary and Secondary objectives: Primary objectives are &amp;quot;must-haves&amp;quot; that need to be addressed before the existing code is mergeable. Secondary objectives are &amp;quot;nice-to-haves,&amp;quot; or features that were either left out of the original project or would make the integration display more complete. &lt;br /&gt;
&lt;br /&gt;
==Primary Objectives (Immediate Action Items)==&lt;br /&gt;
These action items must be addressed before any feature improvements can be attempted. &lt;br /&gt;
&lt;br /&gt;
====API Changes Breaking Functionality====&lt;br /&gt;
The Github API has had minor changes since 2018 which are causing the previously working code to either not display, or throw a NoMethodError. We have already analyzed and applied bugfixes to address these minor API changes, but would like to further refine how the API calls are set-up to decouple our code from the API and make it more robust.&lt;br /&gt;
&lt;br /&gt;
ADD CODEBLOCKS TALKING ABOUT THE TWO BUGFIXES&lt;br /&gt;
&lt;br /&gt;
====Coupling to the Grades Controller====&lt;br /&gt;
CONTINUED&lt;br /&gt;
&lt;br /&gt;
====Views Contain Large Javascript Blocks====&lt;br /&gt;
The views in view.html.erb and view_github_metrics.html.erb in app/views/grades has large JavaScript blocks that were added for the Github metrics functionality. We will move those JavaScript blocks to helper files view_grades_helper.js and view_team_in_grades.js in app/assets/javascipts. The view_team_in_grades.js file is already created with some JavaScript functions that the view_github_metrics.html.erb file already uses, but we will create a new helper file for view.html.erb because it does not currently have any helper files associated with it.&lt;br /&gt;
&lt;br /&gt;
====Methods Out-of-Order====&lt;br /&gt;
CHANGE TO CALLING ORDER OR BETTER RE_ORG&lt;br /&gt;
&lt;br /&gt;
====Lack of Comments / Opaque Code====&lt;br /&gt;
The majority of methods added in E1858 are lacking comments describing their purpose and functionality and comments were not modified for any modified methods.&amp;lt;br&amp;gt;&lt;br /&gt;
Comments will be added to the following files&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/auth_controller.rb: New methods oauth_login and github_login need comments, existing method action_allowed? needs a comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/grade_controller.rb: Large amount of new methods need comments, existing method &amp;quot;view&amp;quot; needs modification to existing comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/helpers/grades_helper.rb: The methods display_github_metrics, chart_options, and graph_scales need comments describing functionality&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/assignments/list_submissions.html.erb: New GitHub Metrics column added to table&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_tabbing.html.erb: Class &amp;quot;tmr_tab&amp;quot; will have to be explained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_teammate_reviews_tab.html.erb: Class &amp;quot;github&amp;quot; will have to be explained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view.html.erb: JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view_github_metrics.html.erb : JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;spec/controllers/grades_controller_spec.rb: Comments will have to be added describing the RSpec tests including their purpose and how they achieve their tests&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Testing====&lt;br /&gt;
SHORT COMMENTARY ON COUPLED TESTING TO GRADES/INSTRUCTOR VIEW. SEE TEST PLAN FOR AT_LENGTH DISCUSSION.&lt;br /&gt;
&lt;br /&gt;
==Secondary Objectives==&lt;br /&gt;
The team would like to address one or more of these secondary objectives which were not addressed by prior teams, as long as they do not break existing functionality. These are listed in order of importance. &lt;br /&gt;
&lt;br /&gt;
====Data Permanence (Model)====&lt;br /&gt;
The existing code polls Github every time the data is displayed. This makes the rendering code unnecessarily complex and introduces the potential for future data loss. For performance reasons, decoupling, and the benefit of future developers and data permanence, an improvement to this method would be creating a model and database table to hold these data, so that the display is querying the local DB instead of querying out to Github each time the display is rendered. The team would like to extend the proposed new controller to include a new model that will take this data, and only insert the diff into the database. One point of discussion among the team and our mentor will be, should an update query be run each time the page is displayed, or should a burst of queries be run at the expiration of a deadline to populate the database table? Another option may be to create a manual &amp;quot;Github Update&amp;quot; link, but reject all data after a deadline. Yet another option would be to keep all Github data and use the &amp;quot;late data&amp;quot; to call out  number of late submissions on the statistics page.&lt;br /&gt;
&lt;br /&gt;
====New Feature: Hide Expertiza Development Team====&lt;br /&gt;
At this time, if a team merges in many old commits by the Expertiza Development team, the bar graph statistics are thrown off by including the development team (&amp;quot;Winbobob&amp;quot; et al). If the data permanence objective is met, creating more complex queries should be easier, and the team would like to explore adding a feature to the Github integration display to &amp;quot;hide known contributors,&amp;quot; or similar. This would hide Github data from any user included on the &amp;quot;blacklist&amp;quot; which can be manually curated with the short list of Expertiza developers. While this would not address edge cases where an Expertiza developer later participates in a course using Expertiza, this edge case should be quite rare.&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
The 2018 project wrote numerous tests, several of which are deeply coupled into the grades_controller tests. The team proposes several changes to the testing strategy.&lt;br /&gt;
&lt;br /&gt;
====Decouple Tests / Pass Builds====&lt;br /&gt;
Builds never passed for this project because, in the current code, a user must be logged in and authorized to Github to render grades#view, and this broke several (expected) test cases. As part of decoupling Github from the grades controller, we expect to decouple and revert tests for the grades controller such that the tests become independent, permitting the build to pass if our Github controller tests pass as expected.&lt;br /&gt;
&lt;br /&gt;
====Detailed Analysis of Github Testing====&lt;br /&gt;
The previous team wrote numerous intricate tests for the new functionality. Our team intends to go through these tests line-by-line and ensure that having so many tests is necessary, and the most elegant way to address the issue. We believe that some of the Github refactoring should move methods to become private, which may mean that specific tests for that method shouldn't be used. Alternately, particularly for API authentication which can be tricky, it may be prudent to have discrete tests. Finally, we want to ensure that the tests are actually covering the code -- we are receiving a TravisCI warning about tests without expectations, which needs to be resolved before the code can be merged.&lt;br /&gt;
&lt;br /&gt;
====New Tests for Secondary Objectives====&lt;br /&gt;
If a new model is created along with associated database tables, new tests for this model will need to be written first in keeping with TDD principles.&lt;br /&gt;
&lt;br /&gt;
==Resources==&lt;br /&gt;
ADD PERTINENT LINKS&lt;br /&gt;
&lt;br /&gt;
==Important Data Structure==&lt;br /&gt;
Example of function get_pull_request_details return value(id field in node is omitted for the saving of space):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
{&amp;quot;data&amp;quot;=&amp;gt;{&amp;quot;repository&amp;quot;=&amp;gt;{&amp;quot;pullRequest&amp;quot;=&amp;gt;{&amp;quot;number&amp;quot;=&amp;gt;23281,&lt;br /&gt;
                                         &amp;quot;additions&amp;quot;=&amp;gt;183,&lt;br /&gt;
                                         &amp;quot;deletions&amp;quot;=&amp;gt;119,&lt;br /&gt;
                                         &amp;quot;changedFiles&amp;quot;=&amp;gt;13,&lt;br /&gt;
                                         &amp;quot;mergeable&amp;quot;=&amp;gt;&amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
                                         &amp;quot;merged&amp;quot;=&amp;gt;true,&lt;br /&gt;
                                         &amp;quot;headRefOid&amp;quot;=&amp;gt;&amp;quot;58f027468c8908160196c34681ff5d224a84d75c&amp;quot;,&lt;br /&gt;
                                         &amp;quot;commits&amp;quot;=&amp;gt;{&amp;quot;totalCount&amp;quot;=&amp;gt;2,&lt;br /&gt;
                                                     &amp;quot;pageInfo&amp;quot;=&amp;gt;{&amp;quot;hasNextPage&amp;quot;=&amp;gt;false,&lt;br /&gt;
                                                                  &amp;quot;startCursor&amp;quot;=&amp;gt;&amp;quot;MQ&amp;quot;,&lt;br /&gt;
                                                                  &amp;quot;endCursor&amp;quot;=&amp;gt;&amp;quot;Mg&amp;quot;&lt;br /&gt;
                                                                  },&lt;br /&gt;
                                                     &amp;quot;edges&amp;quot;=&amp;gt;[{&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                         &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Manish Goregaokar&amp;quot;&lt;br /&gt;
                                                                                              },&lt;br /&gt;
                                                                                    &amp;quot;additions&amp;quot;=&amp;gt;10, &amp;quot;deletions&amp;quot;=&amp;gt;10,&lt;br /&gt;
                                                                                    &amp;quot;changedFiles&amp;quot;=&amp;gt;1,&lt;br /&gt;
                                                                                    &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T02:44:08Z&amp;quot;&lt;br /&gt;
                                                                                   }&lt;br /&gt;
                                                                         }&lt;br /&gt;
                                                                },&lt;br /&gt;
                                                                {&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                          &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Maria Sable&amp;quot;},&lt;br /&gt;
                                                                                     &amp;quot;additions&amp;quot;=&amp;gt;173,&lt;br /&gt;
                                                                                     &amp;quot;deletions&amp;quot;=&amp;gt;109,&lt;br /&gt;
                                                                                     &amp;quot;changedFiles&amp;quot;=&amp;gt;12,&lt;br /&gt;
                                                                                     &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T23:54:52Z&amp;quot;&lt;br /&gt;
                                                                                     }&lt;br /&gt;
                                                                          }&lt;br /&gt;
                                                                 }&lt;br /&gt;
                                                                ]&lt;br /&gt;
                                                     }&lt;br /&gt;
                                         }&lt;br /&gt;
                         }&lt;br /&gt;
          }&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138487</id>
		<title>CSC/ECE 517 Spring 2021 - E2111. Refactor github metrics integration</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138487"/>
		<updated>2021-03-29T19:52:23Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Lack of Comments / Opaque Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Abstract==&lt;br /&gt;
Github repository and pull request links are submitted as part of many projects in several NC State CSC courses. Instructors would like to compile statistics on these Github links on a per-project, per-team basis, and integrate display of these data within Expertiza (similar to Github Insights). Code was introduced to add this functionality in a [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2018-_Project_E1858._Github_metrics_integration 2018 project, E1858,] which was not merged, and another team re-visited [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_E1983._Refactor_E1858,_Github_metrics_integration_view this project in 2019], which was not merged. Our team aims to tackle the issues we've found that prevented these past team's code from being merged.&lt;br /&gt;
&lt;br /&gt;
==Functionality==&lt;br /&gt;
ADD SECTION INTRODUCING THE FUNCTIONALITY (Description and Maybe one screenshot. Past group went overboard with copy-pasting old screenshots.)&lt;br /&gt;
&lt;br /&gt;
TALK ABOUT OMNIAUTH FOR API AND HOW IT ALL WORKS&lt;br /&gt;
&lt;br /&gt;
==Methods==&lt;br /&gt;
The team has synced the original 2018 project code with the current status of the Expertiza/beta branch and begun analyzing the existing code. Prior customer feedback has advised the team to look for coupling, lack of comments/opaque code, and testing issues. We are performing our own end-to-end analysis of the code in addition to this feedback, and have so far found numerous action items. We have separated these action items into Primary and Secondary objectives: Primary objectives are &amp;quot;must-haves&amp;quot; that need to be addressed before the existing code is mergeable. Secondary objectives are &amp;quot;nice-to-haves,&amp;quot; or features that were either left out of the original project or would make the integration display more complete. &lt;br /&gt;
&lt;br /&gt;
==Primary Objectives (Immediate Action Items)==&lt;br /&gt;
These action items must be addressed before any feature improvements can be attempted. &lt;br /&gt;
&lt;br /&gt;
====API Changes Breaking Functionality====&lt;br /&gt;
The Github API has had minor changes since 2018 which are causing the previously working code to either not display, or throw a NoMethodError. We have already analyzed and applied bugfixes to address these minor API changes, but would like to further refine how the API calls are set-up to decouple our code from the API and make it more robust.&lt;br /&gt;
&lt;br /&gt;
ADD CODEBLOCKS TALKING ABOUT THE TWO BUGFIXES&lt;br /&gt;
&lt;br /&gt;
====Coupling to the Grades Controller====&lt;br /&gt;
CONTINUED&lt;br /&gt;
&lt;br /&gt;
====Views Contain Large Javascript Blocks====&lt;br /&gt;
The views in view.html.erb and view_github_metrics.html.erb in app/views/grades has large JavaScript blocks that were added for the Github metrics functionality. We will move those JavaScript blocks to helper files view_grades_helper.js and view_team_in_grades.js in app/assets/javascipts. The view_team_in_grades.js file is already created with some JavaScript functions that the view_github_metrics.html.erb file already uses, but we will create a new helper file for view.html.erb because it does not currently have any helper files associated with it.&lt;br /&gt;
&lt;br /&gt;
====Methods Out-of-Order====&lt;br /&gt;
CHANGE TO CALLING ORDER OR BETTER RE_ORG&lt;br /&gt;
&lt;br /&gt;
====Lack of Comments / Opaque Code====&lt;br /&gt;
The majority of methods added in E1858 are lacking comments describing their purpose and functionality and comments were not modified for any modified methods.&amp;lt;br&amp;gt;&lt;br /&gt;
Comments will be added to the following files&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/auth_controller.rb: New methods oauth_login and github_login need comments, existing method action_allowed? needs a comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/controllers/grade_controller.rb: Large amount of new methods need comments, existing method &amp;quot;view&amp;quot; needs modification to existing comment&lt;br /&gt;
&amp;lt;li&amp;gt;app/helpers/grades_helper.rb: The methods display_github_metrics, chart_options, and graph_scales need comments describing functionality&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/assignments/list_submissions.html.erb: New GitHub Metrics column added to table&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_tabbing.html.erb: Class &amp;quot;tmr_tab&amp;quot; will have to be xplained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/_teammate_reviews_tab.html.erb: Class &amp;quot;github&amp;quot; will have to be explained&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view.html.erb: JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;app/views/grades/view_github_metrics.html.erb : JavaScript functionality to be moved to a helper .js file and then commented.&lt;br /&gt;
&amp;lt;li&amp;gt;spec/controllers/grades_controller_spec.rb: Comments will have to be added describing the RSpec tests including their purpose and how they achieve their tests&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Testing====&lt;br /&gt;
SHORT COMMENTARY ON COUPLED TESTING TO GRADES/INSTRUCTOR VIEW. SEE TEST PLAN FOR AT_LENGTH DISCUSSION.&lt;br /&gt;
&lt;br /&gt;
==Secondary Objectives==&lt;br /&gt;
The team would like to address one or more of these secondary objectives which were not addressed by prior teams, as long as they do not break existing functionality. These are listed in order of importance. &lt;br /&gt;
&lt;br /&gt;
====Data Permanence (Model)====&lt;br /&gt;
The existing code polls Github every time the data is displayed. This makes the rendering code unnecessarily complex and introduces the potential for future data loss. For performance reasons, decoupling, and the benefit of future developers and data permanence, an improvement to this method would be creating a model and database table to hold these data, so that the display is querying the local DB instead of querying out to Github each time the display is rendered. The team would like to extend the proposed new controller to include a new model that will take this data, and only insert the diff into the database. One point of discussion among the team and our mentor will be, should an update query be run each time the page is displayed, or should a burst of queries be run at the expiration of a deadline to populate the database table? Another option may be to create a manual &amp;quot;Github Update&amp;quot; link, but reject all data after a deadline. Yet another option would be to keep all Github data and use the &amp;quot;late data&amp;quot; to call out  number of late submissions on the statistics page.&lt;br /&gt;
&lt;br /&gt;
====New Feature: Hide Expertiza Development Team====&lt;br /&gt;
At this time, if a team merges in many old commits by the Expertiza Development team, the bar graph statistics are thrown off by including the development team (&amp;quot;Winbobob&amp;quot; et al). If the data permanence objective is met, creating more complex queries should be easier, and the team would like to explore adding a feature to the Github integration display to &amp;quot;hide known contributors,&amp;quot; or similar. This would hide Github data from any user included on the &amp;quot;blacklist&amp;quot; which can be manually curated with the short list of Expertiza developers. While this would not address edge cases where an Expertiza developer later participates in a course using Expertiza, this edge case should be quite rare.&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
The 2018 project wrote numerous tests, several of which are deeply coupled into the grades_controller tests. The team proposes several changes to the testing strategy.&lt;br /&gt;
&lt;br /&gt;
====Decouple Tests / Pass Builds====&lt;br /&gt;
Builds never passed for this project because, in the current code, a user must be logged in and authorized to Github to render grades#view, and this broke several (expected) test cases. As part of decoupling Github from the grades controller, we expect to decouple and revert tests for the grades controller such that the tests become independent, permitting the build to pass if our Github controller tests pass as expected.&lt;br /&gt;
&lt;br /&gt;
====Detailed Analysis of Github Testing====&lt;br /&gt;
The previous team wrote numerous intricate tests for the new functionality. Our team intends to go through these tests line-by-line and ensure that having so many tests is necessary, and the most elegant way to address the issue. We believe that some of the Github refactoring should move methods to become private, which may mean that specific tests for that method shouldn't be used. Alternately, particularly for API authentication which can be tricky, it may be prudent to have discrete tests. Finally, we want to ensure that the tests are actually covering the code -- we are receiving a TravisCI warning about tests without expectations, which needs to be resolved before the code can be merged.&lt;br /&gt;
&lt;br /&gt;
====New Tests for Secondary Objectives====&lt;br /&gt;
If a new model is created along with associated database tables, new tests for this model will need to be written first in keeping with TDD principles.&lt;br /&gt;
&lt;br /&gt;
==Resources==&lt;br /&gt;
ADD PERTINENT LINKS&lt;br /&gt;
&lt;br /&gt;
==Important Data Structure==&lt;br /&gt;
Example of function get_pull_request_details return value(id field in node is omitted for the saving of space):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
{&amp;quot;data&amp;quot;=&amp;gt;{&amp;quot;repository&amp;quot;=&amp;gt;{&amp;quot;pullRequest&amp;quot;=&amp;gt;{&amp;quot;number&amp;quot;=&amp;gt;23281,&lt;br /&gt;
                                         &amp;quot;additions&amp;quot;=&amp;gt;183,&lt;br /&gt;
                                         &amp;quot;deletions&amp;quot;=&amp;gt;119,&lt;br /&gt;
                                         &amp;quot;changedFiles&amp;quot;=&amp;gt;13,&lt;br /&gt;
                                         &amp;quot;mergeable&amp;quot;=&amp;gt;&amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
                                         &amp;quot;merged&amp;quot;=&amp;gt;true,&lt;br /&gt;
                                         &amp;quot;headRefOid&amp;quot;=&amp;gt;&amp;quot;58f027468c8908160196c34681ff5d224a84d75c&amp;quot;,&lt;br /&gt;
                                         &amp;quot;commits&amp;quot;=&amp;gt;{&amp;quot;totalCount&amp;quot;=&amp;gt;2,&lt;br /&gt;
                                                     &amp;quot;pageInfo&amp;quot;=&amp;gt;{&amp;quot;hasNextPage&amp;quot;=&amp;gt;false,&lt;br /&gt;
                                                                  &amp;quot;startCursor&amp;quot;=&amp;gt;&amp;quot;MQ&amp;quot;,&lt;br /&gt;
                                                                  &amp;quot;endCursor&amp;quot;=&amp;gt;&amp;quot;Mg&amp;quot;&lt;br /&gt;
                                                                  },&lt;br /&gt;
                                                     &amp;quot;edges&amp;quot;=&amp;gt;[{&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                         &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Manish Goregaokar&amp;quot;&lt;br /&gt;
                                                                                              },&lt;br /&gt;
                                                                                    &amp;quot;additions&amp;quot;=&amp;gt;10, &amp;quot;deletions&amp;quot;=&amp;gt;10,&lt;br /&gt;
                                                                                    &amp;quot;changedFiles&amp;quot;=&amp;gt;1,&lt;br /&gt;
                                                                                    &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T02:44:08Z&amp;quot;&lt;br /&gt;
                                                                                   }&lt;br /&gt;
                                                                         }&lt;br /&gt;
                                                                },&lt;br /&gt;
                                                                {&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                          &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Maria Sable&amp;quot;},&lt;br /&gt;
                                                                                     &amp;quot;additions&amp;quot;=&amp;gt;173,&lt;br /&gt;
                                                                                     &amp;quot;deletions&amp;quot;=&amp;gt;109,&lt;br /&gt;
                                                                                     &amp;quot;changedFiles&amp;quot;=&amp;gt;12,&lt;br /&gt;
                                                                                     &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T23:54:52Z&amp;quot;&lt;br /&gt;
                                                                                     }&lt;br /&gt;
                                                                          }&lt;br /&gt;
                                                                 }&lt;br /&gt;
                                                                ]&lt;br /&gt;
                                                     }&lt;br /&gt;
                                         }&lt;br /&gt;
                         }&lt;br /&gt;
          }&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138486</id>
		<title>CSC/ECE 517 Spring 2021 - E2111. Refactor github metrics integration</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2111._Refactor_github_metrics_integration&amp;diff=138486"/>
		<updated>2021-03-29T19:35:55Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Views Contain Large Javascript Blocks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Abstract==&lt;br /&gt;
Github repository and pull request links are submitted as part of many projects in several NC State CSC courses. Instructors would like to compile statistics on these Github links on a per-project, per-team basis, and integrate display of these data within Expertiza (similar to Github Insights). Code was introduced to add this functionality in a [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2018-_Project_E1858._Github_metrics_integration 2018 project, E1858,] which was not merged, and another team re-visited [https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Fall_2019_-_E1983._Refactor_E1858,_Github_metrics_integration_view this project in 2019], which was not merged. Our team aims to tackle the issues we've found that prevented these past team's code from being merged.&lt;br /&gt;
&lt;br /&gt;
==Functionality==&lt;br /&gt;
ADD SECTION INTRODUCING THE FUNCTIONALITY (Description and Maybe one screenshot. Past group went overboard with copy-pasting old screenshots.)&lt;br /&gt;
&lt;br /&gt;
TALK ABOUT OMNIAUTH FOR API AND HOW IT ALL WORKS&lt;br /&gt;
&lt;br /&gt;
==Methods==&lt;br /&gt;
The team has synced the original 2018 project code with the current status of the Expertiza/beta branch and begun analyzing the existing code. Prior customer feedback has advised the team to look for coupling, lack of comments/opaque code, and testing issues. We are performing our own end-to-end analysis of the code in addition to this feedback, and have so far found numerous action items. We have separated these action items into Primary and Secondary objectives: Primary objectives are &amp;quot;must-haves&amp;quot; that need to be addressed before the existing code is mergeable. Secondary objectives are &amp;quot;nice-to-haves,&amp;quot; or features that were either left out of the original project or would make the integration display more complete. &lt;br /&gt;
&lt;br /&gt;
==Primary Objectives (Immediate Action Items)==&lt;br /&gt;
These action items must be addressed before any feature improvements can be attempted. &lt;br /&gt;
&lt;br /&gt;
====API Changes Breaking Functionality====&lt;br /&gt;
The Github API has had minor changes since 2018 which are causing the previously working code to either not display, or throw a NoMethodError. We have already analyzed and applied bugfixes to address these minor API changes, but would like to further refine how the API calls are set-up to decouple our code from the API and make it more robust.&lt;br /&gt;
&lt;br /&gt;
ADD CODEBLOCKS TALKING ABOUT THE TWO BUGFIXES&lt;br /&gt;
&lt;br /&gt;
====Coupling to the Grades Controller====&lt;br /&gt;
CONTINUED&lt;br /&gt;
&lt;br /&gt;
====Views Contain Large Javascript Blocks====&lt;br /&gt;
The views in view.html.erb and view_github_metrics.html.erb in app/views/grades has large JavaScript blocks that were added for the Github metrics functionality. We will move those JavaScript blocks to helper files view_grades_helper.js and view_team_in_grades.js in app/assets/javascipts. The view_team_in_grades.js file is already created with some JavaScript functions that the view_github_metrics.html.erb file already uses, but we will create a new helper file for view.html.erb because it does not currently have any helper files associated with it.&lt;br /&gt;
&lt;br /&gt;
====Methods Out-of-Order====&lt;br /&gt;
CHANGE TO CALLING ORDER OR BETTER RE_ORG&lt;br /&gt;
&lt;br /&gt;
====Lack of Comments / Opaque Code====&lt;br /&gt;
CONTINUED&lt;br /&gt;
&lt;br /&gt;
====Testing====&lt;br /&gt;
SHORT COMMENTARY ON COUPLED TESTING TO GRADES/INSTRUCTOR VIEW. SEE TEST PLAN FOR AT_LENGTH DISCUSSION.&lt;br /&gt;
&lt;br /&gt;
==Secondary Objectives==&lt;br /&gt;
The team would like to address one or more of these secondary objectives which were not addressed by prior teams, as long as they do not break existing functionality. These are listed in order of importance. &lt;br /&gt;
&lt;br /&gt;
====Data Permanence (Model)====&lt;br /&gt;
The existing code polls Github every time the data is displayed. This makes the rendering code unnecessarily complex and introduces the potential for future data loss. For performance reasons, decoupling, and the benefit of future developers and data permanence, an improvement to this method would be creating a model and database table to hold these data, so that the display is querying the local DB instead of querying out to Github each time the display is rendered. The team would like to extend the proposed new controller to include a new model that will take this data, and only insert the diff into the database. One point of discussion among the team and our mentor will be, should an update query be run each time the page is displayed, or should a burst of queries be run at the expiration of a deadline to populate the database table? Another option may be to create a manual &amp;quot;Github Update&amp;quot; link, but reject all data after a deadline. Yet another option would be to keep all Github data and use the &amp;quot;late data&amp;quot; to call out  number of late submissions on the statistics page.&lt;br /&gt;
&lt;br /&gt;
====New Feature: Hide Expertiza Development Team====&lt;br /&gt;
At this time, if a team merges in many old commits by the Expertiza Development team, the bar graph statistics are thrown off by including the development team (&amp;quot;Winbobob&amp;quot; et al). If the data permanence objective is met, creating more complex queries should be easier, and the team would like to explore adding a feature to the Github integration display to &amp;quot;hide known contributors,&amp;quot; or similar. This would hide Github data from any user included on the &amp;quot;blacklist&amp;quot; which can be manually curated with the short list of Expertiza developers. While this would not address edge cases where an Expertiza developer later participates in a course using Expertiza, this edge case should be quite rare.&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
The 2018 project wrote numerous tests, several of which are deeply coupled into the grades_controller tests. The team proposes several changes to the testing strategy.&lt;br /&gt;
&lt;br /&gt;
====Decouple Tests / Pass Builds====&lt;br /&gt;
Builds never passed for this project because, in the current code, a user must be logged in and authorized to Github to render grades#view, and this broke several (expected) test cases. As part of decoupling Github from the grades controller, we expect to decouple and revert tests for the grades controller such that the tests become independent, permitting the build to pass if our Github controller tests pass as expected.&lt;br /&gt;
&lt;br /&gt;
====Detailed Analysis of Github Testing====&lt;br /&gt;
The previous team wrote numerous intricate tests for the new functionality. Our team intends to go through these tests line-by-line and ensure that having so many tests is necessary, and the most elegant way to address the issue. We believe that some of the Github refactoring should move methods to become private, which may mean that specific tests for that method shouldn't be used. Alternately, particularly for API authentication which can be tricky, it may be prudent to have discrete tests. Finally, we want to ensure that the tests are actually covering the code -- we are receiving a TravisCI warning about tests without expectations, which needs to be resolved before the code can be merged.&lt;br /&gt;
&lt;br /&gt;
====New Tests for Secondary Objectives====&lt;br /&gt;
If a new model is created along with associated database tables, new tests for this model will need to be written first in keeping with TDD principles.&lt;br /&gt;
&lt;br /&gt;
==Resources==&lt;br /&gt;
ADD PERTINENT LINKS&lt;br /&gt;
&lt;br /&gt;
==Important Data Structure==&lt;br /&gt;
Example of function get_pull_request_details return value(id field in node is omitted for the saving of space):&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
{&amp;quot;data&amp;quot;=&amp;gt;{&amp;quot;repository&amp;quot;=&amp;gt;{&amp;quot;pullRequest&amp;quot;=&amp;gt;{&amp;quot;number&amp;quot;=&amp;gt;23281,&lt;br /&gt;
                                         &amp;quot;additions&amp;quot;=&amp;gt;183,&lt;br /&gt;
                                         &amp;quot;deletions&amp;quot;=&amp;gt;119,&lt;br /&gt;
                                         &amp;quot;changedFiles&amp;quot;=&amp;gt;13,&lt;br /&gt;
                                         &amp;quot;mergeable&amp;quot;=&amp;gt;&amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
                                         &amp;quot;merged&amp;quot;=&amp;gt;true,&lt;br /&gt;
                                         &amp;quot;headRefOid&amp;quot;=&amp;gt;&amp;quot;58f027468c8908160196c34681ff5d224a84d75c&amp;quot;,&lt;br /&gt;
                                         &amp;quot;commits&amp;quot;=&amp;gt;{&amp;quot;totalCount&amp;quot;=&amp;gt;2,&lt;br /&gt;
                                                     &amp;quot;pageInfo&amp;quot;=&amp;gt;{&amp;quot;hasNextPage&amp;quot;=&amp;gt;false,&lt;br /&gt;
                                                                  &amp;quot;startCursor&amp;quot;=&amp;gt;&amp;quot;MQ&amp;quot;,&lt;br /&gt;
                                                                  &amp;quot;endCursor&amp;quot;=&amp;gt;&amp;quot;Mg&amp;quot;&lt;br /&gt;
                                                                  },&lt;br /&gt;
                                                     &amp;quot;edges&amp;quot;=&amp;gt;[{&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                         &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Manish Goregaokar&amp;quot;&lt;br /&gt;
                                                                                              },&lt;br /&gt;
                                                                                    &amp;quot;additions&amp;quot;=&amp;gt;10, &amp;quot;deletions&amp;quot;=&amp;gt;10,&lt;br /&gt;
                                                                                    &amp;quot;changedFiles&amp;quot;=&amp;gt;1,&lt;br /&gt;
                                                                                    &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T02:44:08Z&amp;quot;&lt;br /&gt;
                                                                                   }&lt;br /&gt;
                                                                         }&lt;br /&gt;
                                                                },&lt;br /&gt;
                                                                {&amp;quot;node&amp;quot;=&amp;gt;{&amp;quot;id&amp;quot;=&amp;gt;&amp;quot;MDE3OlB1b...&amp;quot;,&lt;br /&gt;
                                                                          &amp;quot;commit&amp;quot;=&amp;gt;{&amp;quot;author&amp;quot;=&amp;gt;{&amp;quot;name&amp;quot;=&amp;gt;&amp;quot;Maria Sable&amp;quot;},&lt;br /&gt;
                                                                                     &amp;quot;additions&amp;quot;=&amp;gt;173,&lt;br /&gt;
                                                                                     &amp;quot;deletions&amp;quot;=&amp;gt;109,&lt;br /&gt;
                                                                                     &amp;quot;changedFiles&amp;quot;=&amp;gt;12,&lt;br /&gt;
                                                                                     &amp;quot;committedDate&amp;quot;=&amp;gt;&amp;quot;2019-04-30T23:54:52Z&amp;quot;&lt;br /&gt;
                                                                                     }&lt;br /&gt;
                                                                          }&lt;br /&gt;
                                                                 }&lt;br /&gt;
                                                                ]&lt;br /&gt;
                                                     }&lt;br /&gt;
                                         }&lt;br /&gt;
                         }&lt;br /&gt;
          }&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138356</id>
		<title>CSC/ECE 517 Spring 2021 - E2100. Tagging report for students</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138356"/>
		<updated>2021-03-25T14:37:42Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: Added Important Links section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page details project documentation for the Spring 2021, &amp;quot;E2100 Tagging report for students&amp;quot; project, which aims to assist students with finding, and completing, incomplete &amp;quot;review tags&amp;quot; on an assignment using a dynamically generated heatgrid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to Expertiza==&lt;br /&gt;
[http://http://expertiza.ncsu.edu/ Expertiza] is an [http://en.wikipedia.org/wiki/Open-source_software Open Source] [http://rubyonrails.org/ Rails] application which is used by instructors and students for creating assignments and submitting peer reviews.  Expertiza allows the instructor to create and customize assignments, create a list of topics the students can sign up for, have students work on teams and then review each other's assignments at the end. The Source code of the application can be cloned from [https://github.com/expertiza/expertiza/ Github].&lt;br /&gt;
&lt;br /&gt;
==About Review Tagging==&lt;br /&gt;
Review &amp;quot;tags&amp;quot; are a form of feedback on Expertiza where students &amp;quot;tag&amp;quot; (classify) text from peer reviews, using Yes or No answers to questions chosen specifically for each tag deployment. The parameters can include helpfulness, positivity, suggestions, whether a review offered mitigation, and other parameters depending on the Answer Tag Deployment, and the number of tags per deployment is arbitrary. These labeled data are then made available to Expertiza researchers for use in developing Natural Language Processing (NLP) / Machine Learning (ML) algorithms. Tagging is only collected for reviews where the reviewer wrote enough words to be useful as labeled data for NLP research. &lt;br /&gt;
&lt;br /&gt;
[[File:tagDeploymentExample.png|700px]]&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
It can be difficult for students to find a tag they missed on the Team View page, and other teams are working with ML algorithms to &amp;quot;pre-tag&amp;quot; as many reviews as possible, leading to a granular field of completed/incomplete tags. For example, an assignment with two rounds of reviewing, ten questions per review, twelve reviews, and a 5-parameter tag deployment could contain as few as zero or as many as one thousand, two hundred tag prompts for a single student to complete. &lt;br /&gt;
&lt;br /&gt;
At this time, the only tagging feedback students see are the tag prompts and a Javascript counter with a numeric representation of how many tags have not been completed. In order to find a missed tag, students have to scroll the page and manually search for tags that aren't done.&lt;br /&gt;
&lt;br /&gt;
In order to help students complete all the tags for an assignment, we propose a new, dynamically generated heatgrid on the &amp;quot;Your Scores&amp;quot; view that breaks down reviews and tags by round, question, and review, which uses visual cues to help students find incomplete tags. The heatgrid shows both a total count of &amp;quot;complete&amp;quot; out of &amp;quot;available&amp;quot; tags, and individual tags with color-scaled feedback for completeness. &lt;br /&gt;
&lt;br /&gt;
'''The new heatgrid must handle these requirements, and display visual feedback accordingly:'''&lt;br /&gt;
* Single and multiple round reviews&lt;br /&gt;
* Reviews with and without tag prompts&lt;br /&gt;
* Dynamically update as tags are completed&lt;br /&gt;
* Different tag deployments will use different numbers of tags per review&lt;br /&gt;
* Use of existing review row-sort functionality&lt;br /&gt;
* Not rely on the database for tag data, as AnswerTag database rows may not exist until tags are clicked.&lt;br /&gt;
* Give useable feedback for all users (characters as well as color-coding)&lt;br /&gt;
* Only be shown when a tags have been deployed to an assignment&lt;br /&gt;
* Show the total progress of tagging in the format, &amp;quot;249 out of 315&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Our proposed solution is a visual feedback aid, primarily for student users. Tagging data, when input, is stored to the database dynamically using jQuery. For these reasons, and to facilitate dynamic updates, we have chosen to implement this functionality entirely on the client-side of the application using Javascript and jQuery. &lt;br /&gt;
&lt;br /&gt;
===Design Patterns===&lt;br /&gt;
The feedback aid consists of a new heat grid using an HTML table, dynamically generated client-side with Javascript, that uses jQuery to extract the requisite data from the page. The design of this functionality utilizes ''Adapter'' and ''Singleton'' design patterns. ''Singleton'' is present in this design because there shall only ever exist a single tagging heatgrid in the application. While the heatgrid contains an aggregation of other objects, there shall not be multiple heatgrids for multiple rounds or reviews competing for space on the page. ''Adapter'' is used in building the code which adapts from the existing backend, where Tag objects are not created until they are populated by the NLP algorithm or clicked by a student, to the heatgrid which must be able to find and display all questions and tags, regardless of their status or backend implementation.&lt;br /&gt;
&lt;br /&gt;
===Flow Diagram===&lt;br /&gt;
The presence or absence of tag prompts are detected at page load, and the heat grid is rendered as appropriate. The flowchart below details the new Javascript functions which were implemented to accomplish this goal, and the associated program flow. &lt;br /&gt;
&lt;br /&gt;
'''Your Scores Implementation Flow Diagram'''&lt;br /&gt;
&lt;br /&gt;
[[File:View_teamRevA.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Heat grid for a two-round review'''&lt;br /&gt;
&lt;br /&gt;
[[File:HeatGridDemo.png|1100px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Code Style===&lt;br /&gt;
A note on code style: Because this project utilizes so much Javascript, we tried to keep the code style consistent and true to best practices for both Ruby and Javascript, depending on which language we were working in. Therefore, any code or variables from the Ruby section should use snake_case, and code in the Javascript section should use lowerCamelCase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Server-Side Implementation: &amp;quot;Your Scores&amp;quot; and &amp;quot;Alternate View&amp;quot;===&lt;br /&gt;
Because most of the logic is implemented on the client-side, the server-side implementation is fairly simple. &lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Your Scores&amp;quot; view (/views/grades/view_team.html.erb)====&lt;br /&gt;
We added an empty HTML &amp;lt;&amp;lt;nowiki /&amp;gt;table&amp;gt; tag with id and tag information that is used to generate the heatgrid with Javascript.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_table_tag.png]]&lt;br /&gt;
&lt;br /&gt;
In order to simplify the jQuery which parses the rendered page for tags, we added three numeric and one boolean jQueryable data- fields and a new id field to the td container tag which holds each review text (and the associated tags, if present). We used existing Rails variables to populate these data and id fields. The id is set using &amp;quot;rr&amp;quot; for reviewrow (shortened for brevity) plus the question and review number (j and index). We also created fields for data-round (round number) data-question_num (question number), data-review_num (review number), and data-has_tag (boolean whether tags exist). &lt;br /&gt;
&lt;br /&gt;
Style Note: the &amp;quot;data-&amp;quot; is the key queried by jQuery, so a dash is used. The string after the dash is free for us to choose the style, so we used Ruby style to match the convention on the rest of the application.&lt;br /&gt;
&lt;br /&gt;
[[File:tdRailsTag.png]]&lt;br /&gt;
&lt;br /&gt;
Finally, we added a call inside the &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler to tagActionOnLoad(), which starts the rendering code for the client-side Javascript once the page has loaded and tags (if available) are present for counting. We also added a jQuery of our new heat grid table within the onClick for the &amp;quot;Hide Tags&amp;quot; link to ensure our heatgrid is hidden when tag prompts are hidden.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_javascript.png]]&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Alternate View&amp;quot; (/views/grades/view_my_scores.html.erb)====&lt;br /&gt;
&lt;br /&gt;
We added function calls to countTotalTags() and countTaggedTags() inside the document.Ready() event handler to calculate the total number of tags and the total number of tagged tags on the page that currently being rendered. We also added div HTML element with id tag_stats and render this element with string &amp;quot;Tag Finished: tagged_tags/total_tags&amp;quot;. For example, if there are 370 tags in total on this page and 10 of them has already been tagged, then 10/370 will be displayed. This element will also be updated dynamically when users make tag changes on this page. &lt;br /&gt;
&lt;br /&gt;
[[File:alternate_view_html_code.png|1150px]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Your Scores&amp;quot; (view_team.html.erb)===&lt;br /&gt;
The majority of the logic is implemented on the client side. After the page is loaded and the rest of the ReactJS interface is rendered, a dynamically rendered heatgrid is rendered on the &amp;quot;Your Scores&amp;quot; view. A series of functions included in Javascript asset files &amp;lt;code&amp;gt;view_team_in_grades.js&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;answer_tags.js&amp;lt;/code&amp;gt; are called which render the appropriate element for each view. These functions are described below, organized in calling order.&lt;br /&gt;
&lt;br /&gt;
====Initialization and  Rendering - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnLoad()=====&lt;br /&gt;
Called by &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler, this function first calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt; getTagPrompts()&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, determines if zero exist, and exits if so. If tag prompts exist, it continues to call other functions which query the page for information and render the tag heatgrid.&lt;br /&gt;
[[File:tagActionOnLoad.png]]&lt;br /&gt;
&lt;br /&gt;
=====getTagPrompts()=====&lt;br /&gt;
A simple Javascript query that collects all the tag prompts as an HTMLCollection for use with other function.&lt;br /&gt;
&lt;br /&gt;
[[File:getTagPrompts.png]]&lt;br /&gt;
&lt;br /&gt;
=====calcTagRatio()=====&lt;br /&gt;
A mathematics helper function that uses the tag prompt collection to gather data used in populating the tag heatgrid header (&amp;quot;tag fraction&amp;quot;) which reads, for example, &amp;quot;25 out of 350&amp;quot;. This function gathers all the numeric data, as well as calculating the completion ratio as a decimal and adapting this decimal to a value that can be used to reference heatgrid color classes (c0 grey, c1 red, c2 orange, c3 yellow, c4 light green, c5 dark green). Note that we avoided using the light green class because it's too easily confused with dark green -- meaning &amp;quot;all done&amp;quot; -- so logic in this function ensures we skip straight from c3 yellow @ .9999 completion, to c5 dark green at 1.0000 completion.&lt;br /&gt;
&lt;br /&gt;
[[File:calcTagRatio.png]]&lt;br /&gt;
&lt;br /&gt;
=====getRowData()=====&lt;br /&gt;
This method uses jQuery to retrieve the row containers for all questions in all rounds of this review, whether they contain tag prompts or not. This allows us to populate the heatgrid with 'grey - universal No-symbol' for reviews whose length metric is too low to have a tag prompt associated.&lt;br /&gt;
&lt;br /&gt;
[[File:getRowData.png]]&lt;br /&gt;
&lt;br /&gt;
=====countRounds()=====&lt;br /&gt;
This function parses the rows of the review to determine how many rounds of reviews exist in the assignment. This allows us to intelligently print &amp;quot;Round 2 -- Question 3&amp;quot; for multi round reviews, or &amp;quot;Question 3&amp;quot; for single round reviews.&lt;br /&gt;
&lt;br /&gt;
[[File:countRounds.png]]&lt;br /&gt;
&lt;br /&gt;
=====getGridWidth()=====&lt;br /&gt;
This function parses the row data to determine how many tag prompts were used in this deployment using a simple selection search for the largest number of tags in a review. Since different tag deployments use different numbers of prompts, the rendering of the heatgrid needs to address this flexibility. Furthermore, the design of this function will allow for future flexibility of tag deployments, i.e., where different reviews in the same assignment or round may have different numbers of tags.&lt;br /&gt;
&lt;br /&gt;
[[File:getGridWidth.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawTagGrid()=====&lt;br /&gt;
This is the master function for the rendering of the tag grid. This function sets up tooltip text, calls countRounds() and getGridWidth() and passes control to the three sub-functions, drawHeader(), drawQuestionRow(), and drawReviewRow(), depending on what type of item needs to be rendered next.&lt;br /&gt;
&lt;br /&gt;
[[File:drawTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawHeader()=====&lt;br /&gt;
This function draws the two-row header of the  tag heatgrid which will contain &amp;quot;Tags Completed&amp;quot; and &amp;quot;15 out of 425&amp;quot;. It also calls addToolTip() to add the tooltips for expand/contract, and the color legend for the tag fraction row. &lt;br /&gt;
&lt;br /&gt;
[[File:drawHeader.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawQuestionRow()=====&lt;br /&gt;
This function is called once per question during the rendering process, and draws the row which will read, &amp;quot;Question 3&amp;quot; or &amp;quot;Round 2 -- Question 3&amp;quot;. This also calls addToolTip() to add a color legend tooltip for the body of the heatgrid.&lt;br /&gt;
&lt;br /&gt;
[[File:drawQuestionRow.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawReviewRow()=====&lt;br /&gt;
This function draws each row containing the presence, or absence of, tag prompts for each review. These contain R.# (Review.number), and a unicode symbol (set in the global variables). The unicode symbol is to enable accessibility for users who cannot see the red/green spectrum clearly, and will be populated with either a Universal No to mean no tags available, a Warning symbol, to mean tags not done, or a Check-Mark to indicate tags done.&lt;br /&gt;
&lt;br /&gt;
[[File:drawReviewRow.png]]&lt;br /&gt;
&lt;br /&gt;
Lastly, a call is made to updateTagsFraction() as part of the initial render to update the &amp;quot;12 out of 230&amp;quot; header row in the tag grid.&lt;br /&gt;
&lt;br /&gt;
====Updating and Interaction Functions - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnUpdate()=====&lt;br /&gt;
Each time a tag prompt is changed, this function is called to update the heatgrid. This function calls several other functions, some of which have been used previously it the rendering step.&lt;br /&gt;
&lt;br /&gt;
[[File:tagActionOnUpdate.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagsFraction()=====&lt;br /&gt;
This function is used both once in the rendering step, and again each time a tag prompt is changed. This function takes the data from calcTagRatio() and updates the contents and color of the header row in the tag heatgrid (&amp;quot;12 out of 220&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagsFraction.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagGrid()=====&lt;br /&gt;
This function queries the tag prompts on the page, and uses these data to update the tag heatgrid class and text.&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====toggleHeatGridRows()=====&lt;br /&gt;
This function &amp;quot;Collapses&amp;quot; the heatgrid down to only the two header rows. Note that this function is called both onClick of the heatgrid header, so a user can collapse the grid manually, and automatically when the tag fraction is updated. This ensures that a user visiting the page with all tags complete will see a collapsed heatgrid by default.&lt;br /&gt;
&lt;br /&gt;
[[File:toggleHeatGridRows.png]]&lt;br /&gt;
&lt;br /&gt;
====Stylesheets - grades.scss====&lt;br /&gt;
We augmented the stylesheets used in grades so that the new heatgrid could be styled as small as possible to save page real estate. To do so, we added new classes in grades.scss&lt;br /&gt;
&lt;br /&gt;
[[File:stylesheetsGrades_scss.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Alternate View&amp;quot; (view_my_scores.html.erb)===&lt;br /&gt;
=====countTotalTags()=====&lt;br /&gt;
A simple JS function to count all the tag elements on current page. It will be invoked when the page first being rendered and also when any tags are changed by users.&lt;br /&gt;
[[File:countTotalTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====countTaggedTags()=====&lt;br /&gt;
A simple JS function to count the number of tagged tags on the current page. It will be invoked when the page first being rendered and when any tags are changed by users. &lt;br /&gt;
[[File:countTaggedTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====Additional Logic Check=====&lt;br /&gt;
This JS helper dynamically render the page when any tags are changed by users. Since we adopt different logic on two view pages, we added a condition check to tell which page we are on and apply the corresponding logic.&lt;br /&gt;
[[File:answer_tag.png]]&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
===Functional Testing===&lt;br /&gt;
Since we did not make any changes to the models, controllers, or the database backend of Expertiza, we did construct any new RSpec tests for any model or controller behavior.&lt;br /&gt;
&lt;br /&gt;
We did perform functional testing using Capybara, to ensure that our changes to the Embedded Ruby views work and are preserved in future modifications of the code. &lt;br /&gt;
&lt;br /&gt;
Our new functional test was placed within spec/features. The file is named view_team_spec.rb and its purpose is to ensure that the table for the heatgrid is generated when accessing the &amp;quot;Your Scores&amp;quot; section. We built this new test using some code from pre-existing feature tests, namely peer_review_spec.rb, which tests the functionality of user access and leaving a review for another project.&lt;br /&gt;
&lt;br /&gt;
You can run our created feature test with the following command in the expertiza directory. &amp;lt;br&amp;gt;&lt;br /&gt;
''rspec spec/features/view_team_spec.rb''&lt;br /&gt;
&lt;br /&gt;
Expected Output:&lt;br /&gt;
1 example, 0 failures&lt;br /&gt;
&lt;br /&gt;
====Capybara Test Code====&lt;br /&gt;
The following section of code sets up the tests with factory methods located in spec/factories/factories.rb. It creates a User First and Second who are located on Team 1, a User Third who is located on Team 2. It also maps the third user to review team 1 and the first and second users to review team 2.&lt;br /&gt;
  ''before(:each) do&lt;br /&gt;
    create(:assignment, name: &amp;quot;TestAssignment&amp;quot;, directory_path: 'test_assignment')&lt;br /&gt;
    create_list(:participant, 3)&lt;br /&gt;
    create(:assignment_node)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;submission&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;review&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;metareview&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;drop_topic&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;signup&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;team_formation&amp;quot;)&lt;br /&gt;
    create(:deadline_right)&lt;br /&gt;
    create(:deadline_right, name: 'Late')&lt;br /&gt;
    create(:deadline_right, name: 'OK')&lt;br /&gt;
    create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)&lt;br /&gt;
    create(:topic)&lt;br /&gt;
    create(:topic, topic_name: &amp;quot;TestReview&amp;quot;)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).first)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).second)&lt;br /&gt;
    create(:assignment_team)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).third, team: AssignmentTeam.second)&lt;br /&gt;
    create(:signed_up_team)&lt;br /&gt;
    create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)&lt;br /&gt;
    create(:assignment_questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:submission_record)&lt;br /&gt;
    create(:submission_record, team_id: 2)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).third.id)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).first.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).second.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_grade, review_graded_at: Time.now.in_time_zone)&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The following section of code executes the common functionality used in the test of logging in as the third user and then navigating to their &amp;quot;Your Scores&amp;quot; page.&lt;br /&gt;
  ''def load_your_scores&lt;br /&gt;
    login_as(User.where(role_id:1).third.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).third.name&lt;br /&gt;
    click_link &amp;quot;Assignments&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Alternate View&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Summary Report for assignment: TestAssignment'&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The next section of code is the add_review method, which logs in as the first user and adds a review to team 2 which the third user is a part of. This section of code was heavy influenced by &amp;quot;peer_review_spec.rb&amp;quot;&lt;br /&gt;
  ''def add_review&lt;br /&gt;
    login_as(User.where(role_id:1).first.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).first.name&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Reviews for &amp;quot;TestAssignment&amp;quot;'&lt;br /&gt;
    choose &amp;quot;topic_id&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Request a new submission to review&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Begin&amp;quot;&lt;br /&gt;
    fill_in &amp;quot;responses[0][comment]&amp;quot;, with: &amp;quot;HelloWorld&amp;quot;&lt;br /&gt;
    select 3, from: &amp;quot;responses[0][score]&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Submit Review&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your response was successfully saved.&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The final section of our test is the actual feature test that checks the heatmap. This section of the code utilizes both of the above defined &amp;quot;load_your_scores&amp;quot; and &amp;quot;add_review&amp;quot; methods to accomplish the task of viewing the heatmap for the third User.&lt;br /&gt;
  ''it &amp;quot;Should contain html target and javascript calls for tag heatgrid&amp;quot; do&lt;br /&gt;
    # Load Summary Report with no reviews&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    expect(page).to have_content &amp;quot;Average peer review score: &amp;quot;&lt;br /&gt;
    # Add review as first user&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
    add_review&lt;br /&gt;
    # View Your Scores with one review&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    # Check for target to build new heatgrid onto&lt;br /&gt;
    expect(page.body).to include &amp;lt;nowiki&amp;gt;'&amp;lt;table id=&amp;quot;tag_heat_grid&amp;quot; class=&amp;quot;tag_heat_grid&amp;quot;&amp;gt;&amp;lt;/table&amp;gt;'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    # Check for Javascript action to generate the heatgrid&lt;br /&gt;
    expect(page.body).to include 'tagActionOnLoad();'&lt;br /&gt;
    # Check for Javascript action to turn the heatgrid on and off with answer tag toggle&lt;br /&gt;
    expect(page.body).to include &amp;quot;$('.tag_heat_grid').toggle();&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Acceptance Testing===&lt;br /&gt;
For manual testing purposes, we logged in as instructor6 in order to:&lt;br /&gt;
* View the tagging report as an instructor/TA (Instructor tagging is permitted, and tags are separate from Student Tags) on '''view_team'''&lt;br /&gt;
* Impersonate a student to view the tagging report on '''view_team''' as a student&lt;br /&gt;
* View the mini-report on '''Alternate View''' both as students and instructors&lt;br /&gt;
* Ensure that changing of tags updates the Tagging Report&lt;br /&gt;
* Ensure that the Tagging Report interacts well with the existing layout&lt;br /&gt;
* Ensure that the Tagging Report is hidden/shown when &amp;quot;Hide Tags&amp;quot; is clicked&lt;br /&gt;
* Ensure that the Tagging Report accordions as expected when clicked&lt;br /&gt;
* Confirm that mouseover events display the expected tooltips, including color scheme for the &amp;quot;10 out of 250&amp;quot; cell as well as color scheme for the main report body, and the Expand/Collapse tooltip&lt;br /&gt;
* Ensure that the Tagging Report is hidden when there are no Tags available for a '''view_team''' page&lt;br /&gt;
* Ensure that the Tagging Report was compatible with different tag deployments containing different numbers of tags&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
A good example of functionality for a project with reviews and one with reviews can both be found with student7856. Program 2 has plenty of reviews that can be tagged and untagged and checked that the heatmap functionality is working. Program 1 does not have any reviews to tag and was checked to ensure that nothing broke due to not having any reviews to tag.&lt;br /&gt;
&lt;br /&gt;
====Manual Acceptance Testing Instructions====&lt;br /&gt;
1. Login to Expertiza using [http://expertiza-tuffy-354839034.us-east-2.elb.amazonaws.com/ production link hosted on Amazon Web Services]. Use user id: '''instructor6''' with password: '''password'''&lt;br /&gt;
&lt;br /&gt;
2. Navigate to an assignment that has a tag deployment. Many recent assignments (2017-present) which have reviews have tag deployments. Click &amp;quot;Assignments&amp;quot;, then the submissions icon.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:clickNav.PNG|1000px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. Both instructors and students can enter tag data. To view the instructor view, tags, and tagging heatgrid, click &amp;quot;Assign Grade&amp;quot;. To impersonate a student, click the Team Member name link.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:clickViews.PNG|1000px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
4. After impersonating a student, select an assignment. We suggest Program 2 or OSS Projects from a CSC/ECE517 section.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:clickStudentView.PNG|1000px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
5. Click &amp;quot;Your Scores&amp;quot; to view the main View Team page which includes most of the functionality, including the tag heatgrid. &amp;lt;br&amp;gt;&lt;br /&gt;
[[File:clickYourScores.PNG|1000px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''6. Items to Check:'''&amp;lt;br&amp;gt;&lt;br /&gt;
a. On the main View Team Page, click a question row and change one or more tag prompts, to ensure the heatgrid and fraction are updated.&amp;lt;br&amp;gt;&lt;br /&gt;
b. Mouseover the fraction and body of the heatgrid to view the two tooltips describing color scheme. &amp;lt;br&amp;gt; &lt;br /&gt;
c. Click &amp;quot;Hide tags/Show Tags&amp;quot; to show and hide all tag-related data.&amp;lt;br&amp;gt;&lt;br /&gt;
d. Click &amp;quot;Toggle Question List&amp;quot; to ensure the layout is working consistently and as expected.&amp;lt;br&amp;gt;&lt;br /&gt;
e. Click the row-sorter for question or average, and change one or more tag prompts to ensure the heatgrid is still updating the correct tags&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:clickSpots.PNG|1000px]] &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
7. To view the &amp;quot;Alternate View&amp;quot;, navigate back and click &amp;quot;Alternate View&amp;quot;. This view should show a small counter near the top of the page with tag completion data.&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:taggingAlternateView.PNG|1000px]]&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
8. To view a different assignment, if you have impersonated a student, in addition to using Browser-Back, you may have to click &amp;quot;Revert&amp;quot; in the impersonate dialogue. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
9.  We suggest now impersonating '''student7856''' and navigating to '''Program 2''', then '''Your Scores''' to view a series of reviews with all tags completed so you can see how the interface behaves. You will find that the tag grid is collapsed at document load when all tags are completed so it is out of the way, and can be expanded by clicking the header.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
10. Finally, we suggest viewing an assignment which has reviews, but does not have a tag deployment assigned, to ensure none of the heatgrid materials related to tagging are displayed. We suggest navigating to this assignment from 2016 to test this functionality:&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:taglessAssignment.PNG|1000px]]&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Additional Modifications -- Bugfix for Average Review Score on View Team==&lt;br /&gt;
We discovered another bug where assignments with multiple rounds of reviewing did not display the average review score for the assignment. These are the screenshots of this bug before and after a patch was applied:&lt;br /&gt;
===Screenshots===&lt;br /&gt;
'''Unpatched Bug: ''' [[File:avgScoreBugUnpatched.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Patch Applied: ''' [[File:avgScoreBugPatched.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
===Bugfix Description===&lt;br /&gt;
We discovered that this bug was due to a change in how scores are calculated, averaged, and stored. Average scores are computed one round of reviews at a time, and for single-round reviews, retrieved using &amp;lt;code&amp;gt;@pscore[:review]&amp;lt;/code&amp;gt;. For multi-round reviews, the scores are stored as &amp;lt;code&amp;gt;@pscore[:review1]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;@pscore[:review2]&amp;lt;/code&amp;gt;. The existing code in view_team.html.erb was only checking for &amp;lt;code&amp;gt;@pscore[:review]&amp;lt;/code&amp;gt;, and printing &amp;quot;There are no reviews for this assignment&amp;quot; if the key, &amp;lt;code&amp;gt;:review&amp;lt;/code&amp;gt; did not have data associated. We added a snippet to test for &amp;lt;code&amp;gt;@pscore[:review1]&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;@pscore[:review2]&amp;lt;/code&amp;gt;, and compute and print the average score across both reviews accordingly. &lt;br /&gt;
&lt;br /&gt;
The team acknowledges that this bugfix is not ideal in implementation, but is an effective short-term fix. A more elegant solution will require significant modifications to how review score models are handled in the backend, which will affect other areas of the application, and needs to be planned more carefully than a one-shot quick fix.&lt;br /&gt;
&lt;br /&gt;
The changes we made to this code are:&lt;br /&gt;
&lt;br /&gt;
[[File:codeAvgScoreBugPatched.png]]&lt;br /&gt;
&lt;br /&gt;
==Important Links==&lt;br /&gt;
E2100 Github repo - https://github.com/smdupor/expertiza&amp;lt;br&amp;gt;&lt;br /&gt;
E2100 Pull Request - https://github.com/expertiza/expertiza/pull/1895&amp;lt;br&amp;gt;&lt;br /&gt;
Files Changed - https://github.com/expertiza/expertiza/pull/1895/files&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138100</id>
		<title>CSC/ECE 517 Spring 2021 - E2100. Tagging report for students</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138100"/>
		<updated>2021-03-19T21:01:25Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page details project documentation for the Spring 2021, &amp;quot;E2100 Tagging report for students&amp;quot; project, which aims to assist students with finding, and completing, incomplete &amp;quot;review tags&amp;quot; on an assignment using a dynamically generated heatgrid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to Expertiza==&lt;br /&gt;
[http://http://expertiza.ncsu.edu/ Expertiza] is an [http://en.wikipedia.org/wiki/Open-source_software Open Source] [http://rubyonrails.org/ Rails] application which is used by instructors and students for creating assignments and submitting peer reviews.  Expertiza allows the instructor to create and customize assignments, create a list of topics the students can sign up for, have students work on teams and then review each other's assignments at the end. The Source code of the application can be cloned from [https://github.com/expertiza/expertiza/ Github].&lt;br /&gt;
&lt;br /&gt;
==About Review Tagging==&lt;br /&gt;
Review &amp;quot;tags&amp;quot; are a form of feedback on Expertiza where students &amp;quot;tag&amp;quot; (classify) text from peer reviews based on parameters specific to each tag deployment. The parameters can include helpfulness, positivity, suggestions, whether a review offered mitigation, and other parameters depending on the Answer Tag Deployment. These labeled data are then made available to Expertiza researchers for use in developing Natural Language Processing (NLP) / Machine Learning (ML) algorithms. Tagging is only collected for reviews where the text is a sufficient length to be useful as labeled data for NLP research. &lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
It can be difficult for students to find a tag they missed on the Team View page, and other teams are working with ML algorithms to &amp;quot;pre-tag&amp;quot; as many reviews as possible, leading to a granular field of completed/incomplete tags. For example, an assignment with two rounds of reviewing, ten questions per review, twelve reviews, and a 5-parameter tag deployment could contain as few as zero or as many as one thousand, two hundred tag prompts for a single student to complete. &lt;br /&gt;
&lt;br /&gt;
At this time, the only tagging feedback students see are the tag prompts and a Javascript counter with a numeric representation of how many tags have not been completed. In order to find a missed tag, students have to scroll the page and manually search for tags that aren't done.&lt;br /&gt;
&lt;br /&gt;
In order to help students complete all the tags for an assignment, we propose a new, dynamically generated heatgrid on the &amp;quot;Your Scores&amp;quot; view that breaks down reviews and tags by round, question, and review, which uses visual cues to help students find incomplete tags. The heatgrid shows both a total count of &amp;quot;complete&amp;quot; out of &amp;quot;available&amp;quot; tags, and individual tags with color-scaled feedback for completeness. &lt;br /&gt;
&lt;br /&gt;
'''The new heatgrid must handle these requirements, and display visual feedback accordingly:'''&lt;br /&gt;
* Single and multiple round reviews&lt;br /&gt;
* Reviews with and without tag prompts&lt;br /&gt;
* Dynamically update as tags are completed&lt;br /&gt;
* Different tag deployments will use different numbers of tags per review&lt;br /&gt;
* Use of existing review row-sort functionality&lt;br /&gt;
* Not rely on the database for tag data, as AnswerTag database rows may not exist until tags are clicked.&lt;br /&gt;
* Give useable feedback for all users (characters as well as color-coding)&lt;br /&gt;
* Only be shown when a tags have been deployed to an assignment&lt;br /&gt;
* Show the total progress of tagging in the format, &amp;quot;249 out of 315&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Our proposed solution is a visual feedback aid which exists purely for student users. Tagging data, when input, is stored to the database dynamically using jQuery. For these reasons, and to facilitate dynamic updates, we have chosen to implement this functionality entirely on the client-side of the application using Javascript and jQuery. The presence or absence of tag prompts are detected at page load, and the heat grid is rendered as appropriate. The flowchart below details the new Javascript functions which were implemented to accomplish this goal, and the associated program flow. &lt;br /&gt;
&lt;br /&gt;
'''Your Scores Flow Diagram'''&lt;br /&gt;
&lt;br /&gt;
[[File:View_teamRevA.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Heat grid for a two-round review'''&lt;br /&gt;
&lt;br /&gt;
[[File:HeatGridDemo.png|1100px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Code Style===&lt;br /&gt;
A note on code style: Because this project utilizes so much Javascript, we tried to keep the code style consistent and true to best practices for both Ruby and Javascript, depending on which language we were working in. Therefore, any code or variables from the Ruby section should use snake_case, and code in the Javascript section should use lowerCamelCase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Server-Side Implementation: &amp;quot;Your Scores&amp;quot; and &amp;quot;Alternate View&amp;quot;===&lt;br /&gt;
Because most of the logic is implemented on the client-side, the server-side implementation is fairly simple. &lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Your Scores&amp;quot; view (/views/grades/view_team.html.erb)====&lt;br /&gt;
We added an empty HTML &amp;lt;&amp;lt;nowiki /&amp;gt;table&amp;gt; tag with id and tag information that is used to generate the heatgrid with Javascript.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_table_tag.png]]&lt;br /&gt;
&lt;br /&gt;
In order to simplify the jQuery which parses the rendered page for tags, we added three numeric and one boolean jQueryable data- fields and a new id field to the td container tag which holds each review text (and the associated tags, if present). We used existing Rails variables to populate these data and id fields. The id is set using &amp;quot;rr&amp;quot; for reviewrow (shortened for brevity) plus the question and review number (j and index). We also created fields for data-round (round number) data-question_num (question number), data-review_num (review number), and data-has_tag (boolean whether tags exist). &lt;br /&gt;
&lt;br /&gt;
A word on style choices: the &amp;quot;data-&amp;quot; is the key queried by jQuery, so a dash is used. The string after the dash is free for us to choose the style, so we used Ruby style to match the convention on the rest of the application.&lt;br /&gt;
&lt;br /&gt;
[[File:tdRailsTag.png]]&lt;br /&gt;
&lt;br /&gt;
Finally, we added a call inside the &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler to tagActionOnLoad(), which starts the rendering code for the client-side Javascript once the page has loaded and tags (if available) are present for counting. We also added a jQuery of our new heat grid table within the onClick for the &amp;quot;Hide Tags&amp;quot; link to ensure our heatgrid is hidden when tag prompts are hidden.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_javascript.png]]&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Alternate View&amp;quot; (/views/grades/view_my_scores.html.erb)====&lt;br /&gt;
&lt;br /&gt;
We added function calls to countTotalTags() and countTaggedTags() inside the document.Ready() event handler to calculate the total number of tags and the total number of tagged tags on the page that currently being rendered. We also added div HTML element with id tag_stats and render this element with string &amp;quot;Tag Finished: tagged_tags/total_tags&amp;quot;. For example, if there are 370 tags in total on this page and 10 of them has already been tagged, then 10/370 will be displayed. This element will also be updated dynamically when users make tag changes on this page. &lt;br /&gt;
&lt;br /&gt;
[[File:alternate_view_html_code.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Your Scores&amp;quot; (view_team.html.erb)===&lt;br /&gt;
The majority of the logic is implemented on the client side. After the page is loaded and the rest of the ReactJS interface is rendered, a dynamically rendered heatgrid is rendered on the &amp;quot;Your Scores&amp;quot; view. A series of functions included in Javascript asset files &amp;lt;code&amp;gt;view_team_in_grades.js&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;answer_tags.js&amp;lt;/code&amp;gt; are called which render the appropriate element for each view. These functions are described below, organized in calling order.&lt;br /&gt;
&lt;br /&gt;
====Initialization and  Rendering - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnLoad()=====&lt;br /&gt;
Called by &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler, this function first calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt; getTagPrompts()&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, determines if zero exist, and exits if so. If tag prompts exist, it continues to call other functions which query the page for information and render the tag heatgrid.&lt;br /&gt;
[[File:tagActionOnLoad.png]]&lt;br /&gt;
&lt;br /&gt;
=====getTagPrompts()=====&lt;br /&gt;
A simple Javascript query that collects all the tag prompts as an HTMLCollection for use with other function.&lt;br /&gt;
&lt;br /&gt;
[[File:getTagPrompts.png]]&lt;br /&gt;
&lt;br /&gt;
=====calcTagRatio()=====&lt;br /&gt;
A mathematics helper function that uses the tag prompt collection to gather data used in populating the tag heatgrid header (&amp;quot;tag fraction&amp;quot;) which reads, for example, &amp;quot;25 out of 350&amp;quot;. This function gathers all the numeric data, as well as calculating the completion ratio as a decimal and adapting this decimal to a value that can be used to reference heatgrid color classes (c0 grey, c1 red, c2 orange, c3 yellow, c4 light green, c5 dark green). Note that we avoided using the light green class because it's too easily confused with dark green -- meaning &amp;quot;all done&amp;quot; -- so logic in this function ensures we skip straight from c3 yellow @ .9999 completion, to c5 dark green at 1.0000 completion.&lt;br /&gt;
&lt;br /&gt;
[[File:calcTagRatio.png]]&lt;br /&gt;
&lt;br /&gt;
=====getRowData()=====&lt;br /&gt;
This method uses jQuery to retrieve the row containers for all questions in all rounds of this review, whether they contain tag prompts or not. This allows us to populate the heatgrid with 'grey - universal No-symbol' for reviews whose length metric is too low to have a tag prompt associated.&lt;br /&gt;
&lt;br /&gt;
[[File:getRowData.png]]&lt;br /&gt;
&lt;br /&gt;
=====countRounds()=====&lt;br /&gt;
This function parses the rows of the review to determine how many rounds of reviews exist in the assignment. This allows us to intelligently print &amp;quot;Round 2 -- Question 3&amp;quot; for multi round reviews, or &amp;quot;Question 3&amp;quot; for single round reviews.&lt;br /&gt;
&lt;br /&gt;
[[File:countRounds.png]]&lt;br /&gt;
&lt;br /&gt;
=====getGridWidth()=====&lt;br /&gt;
This function parses the row data to determine how many tag prompts were used in this deployment using a simple selection search for the largest number of tags in a review. Since different tag deployments use different numbers of prompts, the rendering of the heatgrid needs to address this flexibility. Furthermore, the design of this function will allow for future flexibility of tag deployments, i.e., where different reviews in the same assignment or round may have different numbers of tags.&lt;br /&gt;
&lt;br /&gt;
[[File:getGridWidth.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawTagGrid()=====&lt;br /&gt;
This is the master function for the rendering of the tag grid. This function sets up tooltip text, calls countRounds() and getGridWidth() and passes control to the three sub-functions, drawHeader(), drawQuestionRow(), and drawReviewRow(), depending on what type of item needs to be rendered next.&lt;br /&gt;
&lt;br /&gt;
[[File:drawTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawHeader()=====&lt;br /&gt;
This function draws the two-row header of the  tag heatgrid which will contain &amp;quot;Tags Completed&amp;quot; and &amp;quot;15 out of 425&amp;quot;. It also calls addToolTip() to add the tooltips for expand/contract, and the color legend for the tag fraction row. &lt;br /&gt;
&lt;br /&gt;
[[File:drawHeader.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawQuestionRow()=====&lt;br /&gt;
This function is called once per question during the rendering process, and draws the row which will read, &amp;quot;Question 3&amp;quot; or &amp;quot;Round 2 -- Question 3&amp;quot;. This also calls addToolTip() to add a color legend tooltip for the body of the heatgrid.&lt;br /&gt;
&lt;br /&gt;
[[File:drawQuestionRow.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawReviewRow()=====&lt;br /&gt;
This function draws each row containing the presence, or absence of, tag prompts for each review. These contain R.# (Review.number), and a unicode symbol (set in the global variables). The unicode symbol is to enable accessibility for users who cannot see the red/green spectrum clearly, and will be populated with either a Universal No to mean no tags available, a Warning symbol, to mean tags not done, or a Check-Mark to indicate tags done.&lt;br /&gt;
&lt;br /&gt;
[[File:drawReviewRow.png]]&lt;br /&gt;
&lt;br /&gt;
Lastly, a call is made to updateTagsFraction() as part of the initial render to update the &amp;quot;12 out of 230&amp;quot; header row in the tag grid.&lt;br /&gt;
&lt;br /&gt;
====Updating and Interaction Functions - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnUpdate()=====&lt;br /&gt;
Each time a tag prompt is changed, this function is called to update the heatgrid. This function calls several other functions, some of which have been used previously it the rendering step.&lt;br /&gt;
&lt;br /&gt;
[[File:tagActionOnUpdate.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagsFraction()=====&lt;br /&gt;
This function is used both once in the rendering step, and again each time a tag prompt is changed. This function takes the data from calcTagRatio() and updates the contents and color of the header row in the tag heatgrid (&amp;quot;12 out of 220&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagsFraction.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagGrid()=====&lt;br /&gt;
This function queries the tag prompts on the page, and uses these data to update the tag heatgrid class and text.&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====toggleHeatGridRows()=====&lt;br /&gt;
This function &amp;quot;Collapses&amp;quot; the heatgrid down to only the two header rows. Note that this function is called both onClick of the heatgrid header, so a user can collapse the grid manually, and automatically when the tag fraction is updated. This ensures that a user visiting the page with all tags complete will see a collapsed heatgrid by default.&lt;br /&gt;
&lt;br /&gt;
[[File:toggleHeatGridRows.png]]&lt;br /&gt;
&lt;br /&gt;
====Stylesheets - grades.scss====&lt;br /&gt;
We augmented the stylesheets used in grades so that the new heatgrid could be styled as small as possible to save page real estate. To do so, we added new classes in grades.scss&lt;br /&gt;
&lt;br /&gt;
[[File:stylesheetsGrades_scss.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Alternate View&amp;quot; (view_my_scores.html.erb)===&lt;br /&gt;
=====countTotalTags()=====&lt;br /&gt;
A simple JS function to count all the tag elements on current page. It will be invoked when the page first being rendered and also when any tags are changed by users.&lt;br /&gt;
[[File:countTotalTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====countTaggedTags()=====&lt;br /&gt;
A simple JS function to count the number of tagged tags on the current page. It will be invoked when the page first being rendered and when any tags are changed by users. &lt;br /&gt;
[[File:countTaggedTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====Additional Logic Check=====&lt;br /&gt;
This JS helper dynamically render the page when any tags are changed by users. Since we adopt different logic on two view pages, we added a condition check to tell which page we are on and apply the corresponding logic.&lt;br /&gt;
[[File:answer_tag.png]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
Our modifications were mostly on the front end of the code in the app/views and app/assets sections so we decided to create a spec/features test. Our feature test is named view_team_spec.rb and its purpose is to ensure that the table for the heatgrid is generated when accessing the &amp;quot;Your Scores&amp;quot; section. We were able to accomplish this by mostly drawing off of pre-existing feature tests namely peer_review_spec.rb, which had a user access and review another project.&lt;br /&gt;
&lt;br /&gt;
You can run our created feature test with the following command in the expertiza directory. &amp;lt;br&amp;gt;&lt;br /&gt;
''rspec spec/features/view_team_spec.rb''&lt;br /&gt;
&lt;br /&gt;
Expected Output:&lt;br /&gt;
1 example, 0 failures&lt;br /&gt;
&lt;br /&gt;
Since we did not make any changes to the model or controller classes we did construct any RSpec tests for any model or controller behavior.&lt;br /&gt;
&lt;br /&gt;
===Navigating to Tag Report===&lt;br /&gt;
For manual testing purposes, we logged in as instructor6 and impersonated a student to view the reviews given to that student and be able to tag the reviews and see if the tagged reviews are updated in the heatmap.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
A good example of functionality for a project with reviews and one with reviews can both be found with student7856. Program 2 has plenty of reviews that can be tagged and untagged and checked that the heatmap functionality is working. Program 1 does not have any reviews to tag and was checked to ensure that nothing broke due to not having any reviews to tag.&lt;br /&gt;
&lt;br /&gt;
===Manual Testing Instructions===&lt;br /&gt;
1. Login to Expertiza as &amp;quot;instructor6&amp;quot; with password &amp;quot;password&amp;quot;. If hosted locally the url to access Expertiza should be &amp;quot;localhost:3000&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
2. Click on Manage... then &amp;quot;Impersonate User&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
3. Enter &amp;quot;student7856&amp;quot; into the field and click the Impersonate button&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:ImpersonateStudent.PNG]] &amp;lt;br&amp;gt;&lt;br /&gt;
4. Click on &amp;quot;Program 2&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
5. Click on &amp;quot;Your Scores&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:YourScores.PNG]]&lt;br /&gt;
6. You should be able to click on a review score (1-5) and have the table expand, if you tag or remove a tag from a review, you should see the table on the right hand side of the page update to reflect your changes.&amp;lt;br&amp;gt;&lt;br /&gt;
7. Click Back and then click on &amp;quot;Alternate View&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:AlternateView.PNG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Testing HTML changes using Capybara===&lt;br /&gt;
Our feature file test is located at expertiza/spec/features/view_team_spec.rb and tests adding a review and viewing the &amp;quot;Your Scores&amp;quot; page to see the heatmap of review tags.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following section of code sets up the tests with factory methods located in spec/factories/factories.rb. It creates a User First and Second who are located on Team 1, a User Third who is located on Team 2. It also maps the third user to review team 1 and the first and second users to review team 2.&lt;br /&gt;
  ''before(:each) do&lt;br /&gt;
    create(:assignment, name: &amp;quot;TestAssignment&amp;quot;, directory_path: 'test_assignment')&lt;br /&gt;
    create_list(:participant, 3)&lt;br /&gt;
    create(:assignment_node)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;submission&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;review&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;metareview&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;drop_topic&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;signup&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;team_formation&amp;quot;)&lt;br /&gt;
    create(:deadline_right)&lt;br /&gt;
    create(:deadline_right, name: 'Late')&lt;br /&gt;
    create(:deadline_right, name: 'OK')&lt;br /&gt;
    create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)&lt;br /&gt;
    create(:topic)&lt;br /&gt;
    create(:topic, topic_name: &amp;quot;TestReview&amp;quot;)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).first)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).second)&lt;br /&gt;
    create(:assignment_team)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).third, team: AssignmentTeam.second)&lt;br /&gt;
    create(:signed_up_team)&lt;br /&gt;
    create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)&lt;br /&gt;
    create(:assignment_questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:submission_record)&lt;br /&gt;
    create(:submission_record, team_id: 2)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).third.id)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).first.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).second.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_grade, review_graded_at: Time.now.in_time_zone)&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The following section of code executes the common functionality used in the test of logging in as the third user and then navigating to their &amp;quot;Your Scores&amp;quot; page.&lt;br /&gt;
  ''def load_your_scores&lt;br /&gt;
    login_as(User.where(role_id:1).third.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).third.name&lt;br /&gt;
    click_link &amp;quot;Assignments&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Alternate View&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Summary Report for assignment: TestAssignment'&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The next section of code is the add_review method, which logs in as the first user and adds a review to team 2 which the third user is a part of. This section of code was heavy influenced by &amp;quot;peer_review_spec.rb&amp;quot;&lt;br /&gt;
  ''def add_review&lt;br /&gt;
    login_as(User.where(role_id:1).first.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).first.name&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Reviews for &amp;quot;TestAssignment&amp;quot;'&lt;br /&gt;
    choose &amp;quot;topic_id&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Request a new submission to review&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Begin&amp;quot;&lt;br /&gt;
    fill_in &amp;quot;responses[0][comment]&amp;quot;, with: &amp;quot;HelloWorld&amp;quot;&lt;br /&gt;
    select 3, from: &amp;quot;responses[0][score]&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Submit Review&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your response was successfully saved.&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The final section of our test is the actual feature test that checks the heatmap. This section of the code utilizes both of the above defined &amp;quot;load_your_scores&amp;quot; and &amp;quot;add_review&amp;quot; methods to accomplish the task of viewing the heatmap for the third User.&lt;br /&gt;
  ''it &amp;quot;Should contain html target and javascript calls for tag heatgrid&amp;quot; do&lt;br /&gt;
    # Load Summary Report with no reviews&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    expect(page).to have_content &amp;quot;Average peer review score: &amp;quot;&lt;br /&gt;
    # Add review as first user&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
    add_review&lt;br /&gt;
    # View Your Scores with one review&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    # Check for target to build new heatgrid onto&lt;br /&gt;
    expect(page.body).to include &amp;lt;nowiki&amp;gt;'&amp;lt;table id=&amp;quot;tag_heat_grid&amp;quot; class=&amp;quot;tag_heat_grid&amp;quot;&amp;gt;&amp;lt;/table&amp;gt;'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    # Check for Javascript action to generate the heatgrid&lt;br /&gt;
    expect(page.body).to include 'tagActionOnLoad();'&lt;br /&gt;
    # Check for Javascript action to turn the heatgrid on and off with answer tag toggle&lt;br /&gt;
    expect(page.body).to include &amp;quot;$('.tag_heat_grid').toggle();&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
==Additional Modifications -- Bugfix in Assignment Model and Grades Controller==&lt;br /&gt;
In developing the tagging report, the team noticed a significant, pre-existing view bug on view_team in the beta branch of Expertiza. This bug was causing rows of the review heatgrid to not be populated when the page was rendered. The team is still in communication with the main Expertiza development team to review and verify the validity of these modifications. At the time of this document, all build tests are passing.&lt;br /&gt;
&lt;br /&gt;
'''Screenshot of unpatched bug:   '''&lt;br /&gt;
[[File:unpatched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Screenshot with patch applied: '''&lt;br /&gt;
[[File:patched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
===Origin of Bug===&lt;br /&gt;
We discovered a twofold cause of this bug. In part, in the '''grades controller''', calls were being made to an ActiveRecord query, '''@assignment.vary_by_round,''' which was always returning zero. Upon changing this query to use the helper method, '''varying_rubrics_by_round?''' from the '''Assignment''' model, we discovered that a recent change to the logic of '''varying_rubrics_by_round?''' was also causing unexpected behavior. We have included four modifications to our pull request that patch this bug.&lt;br /&gt;
&lt;br /&gt;
===Patches to Assignment model===&lt;br /&gt;
In the '''varying_rubrics_by_round?''' method in the '''Assignment''' model, we discovered a recent change to the logic that was causing this method to test whether there were 2 or more '''AssignmentQuestionnaires''' assigned to Round 2 of reviews. Instead, this method should test whether there are 2 or more AssignmentQuestionnaires used on the assignment. We changed this logic to query using &amp;lt;code&amp;gt;used_in_round [1,2]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;used_in_round 2&amp;lt;/code&amp;gt;, which should evaluate '''.size &amp;gt; 1 TRUE''' if each round has a different '''AssignmentQuestionnaire'''.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_model_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Patches to Grades Controller===&lt;br /&gt;
In the grades controller, queries were being made to &amp;lt;code&amp;gt;@assignment.vary_by_round&amp;lt;/code&amp;gt; instead of the helper method, &amp;lt;code&amp;gt;@assignment.varying_rubrics_by_round?&amp;lt;/code&amp;gt;. These queries were always returning 0, because that DB field in our DB  appears to be 0 for all table rows. We changed the 4 calls from this query to method calls.&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Assignment Tests===&lt;br /&gt;
We also discovered an issue with how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is tested. It seems, in the unmodified beta branch version, the test that should expect TRUE when rubrics are different for round 1 and 2, is actually testing whether Round 2 has more than 1 rubric assigned to it. We would expect this test to pass when using the old logic in &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt;. We modified the TRUE case to assign AssignmentQuestionnaire 1 to round 2, and AssignmentQuestionnaire 2 to round 1. &lt;br /&gt;
&lt;br /&gt;
Also, it seemed that the test for FALSE was testing using one questionnaire in round 1, and a different questionnaire in round 2, which seems logically incorrect for this method. We believe the false case should test for the same rubric across two rounds, in which case the rubrics would not be varying by round. We modified the FALSE case to assign AssignmentQuestionnaire 1 to both round 1 and round 2.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_spec_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Grades Controller Tests===&lt;br /&gt;
Lastly, the changes to how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is implemented meant that the two tests for the grades controller which rely on this method needed to be updated to expect the new logic. We implemented these updates to these two tests:&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_spec.png]]&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138099</id>
		<title>CSC/ECE 517 Spring 2021 - E2100. Tagging report for students</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138099"/>
		<updated>2021-03-19T20:59:52Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Navigating to Tag Report */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page details project documentation for the Spring 2021, &amp;quot;E2100 Tagging report for students&amp;quot; project, which aims to assist students with finding, and completing, incomplete &amp;quot;review tags&amp;quot; on an assignment using a dynamically generated heatgrid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to Expertiza==&lt;br /&gt;
[http://http://expertiza.ncsu.edu/ Expertiza] is an [http://en.wikipedia.org/wiki/Open-source_software Open Source] [http://rubyonrails.org/ Rails] application which is used by instructors and students for creating assignments and submitting peer reviews.  Expertiza allows the instructor to create and customize assignments, create a list of topics the students can sign up for, have students work on teams and then review each other's assignments at the end. The Source code of the application can be cloned from [https://github.com/expertiza/expertiza/ Github].&lt;br /&gt;
&lt;br /&gt;
==About Review Tagging==&lt;br /&gt;
Review &amp;quot;tags&amp;quot; are a form of feedback on Expertiza where students &amp;quot;tag&amp;quot; (classify) text from peer reviews based on parameters specific to each tag deployment. The parameters can include helpfulness, positivity, suggestions, whether a review offered mitigation, and other parameters depending on the Answer Tag Deployment. These labeled data are then made available to Expertiza researchers for use in developing Natural Language Processing (NLP) / Machine Learning (ML) algorithms. Tagging is only collected for reviews where the text is a sufficient length to be useful as labeled data for NLP research. &lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
It can be difficult for students to find a tag they missed on the Team View page, and other teams are working with ML algorithms to &amp;quot;pre-tag&amp;quot; as many reviews as possible, leading to a granular field of completed/incomplete tags. For example, an assignment with two rounds of reviewing, ten questions per review, twelve reviews, and a 5-parameter tag deployment could contain as few as zero or as many as one thousand, two hundred tag prompts for a single student to complete. &lt;br /&gt;
&lt;br /&gt;
At this time, the only tagging feedback students see are the tag prompts and a Javascript counter with a numeric representation of how many tags have not been completed. In order to find a missed tag, students have to scroll the page and manually search for tags that aren't done.&lt;br /&gt;
&lt;br /&gt;
In order to help students complete all the tags for an assignment, we propose a new, dynamically generated heatgrid on the &amp;quot;Your Scores&amp;quot; view that breaks down reviews and tags by round, question, and review, which uses visual cues to help students find incomplete tags. The heatgrid shows both a total count of &amp;quot;complete&amp;quot; out of &amp;quot;available&amp;quot; tags, and individual tags with color-scaled feedback for completeness. &lt;br /&gt;
&lt;br /&gt;
'''The new heatgrid must handle these requirements, and display visual feedback accordingly:'''&lt;br /&gt;
* Single and multiple round reviews&lt;br /&gt;
* Reviews with and without tag prompts&lt;br /&gt;
* Dynamically update as tags are completed&lt;br /&gt;
* Different tag deployments will use different numbers of tags per review&lt;br /&gt;
* Use of existing review row-sort functionality&lt;br /&gt;
* Not rely on the database for tag data, as AnswerTag database rows may not exist until tags are clicked.&lt;br /&gt;
* Give useable feedback for all users (characters as well as color-coding)&lt;br /&gt;
* Only be shown when a tags have been deployed to an assignment&lt;br /&gt;
* Show the total progress of tagging in the format, &amp;quot;249 out of 315&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Our proposed solution is a visual feedback aid which exists purely for student users. Tagging data, when input, is stored to the database dynamically using jQuery. For these reasons, and to facilitate dynamic updates, we have chosen to implement this functionality entirely on the client-side of the application using Javascript and jQuery. The presence or absence of tag prompts are detected at page load, and the heat grid is rendered as appropriate. The flowchart below details the new Javascript functions which were implemented to accomplish this goal, and the associated program flow. &lt;br /&gt;
&lt;br /&gt;
'''Your Scores Flow Diagram'''&lt;br /&gt;
&lt;br /&gt;
[[File:View_teamRevA.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Heat grid for a two-round review'''&lt;br /&gt;
&lt;br /&gt;
[[File:HeatGridDemo.png|1100px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Code Style===&lt;br /&gt;
A note on code style: Because this project utilizes so much Javascript, we tried to keep the code style consistent and true to best practices for both Ruby and Javascript, depending on which language we were working in. Therefore, any code or variables from the Ruby section should use snake_case, and code in the Javascript section should use lowerCamelCase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Server-Side Implementation: &amp;quot;Your Scores&amp;quot; and &amp;quot;Alternate View&amp;quot;===&lt;br /&gt;
Because most of the logic is implemented on the client-side, the server-side implementation is fairly simple. &lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Your Scores&amp;quot; view (/views/grades/view_team.html.erb)====&lt;br /&gt;
We added an empty HTML &amp;lt;&amp;lt;nowiki /&amp;gt;table&amp;gt; tag with id and tag information that is used to generate the heatgrid with Javascript.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_table_tag.png]]&lt;br /&gt;
&lt;br /&gt;
In order to simplify the jQuery which parses the rendered page for tags, we added three numeric and one boolean jQueryable data- fields and a new id field to the td container tag which holds each review text (and the associated tags, if present). We used existing Rails variables to populate these data and id fields. The id is set using &amp;quot;rr&amp;quot; for reviewrow (shortened for brevity) plus the question and review number (j and index). We also created fields for data-round (round number) data-question_num (question number), data-review_num (review number), and data-has_tag (boolean whether tags exist). &lt;br /&gt;
&lt;br /&gt;
A word on style choices: the &amp;quot;data-&amp;quot; is the key queried by jQuery, so a dash is used. The string after the dash is free for us to choose the style, so we used Ruby style to match the convention on the rest of the application.&lt;br /&gt;
&lt;br /&gt;
[[File:tdRailsTag.png]]&lt;br /&gt;
&lt;br /&gt;
Finally, we added a call inside the &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler to tagActionOnLoad(), which starts the rendering code for the client-side Javascript once the page has loaded and tags (if available) are present for counting. We also added a jQuery of our new heat grid table within the onClick for the &amp;quot;Hide Tags&amp;quot; link to ensure our heatgrid is hidden when tag prompts are hidden.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_javascript.png]]&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Alternate View&amp;quot; (/views/grades/view_my_scores.html.erb)====&lt;br /&gt;
&lt;br /&gt;
We added function calls to countTotalTags() and countTaggedTags() inside the document.Ready() event handler to calculate the total number of tags and the total number of tagged tags on the page that currently being rendered. We also added div HTML element with id tag_stats and render this element with string &amp;quot;Tag Finished: tagged_tags/total_tags&amp;quot;. For example, if there are 370 tags in total on this page and 10 of them has already been tagged, then 10/370 will be displayed. This element will also be updated dynamically when users make tag changes on this page. &lt;br /&gt;
&lt;br /&gt;
[[File:alternate_view_html_code.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Your Scores&amp;quot; (view_team.html.erb)===&lt;br /&gt;
The majority of the logic is implemented on the client side. After the page is loaded and the rest of the ReactJS interface is rendered, a dynamically rendered heatgrid is rendered on the &amp;quot;Your Scores&amp;quot; view. A series of functions included in Javascript asset files &amp;lt;code&amp;gt;view_team_in_grades.js&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;answer_tags.js&amp;lt;/code&amp;gt; are called which render the appropriate element for each view. These functions are described below, organized in calling order.&lt;br /&gt;
&lt;br /&gt;
====Initialization and  Rendering - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnLoad()=====&lt;br /&gt;
Called by &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler, this function first calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt; getTagPrompts()&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, determines if zero exist, and exits if so. If tag prompts exist, it continues to call other functions which query the page for information and render the tag heatgrid.&lt;br /&gt;
[[File:tagActionOnLoad.png]]&lt;br /&gt;
&lt;br /&gt;
=====getTagPrompts()=====&lt;br /&gt;
A simple Javascript query that collects all the tag prompts as an HTMLCollection for use with other function.&lt;br /&gt;
&lt;br /&gt;
[[File:getTagPrompts.png]]&lt;br /&gt;
&lt;br /&gt;
=====calcTagRatio()=====&lt;br /&gt;
A mathematics helper function that uses the tag prompt collection to gather data used in populating the tag heatgrid header (&amp;quot;tag fraction&amp;quot;) which reads, for example, &amp;quot;25 out of 350&amp;quot;. This function gathers all the numeric data, as well as calculating the completion ratio as a decimal and adapting this decimal to a value that can be used to reference heatgrid color classes (c0 grey, c1 red, c2 orange, c3 yellow, c4 light green, c5 dark green). Note that we avoided using the light green class because it's too easily confused with dark green -- meaning &amp;quot;all done&amp;quot; -- so logic in this function ensures we skip straight from c3 yellow @ .9999 completion, to c5 dark green at 1.0000 completion.&lt;br /&gt;
&lt;br /&gt;
[[File:calcTagRatio.png]]&lt;br /&gt;
&lt;br /&gt;
=====getRowData()=====&lt;br /&gt;
This method uses jQuery to retrieve the row containers for all questions in all rounds of this review, whether they contain tag prompts or not. This allows us to populate the heatgrid with 'grey - universal No-symbol' for reviews whose length metric is too low to have a tag prompt associated.&lt;br /&gt;
&lt;br /&gt;
[[File:getRowData.png]]&lt;br /&gt;
&lt;br /&gt;
=====countRounds()=====&lt;br /&gt;
This function parses the rows of the review to determine how many rounds of reviews exist in the assignment. This allows us to intelligently print &amp;quot;Round 2 -- Question 3&amp;quot; for multi round reviews, or &amp;quot;Question 3&amp;quot; for single round reviews.&lt;br /&gt;
&lt;br /&gt;
[[File:countRounds.png]]&lt;br /&gt;
&lt;br /&gt;
=====getGridWidth()=====&lt;br /&gt;
This function parses the row data to determine how many tag prompts were used in this deployment using a simple selection search for the largest number of tags in a review. Since different tag deployments use different numbers of prompts, the rendering of the heatgrid needs to address this flexibility. Furthermore, the design of this function will allow for future flexibility of tag deployments, i.e., where different reviews in the same assignment or round may have different numbers of tags.&lt;br /&gt;
&lt;br /&gt;
[[File:getGridWidth.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawTagGrid()=====&lt;br /&gt;
This is the master function for the rendering of the tag grid. This function sets up tooltip text, calls countRounds() and getGridWidth() and passes control to the three sub-functions, drawHeader(), drawQuestionRow(), and drawReviewRow(), depending on what type of item needs to be rendered next.&lt;br /&gt;
&lt;br /&gt;
[[File:drawTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawHeader()=====&lt;br /&gt;
This function draws the two-row header of the  tag heatgrid which will contain &amp;quot;Tags Completed&amp;quot; and &amp;quot;15 out of 425&amp;quot;. It also calls addToolTip() to add the tooltips for expand/contract, and the color legend for the tag fraction row. &lt;br /&gt;
&lt;br /&gt;
[[File:drawHeader.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawQuestionRow()=====&lt;br /&gt;
This function is called once per question during the rendering process, and draws the row which will read, &amp;quot;Question 3&amp;quot; or &amp;quot;Round 2 -- Question 3&amp;quot;. This also calls addToolTip() to add a color legend tooltip for the body of the heatgrid.&lt;br /&gt;
&lt;br /&gt;
[[File:drawQuestionRow.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawReviewRow()=====&lt;br /&gt;
This function draws each row containing the presence, or absence of, tag prompts for each review. These contain R.# (Review.number), and a unicode symbol (set in the global variables). The unicode symbol is to enable accessibility for users who cannot see the red/green spectrum clearly, and will be populated with either a Universal No to mean no tags available, a Warning symbol, to mean tags not done, or a Check-Mark to indicate tags done.&lt;br /&gt;
&lt;br /&gt;
[[File:drawReviewRow.png]]&lt;br /&gt;
&lt;br /&gt;
Lastly, a call is made to updateTagsFraction() as part of the initial render to update the &amp;quot;12 out of 230&amp;quot; header row in the tag grid.&lt;br /&gt;
&lt;br /&gt;
====Updating and Interaction Functions - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnUpdate()=====&lt;br /&gt;
Each time a tag prompt is changed, this function is called to update the heatgrid. This function calls several other functions, some of which have been used previously it the rendering step.&lt;br /&gt;
&lt;br /&gt;
[[File:tagActionOnUpdate.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagsFraction()=====&lt;br /&gt;
This function is used both once in the rendering step, and again each time a tag prompt is changed. This function takes the data from calcTagRatio() and updates the contents and color of the header row in the tag heatgrid (&amp;quot;12 out of 220&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagsFraction.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagGrid()=====&lt;br /&gt;
This function queries the tag prompts on the page, and uses these data to update the tag heatgrid class and text.&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====toggleHeatGridRows()=====&lt;br /&gt;
This function &amp;quot;Collapses&amp;quot; the heatgrid down to only the two header rows. Note that this function is called both onClick of the heatgrid header, so a user can collapse the grid manually, and automatically when the tag fraction is updated. This ensures that a user visiting the page with all tags complete will see a collapsed heatgrid by default.&lt;br /&gt;
&lt;br /&gt;
[[File:toggleHeatGridRows.png]]&lt;br /&gt;
&lt;br /&gt;
====Stylesheets - grades.scss====&lt;br /&gt;
We augmented the stylesheets used in grades so that the new heatgrid could be styled as small as possible to save page real estate. To do so, we added new classes in grades.scss&lt;br /&gt;
&lt;br /&gt;
[[File:stylesheetsGrades_scss.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Alternate View&amp;quot; (view_my_scores.html.erb)===&lt;br /&gt;
=====countTotalTags()=====&lt;br /&gt;
A simple JS function to count all the tag elements on current page. It will be invoked when the page first being rendered and also when any tags are changed by users.&lt;br /&gt;
[[File:countTotalTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====countTaggedTags()=====&lt;br /&gt;
A simple JS function to count the number of tagged tags on the current page. It will be invoked when the page first being rendered and when any tags are changed by users. &lt;br /&gt;
[[File:countTaggedTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====Additional Logic Check=====&lt;br /&gt;
This JS helper dynamically render the page when any tags are changed by users. Since we adopt different logic on two view pages, we added a condition check to tell which page we are on and apply the corresponding logic.&lt;br /&gt;
[[File:answer_tag.png]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
Our modifications were mostly on the front end of the code in the app/views and app/assets sections so we decided to create a spec/features test. Our feature test is named view_team_spec.rb and its purpose is to ensure that the table for the heatgrid is generated when accessing the &amp;quot;Your Scores&amp;quot; section. We were able to accomplish this by mostly drawing off of pre-existing feature tests namely peer_review_spec.rb, which had a user access and review another project.&lt;br /&gt;
&lt;br /&gt;
You can run our created feature test with the following command in the expertiza directory.&lt;br /&gt;
rspec spec/features/view_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
Expected Output:&lt;br /&gt;
1 example, 0 failures&lt;br /&gt;
&lt;br /&gt;
Since we did not make any changes to the model or controller classes we did construct any RSpec tests for any model or controller behavior.&lt;br /&gt;
&lt;br /&gt;
===Navigating to Tag Report===&lt;br /&gt;
For manual testing purposes, we logged in as instructor6 and impersonated a student to view the reviews given to that student and be able to tag the reviews and see if the tagged reviews are updated in the heatmap.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
A good example of functionality for a project with reviews and one with reviews can both be found with student7856. Program 2 has plenty of reviews that can be tagged and untagged and checked that the heatmap functionality is working. Program 1 does not have any reviews to tag and was checked to ensure that nothing broke due to not having any reviews to tag.&lt;br /&gt;
&lt;br /&gt;
===Manual Testing Instructions===&lt;br /&gt;
1. Login to Expertiza as &amp;quot;instructor6&amp;quot; with password &amp;quot;password&amp;quot;. If hosted locally the url to access Expertiza should be &amp;quot;localhost:3000&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
2. Click on Manage... then &amp;quot;Impersonate User&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
3. Enter &amp;quot;student7856&amp;quot; into the field and click the Impersonate button&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:ImpersonateStudent.PNG]] &amp;lt;br&amp;gt;&lt;br /&gt;
4. Click on &amp;quot;Program 2&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
5. Click on &amp;quot;Your Scores&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:YourScores.PNG]]&lt;br /&gt;
6. You should be able to click on a review score (1-5) and have the table expand, if you tag or remove a tag from a review, you should see the table on the right hand side of the page update to reflect your changes.&amp;lt;br&amp;gt;&lt;br /&gt;
7. Click Back and then click on &amp;quot;Alternate View&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:AlternateView.PNG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Testing HTML changes using Capybara===&lt;br /&gt;
Our feature file test is located at expertiza/spec/features/view_team_spec.rb and tests adding a review and viewing the &amp;quot;Your Scores&amp;quot; page to see the heatmap of review tags.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following section of code sets up the tests with factory methods located in spec/factories/factories.rb. It creates a User First and Second who are located on Team 1, a User Third who is located on Team 2. It also maps the third user to review team 1 and the first and second users to review team 2.&lt;br /&gt;
  ''before(:each) do&lt;br /&gt;
    create(:assignment, name: &amp;quot;TestAssignment&amp;quot;, directory_path: 'test_assignment')&lt;br /&gt;
    create_list(:participant, 3)&lt;br /&gt;
    create(:assignment_node)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;submission&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;review&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;metareview&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;drop_topic&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;signup&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;team_formation&amp;quot;)&lt;br /&gt;
    create(:deadline_right)&lt;br /&gt;
    create(:deadline_right, name: 'Late')&lt;br /&gt;
    create(:deadline_right, name: 'OK')&lt;br /&gt;
    create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)&lt;br /&gt;
    create(:topic)&lt;br /&gt;
    create(:topic, topic_name: &amp;quot;TestReview&amp;quot;)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).first)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).second)&lt;br /&gt;
    create(:assignment_team)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).third, team: AssignmentTeam.second)&lt;br /&gt;
    create(:signed_up_team)&lt;br /&gt;
    create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)&lt;br /&gt;
    create(:assignment_questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:submission_record)&lt;br /&gt;
    create(:submission_record, team_id: 2)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).third.id)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).first.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).second.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_grade, review_graded_at: Time.now.in_time_zone)&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The following section of code executes the common functionality used in the test of logging in as the third user and then navigating to their &amp;quot;Your Scores&amp;quot; page.&lt;br /&gt;
  ''def load_your_scores&lt;br /&gt;
    login_as(User.where(role_id:1).third.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).third.name&lt;br /&gt;
    click_link &amp;quot;Assignments&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Alternate View&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Summary Report for assignment: TestAssignment'&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The next section of code is the add_review method, which logs in as the first user and adds a review to team 2 which the third user is a part of. This section of code was heavy influenced by &amp;quot;peer_review_spec.rb&amp;quot;&lt;br /&gt;
  ''def add_review&lt;br /&gt;
    login_as(User.where(role_id:1).first.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).first.name&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Reviews for &amp;quot;TestAssignment&amp;quot;'&lt;br /&gt;
    choose &amp;quot;topic_id&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Request a new submission to review&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Begin&amp;quot;&lt;br /&gt;
    fill_in &amp;quot;responses[0][comment]&amp;quot;, with: &amp;quot;HelloWorld&amp;quot;&lt;br /&gt;
    select 3, from: &amp;quot;responses[0][score]&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Submit Review&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your response was successfully saved.&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The final section of our test is the actual feature test that checks the heatmap. This section of the code utilizes both of the above defined &amp;quot;load_your_scores&amp;quot; and &amp;quot;add_review&amp;quot; methods to accomplish the task of viewing the heatmap for the third User.&lt;br /&gt;
  ''it &amp;quot;Should contain html target and javascript calls for tag heatgrid&amp;quot; do&lt;br /&gt;
    # Load Summary Report with no reviews&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    expect(page).to have_content &amp;quot;Average peer review score: &amp;quot;&lt;br /&gt;
    # Add review as first user&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
    add_review&lt;br /&gt;
    # View Your Scores with one review&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    # Check for target to build new heatgrid onto&lt;br /&gt;
    expect(page.body).to include &amp;lt;nowiki&amp;gt;'&amp;lt;table id=&amp;quot;tag_heat_grid&amp;quot; class=&amp;quot;tag_heat_grid&amp;quot;&amp;gt;&amp;lt;/table&amp;gt;'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    # Check for Javascript action to generate the heatgrid&lt;br /&gt;
    expect(page.body).to include 'tagActionOnLoad();'&lt;br /&gt;
    # Check for Javascript action to turn the heatgrid on and off with answer tag toggle&lt;br /&gt;
    expect(page.body).to include &amp;quot;$('.tag_heat_grid').toggle();&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
==Additional Modifications -- Bugfix in Assignment Model and Grades Controller==&lt;br /&gt;
In developing the tagging report, the team noticed a significant, pre-existing view bug on view_team in the beta branch of Expertiza. This bug was causing rows of the review heatgrid to not be populated when the page was rendered. The team is still in communication with the main Expertiza development team to review and verify the validity of these modifications. At the time of this document, all build tests are passing.&lt;br /&gt;
&lt;br /&gt;
'''Screenshot of unpatched bug:   '''&lt;br /&gt;
[[File:unpatched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Screenshot with patch applied: '''&lt;br /&gt;
[[File:patched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
===Origin of Bug===&lt;br /&gt;
We discovered a twofold cause of this bug. In part, in the '''grades controller''', calls were being made to an ActiveRecord query, '''@assignment.vary_by_round,''' which was always returning zero. Upon changing this query to use the helper method, '''varying_rubrics_by_round?''' from the '''Assignment''' model, we discovered that a recent change to the logic of '''varying_rubrics_by_round?''' was also causing unexpected behavior. We have included four modifications to our pull request that patch this bug.&lt;br /&gt;
&lt;br /&gt;
===Patches to Assignment model===&lt;br /&gt;
In the '''varying_rubrics_by_round?''' method in the '''Assignment''' model, we discovered a recent change to the logic that was causing this method to test whether there were 2 or more '''AssignmentQuestionnaires''' assigned to Round 2 of reviews. Instead, this method should test whether there are 2 or more AssignmentQuestionnaires used on the assignment. We changed this logic to query using &amp;lt;code&amp;gt;used_in_round [1,2]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;used_in_round 2&amp;lt;/code&amp;gt;, which should evaluate '''.size &amp;gt; 1 TRUE''' if each round has a different '''AssignmentQuestionnaire'''.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_model_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Patches to Grades Controller===&lt;br /&gt;
In the grades controller, queries were being made to &amp;lt;code&amp;gt;@assignment.vary_by_round&amp;lt;/code&amp;gt; instead of the helper method, &amp;lt;code&amp;gt;@assignment.varying_rubrics_by_round?&amp;lt;/code&amp;gt;. These queries were always returning 0, because that DB field in our DB  appears to be 0 for all table rows. We changed the 4 calls from this query to method calls.&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Assignment Tests===&lt;br /&gt;
We also discovered an issue with how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is tested. It seems, in the unmodified beta branch version, the test that should expect TRUE when rubrics are different for round 1 and 2, is actually testing whether Round 2 has more than 1 rubric assigned to it. We would expect this test to pass when using the old logic in &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt;. We modified the TRUE case to assign AssignmentQuestionnaire 1 to round 2, and AssignmentQuestionnaire 2 to round 1. &lt;br /&gt;
&lt;br /&gt;
Also, it seemed that the test for FALSE was testing using one questionnaire in round 1, and a different questionnaire in round 2, which seems logically incorrect for this method. We believe the false case should test for the same rubric across two rounds, in which case the rubrics would not be varying by round. We modified the FALSE case to assign AssignmentQuestionnaire 1 to both round 1 and round 2.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_spec_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Grades Controller Tests===&lt;br /&gt;
Lastly, the changes to how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is implemented meant that the two tests for the grades controller which rely on this method needed to be updated to expect the new logic. We implemented these updates to these two tests:&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_spec.png]]&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138098</id>
		<title>CSC/ECE 517 Spring 2021 - E2100. Tagging report for students</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138098"/>
		<updated>2021-03-19T20:56:00Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Testing HTML changes using Capybara */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page details project documentation for the Spring 2021, &amp;quot;E2100 Tagging report for students&amp;quot; project, which aims to assist students with finding, and completing, incomplete &amp;quot;review tags&amp;quot; on an assignment using a dynamically generated heatgrid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to Expertiza==&lt;br /&gt;
[http://http://expertiza.ncsu.edu/ Expertiza] is an [http://en.wikipedia.org/wiki/Open-source_software Open Source] [http://rubyonrails.org/ Rails] application which is used by instructors and students for creating assignments and submitting peer reviews.  Expertiza allows the instructor to create and customize assignments, create a list of topics the students can sign up for, have students work on teams and then review each other's assignments at the end. The Source code of the application can be cloned from [https://github.com/expertiza/expertiza/ Github].&lt;br /&gt;
&lt;br /&gt;
==About Review Tagging==&lt;br /&gt;
Review &amp;quot;tags&amp;quot; are a form of feedback on Expertiza where students &amp;quot;tag&amp;quot; (classify) text from peer reviews based on parameters specific to each tag deployment. The parameters can include helpfulness, positivity, suggestions, whether a review offered mitigation, and other parameters depending on the Answer Tag Deployment. These labeled data are then made available to Expertiza researchers for use in developing Natural Language Processing (NLP) / Machine Learning (ML) algorithms. Tagging is only collected for reviews where the text is a sufficient length to be useful as labeled data for NLP research. &lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
It can be difficult for students to find a tag they missed on the Team View page, and other teams are working with ML algorithms to &amp;quot;pre-tag&amp;quot; as many reviews as possible, leading to a granular field of completed/incomplete tags. For example, an assignment with two rounds of reviewing, ten questions per review, twelve reviews, and a 5-parameter tag deployment could contain as few as zero or as many as one thousand, two hundred tag prompts for a single student to complete. &lt;br /&gt;
&lt;br /&gt;
At this time, the only tagging feedback students see are the tag prompts and a Javascript counter with a numeric representation of how many tags have not been completed. In order to find a missed tag, students have to scroll the page and manually search for tags that aren't done.&lt;br /&gt;
&lt;br /&gt;
In order to help students complete all the tags for an assignment, we propose a new, dynamically generated heatgrid on the &amp;quot;Your Scores&amp;quot; view that breaks down reviews and tags by round, question, and review, which uses visual cues to help students find incomplete tags. The heatgrid shows both a total count of &amp;quot;complete&amp;quot; out of &amp;quot;available&amp;quot; tags, and individual tags with color-scaled feedback for completeness. &lt;br /&gt;
&lt;br /&gt;
'''The new heatgrid must handle these requirements, and display visual feedback accordingly:'''&lt;br /&gt;
* Single and multiple round reviews&lt;br /&gt;
* Reviews with and without tag prompts&lt;br /&gt;
* Dynamically update as tags are completed&lt;br /&gt;
* Different tag deployments will use different numbers of tags per review&lt;br /&gt;
* Use of existing review row-sort functionality&lt;br /&gt;
* Not rely on the database for tag data, as AnswerTag database rows may not exist until tags are clicked.&lt;br /&gt;
* Give useable feedback for all users (characters as well as color-coding)&lt;br /&gt;
* Only be shown when a tags have been deployed to an assignment&lt;br /&gt;
* Show the total progress of tagging in the format, &amp;quot;249 out of 315&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Our proposed solution is a visual feedback aid which exists purely for student users. Tagging data, when input, is stored to the database dynamically using jQuery. For these reasons, and to facilitate dynamic updates, we have chosen to implement this functionality entirely on the client-side of the application using Javascript and jQuery. The presence or absence of tag prompts are detected at page load, and the heat grid is rendered as appropriate. The flowchart below details the new Javascript functions which were implemented to accomplish this goal, and the associated program flow. &lt;br /&gt;
&lt;br /&gt;
'''Your Scores Flow Diagram'''&lt;br /&gt;
&lt;br /&gt;
[[File:View_teamRevA.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Heat grid for a two-round review'''&lt;br /&gt;
&lt;br /&gt;
[[File:HeatGridDemo.png|1100px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Code Style===&lt;br /&gt;
A note on code style: Because this project utilizes so much Javascript, we tried to keep the code style consistent and true to best practices for both Ruby and Javascript, depending on which language we were working in. Therefore, any code or variables from the Ruby section should use snake_case, and code in the Javascript section should use lowerCamelCase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Server-Side Implementation: &amp;quot;Your Scores&amp;quot; and &amp;quot;Alternate View&amp;quot;===&lt;br /&gt;
Because most of the logic is implemented on the client-side, the server-side implementation is fairly simple. &lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Your Scores&amp;quot; view (/views/grades/view_team.html.erb)====&lt;br /&gt;
We added an empty HTML &amp;lt;&amp;lt;nowiki /&amp;gt;table&amp;gt; tag with id and tag information that is used to generate the heatgrid with Javascript.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_table_tag.png]]&lt;br /&gt;
&lt;br /&gt;
In order to simplify the jQuery which parses the rendered page for tags, we added three numeric and one boolean jQueryable data- fields and a new id field to the td container tag which holds each review text (and the associated tags, if present). We used existing Rails variables to populate these data and id fields. The id is set using &amp;quot;rr&amp;quot; for reviewrow (shortened for brevity) plus the question and review number (j and index). We also created fields for data-round (round number) data-question_num (question number), data-review_num (review number), and data-has_tag (boolean whether tags exist). &lt;br /&gt;
&lt;br /&gt;
A word on style choices: the &amp;quot;data-&amp;quot; is the key queried by jQuery, so a dash is used. The string after the dash is free for us to choose the style, so we used Ruby style to match the convention on the rest of the application.&lt;br /&gt;
&lt;br /&gt;
[[File:tdRailsTag.png]]&lt;br /&gt;
&lt;br /&gt;
Finally, we added a call inside the &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler to tagActionOnLoad(), which starts the rendering code for the client-side Javascript once the page has loaded and tags (if available) are present for counting. We also added a jQuery of our new heat grid table within the onClick for the &amp;quot;Hide Tags&amp;quot; link to ensure our heatgrid is hidden when tag prompts are hidden.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_javascript.png]]&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Alternate View&amp;quot; (/views/grades/view_my_scores.html.erb)====&lt;br /&gt;
&lt;br /&gt;
We added function calls to countTotalTags() and countTaggedTags() inside the document.Ready() event handler to calculate the total number of tags and the total number of tagged tags on the page that currently being rendered. We also added div HTML element with id tag_stats and render this element with string &amp;quot;Tag Finished: tagged_tags/total_tags&amp;quot;. For example, if there are 370 tags in total on this page and 10 of them has already been tagged, then 10/370 will be displayed. This element will also be updated dynamically when users make tag changes on this page. &lt;br /&gt;
&lt;br /&gt;
[[File:alternate_view_html_code.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Your Scores&amp;quot; (view_team.html.erb)===&lt;br /&gt;
The majority of the logic is implemented on the client side. After the page is loaded and the rest of the ReactJS interface is rendered, a dynamically rendered heatgrid is rendered on the &amp;quot;Your Scores&amp;quot; view. A series of functions included in Javascript asset files &amp;lt;code&amp;gt;view_team_in_grades.js&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;answer_tags.js&amp;lt;/code&amp;gt; are called which render the appropriate element for each view. These functions are described below, organized in calling order.&lt;br /&gt;
&lt;br /&gt;
====Initialization and  Rendering - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnLoad()=====&lt;br /&gt;
Called by &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler, this function first calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt; getTagPrompts()&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, determines if zero exist, and exits if so. If tag prompts exist, it continues to call other functions which query the page for information and render the tag heatgrid.&lt;br /&gt;
[[File:tagActionOnLoad.png]]&lt;br /&gt;
&lt;br /&gt;
=====getTagPrompts()=====&lt;br /&gt;
A simple Javascript query that collects all the tag prompts as an HTMLCollection for use with other function.&lt;br /&gt;
&lt;br /&gt;
[[File:getTagPrompts.png]]&lt;br /&gt;
&lt;br /&gt;
=====calcTagRatio()=====&lt;br /&gt;
A mathematics helper function that uses the tag prompt collection to gather data used in populating the tag heatgrid header (&amp;quot;tag fraction&amp;quot;) which reads, for example, &amp;quot;25 out of 350&amp;quot;. This function gathers all the numeric data, as well as calculating the completion ratio as a decimal and adapting this decimal to a value that can be used to reference heatgrid color classes (c0 grey, c1 red, c2 orange, c3 yellow, c4 light green, c5 dark green). Note that we avoided using the light green class because it's too easily confused with dark green -- meaning &amp;quot;all done&amp;quot; -- so logic in this function ensures we skip straight from c3 yellow @ .9999 completion, to c5 dark green at 1.0000 completion.&lt;br /&gt;
&lt;br /&gt;
[[File:calcTagRatio.png]]&lt;br /&gt;
&lt;br /&gt;
=====getRowData()=====&lt;br /&gt;
This method uses jQuery to retrieve the row containers for all questions in all rounds of this review, whether they contain tag prompts or not. This allows us to populate the heatgrid with 'grey - universal No-symbol' for reviews whose length metric is too low to have a tag prompt associated.&lt;br /&gt;
&lt;br /&gt;
[[File:getRowData.png]]&lt;br /&gt;
&lt;br /&gt;
=====countRounds()=====&lt;br /&gt;
This function parses the rows of the review to determine how many rounds of reviews exist in the assignment. This allows us to intelligently print &amp;quot;Round 2 -- Question 3&amp;quot; for multi round reviews, or &amp;quot;Question 3&amp;quot; for single round reviews.&lt;br /&gt;
&lt;br /&gt;
[[File:countRounds.png]]&lt;br /&gt;
&lt;br /&gt;
=====getGridWidth()=====&lt;br /&gt;
This function parses the row data to determine how many tag prompts were used in this deployment using a simple selection search for the largest number of tags in a review. Since different tag deployments use different numbers of prompts, the rendering of the heatgrid needs to address this flexibility. Furthermore, the design of this function will allow for future flexibility of tag deployments, i.e., where different reviews in the same assignment or round may have different numbers of tags.&lt;br /&gt;
&lt;br /&gt;
[[File:getGridWidth.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawTagGrid()=====&lt;br /&gt;
This is the master function for the rendering of the tag grid. This function sets up tooltip text, calls countRounds() and getGridWidth() and passes control to the three sub-functions, drawHeader(), drawQuestionRow(), and drawReviewRow(), depending on what type of item needs to be rendered next.&lt;br /&gt;
&lt;br /&gt;
[[File:drawTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawHeader()=====&lt;br /&gt;
This function draws the two-row header of the  tag heatgrid which will contain &amp;quot;Tags Completed&amp;quot; and &amp;quot;15 out of 425&amp;quot;. It also calls addToolTip() to add the tooltips for expand/contract, and the color legend for the tag fraction row. &lt;br /&gt;
&lt;br /&gt;
[[File:drawHeader.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawQuestionRow()=====&lt;br /&gt;
This function is called once per question during the rendering process, and draws the row which will read, &amp;quot;Question 3&amp;quot; or &amp;quot;Round 2 -- Question 3&amp;quot;. This also calls addToolTip() to add a color legend tooltip for the body of the heatgrid.&lt;br /&gt;
&lt;br /&gt;
[[File:drawQuestionRow.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawReviewRow()=====&lt;br /&gt;
This function draws each row containing the presence, or absence of, tag prompts for each review. These contain R.# (Review.number), and a unicode symbol (set in the global variables). The unicode symbol is to enable accessibility for users who cannot see the red/green spectrum clearly, and will be populated with either a Universal No to mean no tags available, a Warning symbol, to mean tags not done, or a Check-Mark to indicate tags done.&lt;br /&gt;
&lt;br /&gt;
[[File:drawReviewRow.png]]&lt;br /&gt;
&lt;br /&gt;
Lastly, a call is made to updateTagsFraction() as part of the initial render to update the &amp;quot;12 out of 230&amp;quot; header row in the tag grid.&lt;br /&gt;
&lt;br /&gt;
====Updating and Interaction Functions - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnUpdate()=====&lt;br /&gt;
Each time a tag prompt is changed, this function is called to update the heatgrid. This function calls several other functions, some of which have been used previously it the rendering step.&lt;br /&gt;
&lt;br /&gt;
[[File:tagActionOnUpdate.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagsFraction()=====&lt;br /&gt;
This function is used both once in the rendering step, and again each time a tag prompt is changed. This function takes the data from calcTagRatio() and updates the contents and color of the header row in the tag heatgrid (&amp;quot;12 out of 220&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagsFraction.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagGrid()=====&lt;br /&gt;
This function queries the tag prompts on the page, and uses these data to update the tag heatgrid class and text.&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====toggleHeatGridRows()=====&lt;br /&gt;
This function &amp;quot;Collapses&amp;quot; the heatgrid down to only the two header rows. Note that this function is called both onClick of the heatgrid header, so a user can collapse the grid manually, and automatically when the tag fraction is updated. This ensures that a user visiting the page with all tags complete will see a collapsed heatgrid by default.&lt;br /&gt;
&lt;br /&gt;
[[File:toggleHeatGridRows.png]]&lt;br /&gt;
&lt;br /&gt;
====Stylesheets - grades.scss====&lt;br /&gt;
We augmented the stylesheets used in grades so that the new heatgrid could be styled as small as possible to save page real estate. To do so, we added new classes in grades.scss&lt;br /&gt;
&lt;br /&gt;
[[File:stylesheetsGrades_scss.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Alternate View&amp;quot; (view_my_scores.html.erb)===&lt;br /&gt;
=====countTotalTags()=====&lt;br /&gt;
A simple JS function to count all the tag elements on current page. It will be invoked when the page first being rendered and also when any tags are changed by users.&lt;br /&gt;
[[File:countTotalTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====countTaggedTags()=====&lt;br /&gt;
A simple JS function to count the number of tagged tags on the current page. It will be invoked when the page first being rendered and when any tags are changed by users. &lt;br /&gt;
[[File:countTaggedTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====Additional Logic Check=====&lt;br /&gt;
This JS helper dynamically render the page when any tags are changed by users. Since we adopt different logic on two view pages, we added a condition check to tell which page we are on and apply the corresponding logic.&lt;br /&gt;
[[File:answer_tag.png]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
Our modifications were mostly on the front end of the code in the app/views and app/assets sections so we decided to create a spec/features test. Our feature test is named view_team_spec.rb and its purpose is to ensure that the table for the heatgrid is generated when accessing the &amp;quot;Your Scores&amp;quot; section. We were able to accomplish this by mostly drawing off of pre-existing feature tests namely peer_review_spec.rb, which had a user access and review another project.&lt;br /&gt;
&lt;br /&gt;
You can run our created feature test with the following command in the expertiza directory.&lt;br /&gt;
rspec spec/features/view_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
Expected Output:&lt;br /&gt;
1 example, 0 failures&lt;br /&gt;
&lt;br /&gt;
Since we did not make any changes to the model or controller classes we did construct any RSpec tests for any model or controller behavior.&lt;br /&gt;
&lt;br /&gt;
===Navigating to Tag Report===&lt;br /&gt;
For manual testing purposes, we logged in as instructor6 and impersonated a student to view the reviews given to that student and be able to tag the reviews and see if the tagged reviews are updated in the heatmap.&lt;br /&gt;
&lt;br /&gt;
===Manual Testing Instructions===&lt;br /&gt;
1. Login to Expertiza as &amp;quot;instructor6&amp;quot; with password &amp;quot;password&amp;quot;. If hosted locally the url to access Expertiza should be &amp;quot;localhost:3000&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
2. Click on Manage... then &amp;quot;Impersonate User&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
3. Enter &amp;quot;student7856&amp;quot; into the field and click the Impersonate button&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:ImpersonateStudent.PNG]] &amp;lt;br&amp;gt;&lt;br /&gt;
4. Click on &amp;quot;Program 2&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
5. Click on &amp;quot;Your Scores&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:YourScores.PNG]]&lt;br /&gt;
6. You should be able to click on a review score (1-5) and have the table expand, if you tag or remove a tag from a review, you should see the table on the right hand side of the page update to reflect your changes.&amp;lt;br&amp;gt;&lt;br /&gt;
7. Click Back and then click on &amp;quot;Alternate View&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:AlternateView.PNG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Testing HTML changes using Capybara===&lt;br /&gt;
Our feature file test is located at expertiza/spec/features/view_team_spec.rb and tests adding a review and viewing the &amp;quot;Your Scores&amp;quot; page to see the heatmap of review tags.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following section of code sets up the tests with factory methods located in spec/factories/factories.rb. It creates a User First and Second who are located on Team 1, a User Third who is located on Team 2. It also maps the third user to review team 1 and the first and second users to review team 2.&lt;br /&gt;
  ''before(:each) do&lt;br /&gt;
    create(:assignment, name: &amp;quot;TestAssignment&amp;quot;, directory_path: 'test_assignment')&lt;br /&gt;
    create_list(:participant, 3)&lt;br /&gt;
    create(:assignment_node)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;submission&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;review&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;metareview&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;drop_topic&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;signup&amp;quot;)&lt;br /&gt;
    create(:deadline_type, name: &amp;quot;team_formation&amp;quot;)&lt;br /&gt;
    create(:deadline_right)&lt;br /&gt;
    create(:deadline_right, name: 'Late')&lt;br /&gt;
    create(:deadline_right, name: 'OK')&lt;br /&gt;
    create(:assignment_due_date, deadline_type: DeadlineType.where(name: 'review').first, due_at: Time.now.in_time_zone + 1.day)&lt;br /&gt;
    create(:topic)&lt;br /&gt;
    create(:topic, topic_name: &amp;quot;TestReview&amp;quot;)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).first)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).second)&lt;br /&gt;
    create(:assignment_team)&lt;br /&gt;
    create(:team_user, user: User.where(role_id: 1).third, team: AssignmentTeam.second)&lt;br /&gt;
    create(:signed_up_team)&lt;br /&gt;
    create(:signed_up_team, team_id: 2, topic: SignUpTopic.second)&lt;br /&gt;
    create(:assignment_questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:submission_record)&lt;br /&gt;
    create(:submission_record, team_id: 2)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).third.id)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).first.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_response_map, reviewer_id: User.where(role_id: 1).second.id, reviewee: AssignmentTeam.second)&lt;br /&gt;
    create(:review_grade, review_graded_at: Time.now.in_time_zone)&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The following section of code executes the common functionality used in the test of logging in as the third user and then navigating to their &amp;quot;Your Scores&amp;quot; page.&lt;br /&gt;
  ''def load_your_scores&lt;br /&gt;
    login_as(User.where(role_id:1).third.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).third.name&lt;br /&gt;
    click_link &amp;quot;Assignments&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Alternate View&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Your scores&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Summary Report for assignment: TestAssignment'&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The next section of code is the add_review method, which logs in as the first user and adds a review to team 2 which the third user is a part of. This section of code was heavy influenced by &amp;quot;peer_review_spec.rb&amp;quot;&lt;br /&gt;
  ''def add_review&lt;br /&gt;
    login_as(User.where(role_id:1).first.name)&lt;br /&gt;
    expect(page).to have_content &amp;quot;User: &amp;quot; + User.where(role_id:1).first.name&lt;br /&gt;
    expect(page).to have_content &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    click_link &amp;quot;TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Submit or Review work for TestAssignment&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Others' work&amp;quot;&lt;br /&gt;
    expect(page).to have_content 'Reviews for &amp;quot;TestAssignment&amp;quot;'&lt;br /&gt;
    choose &amp;quot;topic_id&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Request a new submission to review&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Begin&amp;quot;&lt;br /&gt;
    fill_in &amp;quot;responses[0][comment]&amp;quot;, with: &amp;quot;HelloWorld&amp;quot;&lt;br /&gt;
    select 3, from: &amp;quot;responses[0][score]&amp;quot;&lt;br /&gt;
    click_button &amp;quot;Submit Review&amp;quot;&lt;br /&gt;
    expect(page).to have_content &amp;quot;Your response was successfully saved.&amp;quot;&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
The final section of our test is the actual feature test that checks the heatmap. This section of the code utilizes both of the above defined &amp;quot;load_your_scores&amp;quot; and &amp;quot;add_review&amp;quot; methods to accomplish the task of viewing the heatmap for the third User.&lt;br /&gt;
  ''it &amp;quot;Should contain html target and javascript calls for tag heatgrid&amp;quot; do&lt;br /&gt;
    # Load Summary Report with no reviews&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    expect(page).to have_content &amp;quot;Average peer review score: &amp;quot;&lt;br /&gt;
    # Add review as first user&lt;br /&gt;
    click_link &amp;quot;Logout&amp;quot;&lt;br /&gt;
    add_review&lt;br /&gt;
    # View Your Scores with one review&lt;br /&gt;
    load_your_scores&lt;br /&gt;
    # Check for target to build new heatgrid onto&lt;br /&gt;
    expect(page.body).to include &amp;lt;nowiki&amp;gt;'&amp;lt;table id=&amp;quot;tag_heat_grid&amp;quot; class=&amp;quot;tag_heat_grid&amp;quot;&amp;gt;&amp;lt;/table&amp;gt;'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
    # Check for Javascript action to generate the heatgrid&lt;br /&gt;
    expect(page.body).to include 'tagActionOnLoad();'&lt;br /&gt;
    # Check for Javascript action to turn the heatgrid on and off with answer tag toggle&lt;br /&gt;
    expect(page.body).to include &amp;quot;$('.tag_heat_grid').toggle();&amp;quot;&lt;br /&gt;
  end''&lt;br /&gt;
&lt;br /&gt;
==Additional Modifications -- Bugfix in Assignment Model and Grades Controller==&lt;br /&gt;
In developing the tagging report, the team noticed a significant, pre-existing view bug on view_team in the beta branch of Expertiza. This bug was causing rows of the review heatgrid to not be populated when the page was rendered. The team is still in communication with the main Expertiza development team to review and verify the validity of these modifications. At the time of this document, all build tests are passing.&lt;br /&gt;
&lt;br /&gt;
'''Screenshot of unpatched bug:   '''&lt;br /&gt;
[[File:unpatched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Screenshot with patch applied: '''&lt;br /&gt;
[[File:patched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
===Origin of Bug===&lt;br /&gt;
We discovered a twofold cause of this bug. In part, in the '''grades controller''', calls were being made to an ActiveRecord query, '''@assignment.vary_by_round,''' which was always returning zero. Upon changing this query to use the helper method, '''varying_rubrics_by_round?''' from the '''Assignment''' model, we discovered that a recent change to the logic of '''varying_rubrics_by_round?''' was also causing unexpected behavior. We have included four modifications to our pull request that patch this bug.&lt;br /&gt;
&lt;br /&gt;
===Patches to Assignment model===&lt;br /&gt;
In the '''varying_rubrics_by_round?''' method in the '''Assignment''' model, we discovered a recent change to the logic that was causing this method to test whether there were 2 or more '''AssignmentQuestionnaires''' assigned to Round 2 of reviews. Instead, this method should test whether there are 2 or more AssignmentQuestionnaires used on the assignment. We changed this logic to query using &amp;lt;code&amp;gt;used_in_round [1,2]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;used_in_round 2&amp;lt;/code&amp;gt;, which should evaluate '''.size &amp;gt; 1 TRUE''' if each round has a different '''AssignmentQuestionnaire'''.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_model_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Patches to Grades Controller===&lt;br /&gt;
In the grades controller, queries were being made to &amp;lt;code&amp;gt;@assignment.vary_by_round&amp;lt;/code&amp;gt; instead of the helper method, &amp;lt;code&amp;gt;@assignment.varying_rubrics_by_round?&amp;lt;/code&amp;gt;. These queries were always returning 0, because that DB field in our DB  appears to be 0 for all table rows. We changed the 4 calls from this query to method calls.&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Assignment Tests===&lt;br /&gt;
We also discovered an issue with how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is tested. It seems, in the unmodified beta branch version, the test that should expect TRUE when rubrics are different for round 1 and 2, is actually testing whether Round 2 has more than 1 rubric assigned to it. We would expect this test to pass when using the old logic in &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt;. We modified the TRUE case to assign AssignmentQuestionnaire 1 to round 2, and AssignmentQuestionnaire 2 to round 1. &lt;br /&gt;
&lt;br /&gt;
Also, it seemed that the test for FALSE was testing using one questionnaire in round 1, and a different questionnaire in round 2, which seems logically incorrect for this method. We believe the false case should test for the same rubric across two rounds, in which case the rubrics would not be varying by round. We modified the FALSE case to assign AssignmentQuestionnaire 1 to both round 1 and round 2.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_spec_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Grades Controller Tests===&lt;br /&gt;
Lastly, the changes to how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is implemented meant that the two tests for the grades controller which rely on this method needed to be updated to expect the new logic. We implemented these updates to these two tests:&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_spec.png]]&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138096</id>
		<title>CSC/ECE 517 Spring 2021 - E2100. Tagging report for students</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2021_-_E2100._Tagging_report_for_students&amp;diff=138096"/>
		<updated>2021-03-19T20:43:45Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: /* Testing */ First changes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page details project documentation for the Spring 2021, &amp;quot;E2100 Tagging report for students&amp;quot; project, which aims to assist students with finding, and completing, incomplete &amp;quot;review tags&amp;quot; on an assignment using a dynamically generated heatgrid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction to Expertiza==&lt;br /&gt;
[http://http://expertiza.ncsu.edu/ Expertiza] is an [http://en.wikipedia.org/wiki/Open-source_software Open Source] [http://rubyonrails.org/ Rails] application which is used by instructors and students for creating assignments and submitting peer reviews.  Expertiza allows the instructor to create and customize assignments, create a list of topics the students can sign up for, have students work on teams and then review each other's assignments at the end. The Source code of the application can be cloned from [https://github.com/expertiza/expertiza/ Github].&lt;br /&gt;
&lt;br /&gt;
==About Review Tagging==&lt;br /&gt;
Review &amp;quot;tags&amp;quot; are a form of feedback on Expertiza where students &amp;quot;tag&amp;quot; (classify) text from peer reviews based on parameters specific to each tag deployment. The parameters can include helpfulness, positivity, suggestions, whether a review offered mitigation, and other parameters depending on the Answer Tag Deployment. These labeled data are then made available to Expertiza researchers for use in developing Natural Language Processing (NLP) / Machine Learning (ML) algorithms. Tagging is only collected for reviews where the text is a sufficient length to be useful as labeled data for NLP research. &lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
It can be difficult for students to find a tag they missed on the Team View page, and other teams are working with ML algorithms to &amp;quot;pre-tag&amp;quot; as many reviews as possible, leading to a granular field of completed/incomplete tags. For example, an assignment with two rounds of reviewing, ten questions per review, twelve reviews, and a 5-parameter tag deployment could contain as few as zero or as many as one thousand, two hundred tag prompts for a single student to complete. &lt;br /&gt;
&lt;br /&gt;
At this time, the only tagging feedback students see are the tag prompts and a Javascript counter with a numeric representation of how many tags have not been completed. In order to find a missed tag, students have to scroll the page and manually search for tags that aren't done.&lt;br /&gt;
&lt;br /&gt;
In order to help students complete all the tags for an assignment, we propose a new, dynamically generated heatgrid on the &amp;quot;Your Scores&amp;quot; view that breaks down reviews and tags by round, question, and review, which uses visual cues to help students find incomplete tags. The heatgrid shows both a total count of &amp;quot;complete&amp;quot; out of &amp;quot;available&amp;quot; tags, and individual tags with color-scaled feedback for completeness. &lt;br /&gt;
&lt;br /&gt;
'''The new heatgrid must handle these requirements, and display visual feedback accordingly:'''&lt;br /&gt;
* Single and multiple round reviews&lt;br /&gt;
* Reviews with and without tag prompts&lt;br /&gt;
* Dynamically update as tags are completed&lt;br /&gt;
* Different tag deployments will use different numbers of tags per review&lt;br /&gt;
* Use of existing review row-sort functionality&lt;br /&gt;
* Not rely on the database for tag data, as AnswerTag database rows may not exist until tags are clicked.&lt;br /&gt;
* Give useable feedback for all users (characters as well as color-coding)&lt;br /&gt;
* Only be shown when a tags have been deployed to an assignment&lt;br /&gt;
* Show the total progress of tagging in the format, &amp;quot;249 out of 315&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Our proposed solution is a visual feedback aid which exists purely for student users. Tagging data, when input, is stored to the database dynamically using jQuery. For these reasons, and to facilitate dynamic updates, we have chosen to implement this functionality entirely on the client-side of the application using Javascript and jQuery. The presence or absence of tag prompts are detected at page load, and the heat grid is rendered as appropriate. The flowchart below details the new Javascript functions which were implemented to accomplish this goal, and the associated program flow. &lt;br /&gt;
&lt;br /&gt;
'''Your Scores Flow Diagram'''&lt;br /&gt;
&lt;br /&gt;
[[File:View_teamRevA.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Heat grid for a two-round review'''&lt;br /&gt;
&lt;br /&gt;
[[File:HeatGridDemo.png|1100px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Code Style===&lt;br /&gt;
A note on code style: Because this project utilizes so much Javascript, we tried to keep the code style consistent and true to best practices for both Ruby and Javascript, depending on which language we were working in. Therefore, any code or variables from the Ruby section should use snake_case, and code in the Javascript section should use lowerCamelCase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Server-Side Implementation: &amp;quot;Your Scores&amp;quot; and &amp;quot;Alternate View&amp;quot;===&lt;br /&gt;
Because most of the logic is implemented on the client-side, the server-side implementation is fairly simple. &lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Your Scores&amp;quot; view (/views/grades/view_team.html.erb)====&lt;br /&gt;
We added an empty HTML &amp;lt;&amp;lt;nowiki /&amp;gt;table&amp;gt; tag with id and tag information that is used to generate the heatgrid with Javascript.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_table_tag.png]]&lt;br /&gt;
&lt;br /&gt;
In order to simplify the jQuery which parses the rendered page for tags, we added three numeric and one boolean jQueryable data- fields and a new id field to the td container tag which holds each review text (and the associated tags, if present). We used existing Rails variables to populate these data and id fields. The id is set using &amp;quot;rr&amp;quot; for reviewrow (shortened for brevity) plus the question and review number (j and index). We also created fields for data-round (round number) data-question_num (question number), data-review_num (review number), and data-has_tag (boolean whether tags exist). &lt;br /&gt;
&lt;br /&gt;
A word on style choices: the &amp;quot;data-&amp;quot; is the key queried by jQuery, so a dash is used. The string after the dash is free for us to choose the style, so we used Ruby style to match the convention on the rest of the application.&lt;br /&gt;
&lt;br /&gt;
[[File:tdRailsTag.png]]&lt;br /&gt;
&lt;br /&gt;
Finally, we added a call inside the &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler to tagActionOnLoad(), which starts the rendering code for the client-side Javascript once the page has loaded and tags (if available) are present for counting. We also added a jQuery of our new heat grid table within the onClick for the &amp;quot;Hide Tags&amp;quot; link to ensure our heatgrid is hidden when tag prompts are hidden.&lt;br /&gt;
&lt;br /&gt;
[[File:view_team_javascript.png]]&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Alternate View&amp;quot; (/views/grades/view_my_scores.html.erb)====&lt;br /&gt;
&lt;br /&gt;
We added function calls to countTotalTags() and countTaggedTags() inside the document.Ready() event handler to calculate the total number of tags and the total number of tagged tags on the page that currently being rendered. We also added div HTML element with id tag_stats and render this element with string &amp;quot;Tag Finished: tagged_tags/total_tags&amp;quot;. For example, if there are 370 tags in total on this page and 10 of them has already been tagged, then 10/370 will be displayed. This element will also be updated dynamically when users make tag changes on this page. &lt;br /&gt;
&lt;br /&gt;
[[File:alternate_view_html_code.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Your Scores&amp;quot; (view_team.html.erb)===&lt;br /&gt;
The majority of the logic is implemented on the client side. After the page is loaded and the rest of the ReactJS interface is rendered, a dynamically rendered heatgrid is rendered on the &amp;quot;Your Scores&amp;quot; view. A series of functions included in Javascript asset files &amp;lt;code&amp;gt;view_team_in_grades.js&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;answer_tags.js&amp;lt;/code&amp;gt; are called which render the appropriate element for each view. These functions are described below, organized in calling order.&lt;br /&gt;
&lt;br /&gt;
====Initialization and  Rendering - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnLoad()=====&lt;br /&gt;
Called by &amp;lt;code&amp;gt;document.Ready()&amp;lt;/code&amp;gt; event handler, this function first calls &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt; getTagPrompts()&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, determines if zero exist, and exits if so. If tag prompts exist, it continues to call other functions which query the page for information and render the tag heatgrid.&lt;br /&gt;
[[File:tagActionOnLoad.png]]&lt;br /&gt;
&lt;br /&gt;
=====getTagPrompts()=====&lt;br /&gt;
A simple Javascript query that collects all the tag prompts as an HTMLCollection for use with other function.&lt;br /&gt;
&lt;br /&gt;
[[File:getTagPrompts.png]]&lt;br /&gt;
&lt;br /&gt;
=====calcTagRatio()=====&lt;br /&gt;
A mathematics helper function that uses the tag prompt collection to gather data used in populating the tag heatgrid header (&amp;quot;tag fraction&amp;quot;) which reads, for example, &amp;quot;25 out of 350&amp;quot;. This function gathers all the numeric data, as well as calculating the completion ratio as a decimal and adapting this decimal to a value that can be used to reference heatgrid color classes (c0 grey, c1 red, c2 orange, c3 yellow, c4 light green, c5 dark green). Note that we avoided using the light green class because it's too easily confused with dark green -- meaning &amp;quot;all done&amp;quot; -- so logic in this function ensures we skip straight from c3 yellow @ .9999 completion, to c5 dark green at 1.0000 completion.&lt;br /&gt;
&lt;br /&gt;
[[File:calcTagRatio.png]]&lt;br /&gt;
&lt;br /&gt;
=====getRowData()=====&lt;br /&gt;
This method uses jQuery to retrieve the row containers for all questions in all rounds of this review, whether they contain tag prompts or not. This allows us to populate the heatgrid with 'grey - universal No-symbol' for reviews whose length metric is too low to have a tag prompt associated.&lt;br /&gt;
&lt;br /&gt;
[[File:getRowData.png]]&lt;br /&gt;
&lt;br /&gt;
=====countRounds()=====&lt;br /&gt;
This function parses the rows of the review to determine how many rounds of reviews exist in the assignment. This allows us to intelligently print &amp;quot;Round 2 -- Question 3&amp;quot; for multi round reviews, or &amp;quot;Question 3&amp;quot; for single round reviews.&lt;br /&gt;
&lt;br /&gt;
[[File:countRounds.png]]&lt;br /&gt;
&lt;br /&gt;
=====getGridWidth()=====&lt;br /&gt;
This function parses the row data to determine how many tag prompts were used in this deployment using a simple selection search for the largest number of tags in a review. Since different tag deployments use different numbers of prompts, the rendering of the heatgrid needs to address this flexibility. Furthermore, the design of this function will allow for future flexibility of tag deployments, i.e., where different reviews in the same assignment or round may have different numbers of tags.&lt;br /&gt;
&lt;br /&gt;
[[File:getGridWidth.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawTagGrid()=====&lt;br /&gt;
This is the master function for the rendering of the tag grid. This function sets up tooltip text, calls countRounds() and getGridWidth() and passes control to the three sub-functions, drawHeader(), drawQuestionRow(), and drawReviewRow(), depending on what type of item needs to be rendered next.&lt;br /&gt;
&lt;br /&gt;
[[File:drawTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawHeader()=====&lt;br /&gt;
This function draws the two-row header of the  tag heatgrid which will contain &amp;quot;Tags Completed&amp;quot; and &amp;quot;15 out of 425&amp;quot;. It also calls addToolTip() to add the tooltips for expand/contract, and the color legend for the tag fraction row. &lt;br /&gt;
&lt;br /&gt;
[[File:drawHeader.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawQuestionRow()=====&lt;br /&gt;
This function is called once per question during the rendering process, and draws the row which will read, &amp;quot;Question 3&amp;quot; or &amp;quot;Round 2 -- Question 3&amp;quot;. This also calls addToolTip() to add a color legend tooltip for the body of the heatgrid.&lt;br /&gt;
&lt;br /&gt;
[[File:drawQuestionRow.png]]&lt;br /&gt;
&lt;br /&gt;
=====drawReviewRow()=====&lt;br /&gt;
This function draws each row containing the presence, or absence of, tag prompts for each review. These contain R.# (Review.number), and a unicode symbol (set in the global variables). The unicode symbol is to enable accessibility for users who cannot see the red/green spectrum clearly, and will be populated with either a Universal No to mean no tags available, a Warning symbol, to mean tags not done, or a Check-Mark to indicate tags done.&lt;br /&gt;
&lt;br /&gt;
[[File:drawReviewRow.png]]&lt;br /&gt;
&lt;br /&gt;
Lastly, a call is made to updateTagsFraction() as part of the initial render to update the &amp;quot;12 out of 230&amp;quot; header row in the tag grid.&lt;br /&gt;
&lt;br /&gt;
====Updating and Interaction Functions - view_team_in_grades.js====&lt;br /&gt;
=====tagActionOnUpdate()=====&lt;br /&gt;
Each time a tag prompt is changed, this function is called to update the heatgrid. This function calls several other functions, some of which have been used previously it the rendering step.&lt;br /&gt;
&lt;br /&gt;
[[File:tagActionOnUpdate.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagsFraction()=====&lt;br /&gt;
This function is used both once in the rendering step, and again each time a tag prompt is changed. This function takes the data from calcTagRatio() and updates the contents and color of the header row in the tag heatgrid (&amp;quot;12 out of 220&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagsFraction.png]]&lt;br /&gt;
&lt;br /&gt;
=====updateTagGrid()=====&lt;br /&gt;
This function queries the tag prompts on the page, and uses these data to update the tag heatgrid class and text.&lt;br /&gt;
&lt;br /&gt;
[[File:updateTagGrid.png]]&lt;br /&gt;
&lt;br /&gt;
=====toggleHeatGridRows()=====&lt;br /&gt;
This function &amp;quot;Collapses&amp;quot; the heatgrid down to only the two header rows. Note that this function is called both onClick of the heatgrid header, so a user can collapse the grid manually, and automatically when the tag fraction is updated. This ensures that a user visiting the page with all tags complete will see a collapsed heatgrid by default.&lt;br /&gt;
&lt;br /&gt;
[[File:toggleHeatGridRows.png]]&lt;br /&gt;
&lt;br /&gt;
====Stylesheets - grades.scss====&lt;br /&gt;
We augmented the stylesheets used in grades so that the new heatgrid could be styled as small as possible to save page real estate. To do so, we added new classes in grades.scss&lt;br /&gt;
&lt;br /&gt;
[[File:stylesheetsGrades_scss.png]]&lt;br /&gt;
&lt;br /&gt;
===Client-Side Implementation: &amp;quot;Alternate View&amp;quot; (view_my_scores.html.erb)===&lt;br /&gt;
=====countTotalTags()=====&lt;br /&gt;
A simple JS function to count all the tag elements on current page. It will be invoked when the page first being rendered and also when any tags are changed by users.&lt;br /&gt;
[[File:countTotalTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====countTaggedTags()=====&lt;br /&gt;
A simple JS function to count the number of tagged tags on the current page. It will be invoked when the page first being rendered and when any tags are changed by users. &lt;br /&gt;
[[File:countTaggedTags.png]]&lt;br /&gt;
&lt;br /&gt;
=====Additional Logic Check=====&lt;br /&gt;
This JS helper dynamically render the page when any tags are changed by users. Since we adopt different logic on two view pages, we added a condition check to tell which page we are on and apply the corresponding logic.&lt;br /&gt;
[[File:answer_tag.png]]&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
Our modifications were mostly on the front end of the code in the app/views and app/assets sections so we decided to create a spec/features test. Our feature test is named view_team_spec.rb and its purpose is to ensure that the table for the heatgrid is generated when accessing the &amp;quot;Your Scores&amp;quot; section. We were able to accomplish this by mostly drawing off of pre-existing feature tests namely peer_review_spec.rb, which had a user access and review another project.&lt;br /&gt;
&lt;br /&gt;
You can run our created feature test with the following command in the expertiza directory.&lt;br /&gt;
rspec spec/features/view_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
Expected Output:&lt;br /&gt;
1 example, 0 failures&lt;br /&gt;
&lt;br /&gt;
Since we did not make any changes to the model or controller classes we did construct any RSpec tests for any model or controller behavior.&lt;br /&gt;
&lt;br /&gt;
===Navigating to Tag Report===&lt;br /&gt;
For manual testing purposes, we logged in as instructor6 and impersonated a student to view the reviews given to that student and be able to tag the reviews and see if the tagged reviews are updated in the heatmap.&lt;br /&gt;
&lt;br /&gt;
===Manual Testing Instructions===&lt;br /&gt;
1. Login to Expertiza as &amp;quot;instructor6&amp;quot; with password &amp;quot;password&amp;quot;. If hosted locally the url to access Expertiza should be &amp;quot;localhost:3000&amp;quot;.&amp;lt;br&amp;gt;&lt;br /&gt;
2. Click on Manage... then &amp;quot;Impersonate User&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
3. Enter &amp;quot;student7856&amp;quot; into the field and click the Impersonate button&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:ImpersonateStudent.PNG]] &amp;lt;br&amp;gt;&lt;br /&gt;
4. Click on &amp;quot;Program 2&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
5. Click on &amp;quot;Your Scores&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:YourScores.PNG]]&lt;br /&gt;
6. You should be able to click on a review score (1-5) and have the table expand, if you tag or remove a tag from a review, you should see the table on the right hand side of the page update to reflect your changes.&amp;lt;br&amp;gt;&lt;br /&gt;
7. Click Back and then click on &amp;quot;Alternate View&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
You should see the following page.&amp;lt;br&amp;gt;&lt;br /&gt;
[[File:AlternateView.PNG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Testing HTML changes using Capybara===&lt;br /&gt;
rspec&lt;br /&gt;
&lt;br /&gt;
==Additional Modifications -- Bugfix in Assignment Model and Grades Controller==&lt;br /&gt;
In developing the tagging report, the team noticed a significant, pre-existing view bug on view_team in the beta branch of Expertiza. This bug was causing rows of the review heatgrid to not be populated when the page was rendered. The team is still in communication with the main Expertiza development team to review and verify the validity of these modifications. At the time of this document, all build tests are passing.&lt;br /&gt;
&lt;br /&gt;
'''Screenshot of unpatched bug:   '''&lt;br /&gt;
[[File:unpatched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Screenshot with patch applied: '''&lt;br /&gt;
[[File:patched_view_bug.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
===Origin of Bug===&lt;br /&gt;
We discovered a twofold cause of this bug. In part, in the '''grades controller''', calls were being made to an ActiveRecord query, '''@assignment.vary_by_round,''' which was always returning zero. Upon changing this query to use the helper method, '''varying_rubrics_by_round?''' from the '''Assignment''' model, we discovered that a recent change to the logic of '''varying_rubrics_by_round?''' was also causing unexpected behavior. We have included four modifications to our pull request that patch this bug.&lt;br /&gt;
&lt;br /&gt;
===Patches to Assignment model===&lt;br /&gt;
In the '''varying_rubrics_by_round?''' method in the '''Assignment''' model, we discovered a recent change to the logic that was causing this method to test whether there were 2 or more '''AssignmentQuestionnaires''' assigned to Round 2 of reviews. Instead, this method should test whether there are 2 or more AssignmentQuestionnaires used on the assignment. We changed this logic to query using &amp;lt;code&amp;gt;used_in_round [1,2]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;used_in_round 2&amp;lt;/code&amp;gt;, which should evaluate '''.size &amp;gt; 1 TRUE''' if each round has a different '''AssignmentQuestionnaire'''.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_model_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Patches to Grades Controller===&lt;br /&gt;
In the grades controller, queries were being made to &amp;lt;code&amp;gt;@assignment.vary_by_round&amp;lt;/code&amp;gt; instead of the helper method, &amp;lt;code&amp;gt;@assignment.varying_rubrics_by_round?&amp;lt;/code&amp;gt;. These queries were always returning 0, because that DB field in our DB  appears to be 0 for all table rows. We changed the 4 calls from this query to method calls.&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Assignment Tests===&lt;br /&gt;
We also discovered an issue with how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is tested. It seems, in the unmodified beta branch version, the test that should expect TRUE when rubrics are different for round 1 and 2, is actually testing whether Round 2 has more than 1 rubric assigned to it. We would expect this test to pass when using the old logic in &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt;. We modified the TRUE case to assign AssignmentQuestionnaire 1 to round 2, and AssignmentQuestionnaire 2 to round 1. &lt;br /&gt;
&lt;br /&gt;
Also, it seemed that the test for FALSE was testing using one questionnaire in round 1, and a different questionnaire in round 2, which seems logically incorrect for this method. We believe the false case should test for the same rubric across two rounds, in which case the rubrics would not be varying by round. We modified the FALSE case to assign AssignmentQuestionnaire 1 to both round 1 and round 2.&lt;br /&gt;
&lt;br /&gt;
[[File:assignment_spec_patch.png]]&lt;br /&gt;
&lt;br /&gt;
===Updates to Grades Controller Tests===&lt;br /&gt;
Lastly, the changes to how &amp;lt;code&amp;gt;varying_rubrics_by_round?&amp;lt;/code&amp;gt; is implemented meant that the two tests for the grades controller which rely on this method needed to be updated to expect the new logic. We implemented these updates to these two tests:&lt;br /&gt;
&lt;br /&gt;
[[File:grades_controller_spec.png]]&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:YourScores.PNG&amp;diff=138095</id>
		<title>File:YourScores.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:YourScores.PNG&amp;diff=138095"/>
		<updated>2021-03-19T20:39:26Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:ImpersonateStudent.PNG&amp;diff=138094</id>
		<title>File:ImpersonateStudent.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:ImpersonateStudent.PNG&amp;diff=138094"/>
		<updated>2021-03-19T20:39:13Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:AlternateView.PNG&amp;diff=138093</id>
		<title>File:AlternateView.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:AlternateView.PNG&amp;diff=138093"/>
		<updated>2021-03-19T20:38:53Z</updated>

		<summary type="html">&lt;p&gt;Jmhurst: Jmhurst uploaded a new version of File:AlternateView.PNG&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Jmhurst</name></author>
	</entry>
</feed>