<?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=Jdjohns4</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=Jdjohns4"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Jdjohns4"/>
	<updated>2026-09-27T22:30:25Z</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_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150129</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150129"/>
		<updated>2023-04-25T02:33:31Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: added video link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Definition&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface. The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_app_workflow_updated.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== App workflow ===&lt;br /&gt;
The visualization below presents the general workflow of our app.  &lt;br /&gt;
&lt;br /&gt;
Upon launch, the app starts with a open_file window dialog box which prompts user to load a local file (or enter query input, in the future).  The open_file window allows file browsing (in the future, query to GitHub GraphQL API), and upon loading the data, the app will load and preprocess the data for display and computation.  Once data is loaded and prepare, main_window opens and this event will close the initial ope_file dialog.  &lt;br /&gt;
&lt;br /&gt;
The main window contains two tabs: one for metrics summary statistics and one for ranking users (returned by the query or data file) for each metric. Next to each metric in the summary statistics tab, users will have the option to visualize the metric.  Additionally, the main window contains a menu option that allows loading another file (or performing another query).  This action will close the report and visualization of current data and load up the new data.&lt;br /&gt;
&lt;br /&gt;
The visualization window contains two tabs: one for box plot and one for histogram.  These visualizations provide graphical insights into the chosen metric.&lt;br /&gt;
&lt;br /&gt;
[[File: G2335_app_flow.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate  ## mac and linux&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; venv\Scripts\activate  ## windows&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Run the Application ====&lt;br /&gt;
From within the virtual environment in the root directory (PYQT_UI) run the following command to start the application:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;python run.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
&lt;br /&gt;
===== GUI =====&lt;br /&gt;
We implemented the graphical user interface of our application using PyQt6. Our interface consists of three main windows:&lt;br /&gt;
&lt;br /&gt;
* File Browser Window -- the interface allowing the user to browse and load an input CSV file&lt;br /&gt;
* Report Window -- the interface showing two tabs, one for summary statistics for all metrics and one for the lowest ranked users in each metric &lt;br /&gt;
* Metric Detail Window -- the interface showing two tabs, one for an boxplot of the selected metric data, and one for a histogram of the selected metric data&lt;br /&gt;
&lt;br /&gt;
We constructed our three windows using the Qt Designer [https://build-system.fman.io/qt-designer-download] application which allows the laying out and building of a user interface via graphical drag-and-drop methods. The output of Qt Designer are .ui files which are an XML format file that describes the user interface elements and properties.&lt;br /&gt;
&lt;br /&gt;
With the .ui files, there are two methods to using them within the PyQt library:&lt;br /&gt;
* Generating python class source code directly from the .ui file(s) for insertion into the application or,&lt;br /&gt;
* Reading the .ui files in directly during the execution of the python application which constructs the GUI objects on the fly at runtime.&lt;br /&gt;
&lt;br /&gt;
Since the application is small and performance with our sample data was not an issue, we elected for the second method to generate the GUI objects on the fly. Additionally we are able to version the XML .ui files and directly tweak elements within the XML to manipulate the interface.&lt;br /&gt;
&lt;br /&gt;
Each of our windows has a python class and a paired .ui file that is loaded by the class when it is instantiated. An example of this from the FileWindow class within app/gui/file_window.py:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;
  DESIGNER_OPEN_FILE = os.path.join(ROOT_DIR,&amp;quot;designer&amp;quot;,&amp;quot;file_window.ui&amp;quot;)&lt;br /&gt;
  self.ui = uic.loadUi(DESIGNER_OPEN_FILE, self)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From there the ui objects can be referenced to hook up interaction events with the rest of the application. Continuing the above example, the file browse and load buttons are hooked to downstream browseFile() and openReport() functions:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  self.ui.browseButton.clicked.connect(self.browseFile)&lt;br /&gt;
  self.ui.loadFileButton.clicked.connect(self.openReport)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to our three main windows, we use a singleton class called Main (app/gui/main.py) that is responsible for all programmatic opening/closing of windows such that a single version of the current state of the application is kept by that object. Instantiated window objects have a pointer to this Main class which allows windows to interact with one another, for instance to single the report window to open once the file window has loaded a CSV.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== File browsing/loading and summary statistics tables =====&lt;br /&gt;
The app starts with a browse/load file dialog box that prompts the user to provide data for the report.  ''Browse'' button opens file explorer on the user's machine and allows the user to search for the file they need.  Based on how the data is organized behind the scene and the tools we're currently using, our app only accepts files with CSV extension.  Our app accesses the machine's file explorer system and safeguards the file type using the following code.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## browse csv file using file explorer&lt;br /&gt;
  def browseFile(self):&lt;br /&gt;
      filename = QFileDialog.getOpenFileName(self, 'Open File', '.', 'CSV Files (*.csv)')&lt;br /&gt;
      self.filename = filename[0]&lt;br /&gt;
      self.ui.pathToFileTextbox.setText(self.filename)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The text box next to ''browse'' button display the path of the chosen file on the user's machine.  This text box will not allow the user to manually enter the path to the file or edit the path of the file browsed.  This is to avoid human error while interacting with the file's path. The load function of the ''open_file'' window also further checks the validity of the path using the following code.  If the path of a file is invalid, a dialog box will pop up to notify the user as such.  Note that, the validity of the path to a file is not checked until the ''load'' button is clicked.  This is to prevent situations in which the user browses a valid file through the machine's file explorer, but the file, under whatever circumstances, gets deleted between after browsing is done and before loading is finished.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## check the validity of user-entered filename&lt;br /&gt;
  def _isValidFile(self):&lt;br /&gt;
      return self.filename and os.path.isfile(self.filename) and self.filename[-4:] == '.csv'&lt;br /&gt;
&lt;br /&gt;
  ## load csv file &lt;br /&gt;
  def loadFile(self):&lt;br /&gt;
      if not self._isValidFile():&lt;br /&gt;
          return self.raiseMissingFilenameError()&lt;br /&gt;
      metric_constructor = Metrics(self.filename)&lt;br /&gt;
      metric_constructor.constructMetricsTable()&lt;br /&gt;
      self.data = metric_constructor.get_datatable()&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Behind the scene, the ''load'' button also performs the following functionalities upon activation and before the main window loads&lt;br /&gt;
* Computes the metric values (columns) for each user (row) [1]&lt;br /&gt;
* Compute summary statistics for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Compute user ranking for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Organize the summary statistics into pandas dataframe for display &lt;br /&gt;
* Organize the ranking data into pandas dataframe for display&lt;br /&gt;
&lt;br /&gt;
===== Visualization =====&lt;br /&gt;
Once a CSV has been loaded and the Report Window appears, the detail icon to the left of each metric row can be selected to launch the Metric Detail Window. This window consists of a tabbed interface allowing the toggling between Boxplot and Histogram visual representations of the underlying metric data. The class responsible for initializing the window and visualizations is app.gui.metric_detail_window.MetricDetailWindow().&lt;br /&gt;
&lt;br /&gt;
The boxplot and histogram visualizations are constructed via the matplotlib library which is initialized in a special manner to allow use within the PyQt library. These imports were demonstrated in a useful example at https://www.pythonguis.com/tutorials/pyqt6-plotting-matplotlib/ and are included below for reference:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
import matplotlib&lt;br /&gt;
matplotlib.use('QtAgg')&lt;br /&gt;
&lt;br /&gt;
from PyQt6 import uic&lt;br /&gt;
from PyQt6.QtGui import QAction&lt;br /&gt;
from PyQt6.QtWidgets import (&lt;br /&gt;
    QDialog&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg&lt;br /&gt;
from matplotlib.figure import Figure&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The MetricDetailWindow class is instantiated with a pointer to the parent Main() as well as the associated metric identifier, summary data, and histogram data used to construct the visualizations. The window is labeled with the metric identifier and a short description of the metric from a currently hardcoded dictionary within the class. The boxplot and histogram matplotlib figures are initialized via the app.gui.metric_detail_window.MplCanvas() class from the input data which was derived within app/data/compute_statistics.py.&lt;br /&gt;
&lt;br /&gt;
The boxplot is constructed per the following:&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
boxplot_data = [{&lt;br /&gt;
    'label': &amp;quot;&amp;quot;,&lt;br /&gt;
   'whislo': float(metric_summary_data['Min']), &lt;br /&gt;
   'q1': float(metric_summary_data['25th']), &lt;br /&gt;
   'med': float(metric_summary_data['Median']), &lt;br /&gt;
   'q3': float(metric_summary_data['75th']), &lt;br /&gt;
    whishi': float(metric_summary_data['Max']), &lt;br /&gt;
    'fliers': []&lt;br /&gt;
}]&lt;br /&gt;
sc.axes.bxp(boxplot_data, showfliers=False) &lt;br /&gt;
  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The histogram is constructed per the following:&lt;br /&gt;
   &amp;lt;nowiki&amp;gt;&lt;br /&gt;
sc.axes.hist(self.histogram_data)&lt;br /&gt;
   &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Default matplotlib settings are used for both visualizations -- this is an area where possible tweaks to the application could be beneficial.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
&lt;br /&gt;
*Before you run your test make sure you Install Prereq Libraries:&lt;br /&gt;
 &amp;lt;code&amp;gt; pip install -r requirements.txt &amp;lt;/code&amp;gt;&lt;br /&gt;
*To run different test cases:&lt;br /&gt;
 &amp;lt;code&amp;gt; pytest tests/test.py &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left:40px&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Test No !! Test Description !! Expected Output || Test Result&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Test that the browseFile method opens the file dialog || A file dialog should appear and the file name should not be empty || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 1.2 || Test that the loadFile method loads the file correctly || User should be able to load csv file in their device || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Test if the file is valid || User should be able to select a valid CSV file is selected, Invalid files should trigger an error message || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Test getSummaryStatistics function || It verifies that the function returns a DataFrame, if DataFrame has the expected columns and also that the DataFrame has the expected index || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Test getHistogramData function || It verifies if function returns a Series, if series has the expected length and that the values are numeric || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Test constructMetricsTable method || It verifies if the constructMetricsTable method generates the expected data frame with the correct columns and dimensions || PASS&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Details====&lt;br /&gt;
&lt;br /&gt;
*Browse File:&lt;br /&gt;
&lt;br /&gt;
In this test, we verify that the browseFile() method of the FileWindow class opens a file dialog correctly. &lt;br /&gt;
A qtbot fixture is passed as an argument to this fixture. For testing PyQt-based GUI applications, the pytest-qt library provides the qtbot fixture. &lt;br /&gt;
It then asserts that the filename attribute of the FileWindow instance is not empty, indicating that the user has selected a file.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
@pytest.fixture&lt;br /&gt;
def file_window(qtbot):&lt;br /&gt;
# Create a FileWindow instance for testing and show it&lt;br /&gt;
    file_window = FileWindow(None)&lt;br /&gt;
    file_window.show()&lt;br /&gt;
# Test that the browseFile method opens the file dialog&lt;br /&gt;
    with qtbot.waitSignal(file_window.ui.pathToFileTextbox.textChanged):&lt;br /&gt;
        file_window.browseFile()&lt;br /&gt;
        assert file_window.filename != ''&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Load File:&lt;br /&gt;
&lt;br /&gt;
A file can be selected from a local machine and loaded into the application(particularly CSV file). This functionality is essential to the application's primary purpose, which is to analyze data for users.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test that the loadFile method loads the file correctly&lt;br /&gt;
    file_window.loadFile()&lt;br /&gt;
    assert file_window.data is not None&lt;br /&gt;
    qtbot.addWidget(file_window)&lt;br /&gt;
    return file_window&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Valid / Invalid files:&lt;br /&gt;
&lt;br /&gt;
The first test function, &amp;quot;test_is_valid_file&amp;quot;, checks if a CSV file exists by mocking the &amp;quot;isfile&amp;quot; function from the &amp;quot;os.path&amp;quot; module. And the second test function, &amp;quot;test_invalid_file&amp;quot;, checks if a file with a TXT format does not exist by mocking the &amp;quot;isfile&amp;quot; function to always return false. &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_is_valid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is valid&lt;br /&gt;
    filename = &amp;quot;CSV Files (*.csv)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return True&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
   &lt;br /&gt;
&lt;br /&gt;
def test_invalid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is invalid&lt;br /&gt;
    filename = &amp;quot;TXT Files (*.txt)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return False&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getSummaryStatistics:&lt;br /&gt;
&lt;br /&gt;
This test determines whether a given input dataset's summary statistics, such as the median, 25th percentile, mean, 75th percentile, standard deviation, minimum, and maximum, are accurately computed by the getSummaryStatistics function. It makes use of NumPy and Pandas for creating a random dataset, which it then gives to the getSummaryStatistics method. The test verifies whether a Pandas DataFrame with the anticipated columns and index is properly returned by the function.&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_getSummaryStatistics():&lt;br /&gt;
    # Test that the function returns a DataFrame&lt;br /&gt;
    result = getSummaryStatistics(data)&lt;br /&gt;
    assert isinstance(result, pd.DataFrame)&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected columns&lt;br /&gt;
    expected_columns = ['Median', '25th', 'Mean', '75th', 'Stdev', 'Min', 'Max']&lt;br /&gt;
    assert result.columns.tolist() == expected_columns&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected index&lt;br /&gt;
    expected_index = data.columns.tolist()&lt;br /&gt;
    assert result.index.tolist() == expected_index&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getHistogramData:&lt;br /&gt;
&lt;br /&gt;
This test checks whether a particular input dataset and metric has been utilized by the getHistogramData method to compute histogram data correctly. Using NumPy and Pandas, the program generates a random dataset and chooses the first column in that dataset as the metric. Following that, this dataset and metric are passed to the getHistogramData function. The function's accuracy in returning a Pandas Series with the desired length and data type is checked in this test. It also tests that the values are numeric.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
def test_getHistogramData():&lt;br /&gt;
    # Test that the function returns a Series&lt;br /&gt;
    metric = data.columns[0]&lt;br /&gt;
    result = getHistogramData(data, metric)&lt;br /&gt;
    assert isinstance(result, pd.Series)&lt;br /&gt;
&lt;br /&gt;
    # Test that the Series has the expected length&lt;br /&gt;
    expected_length = len(data)&lt;br /&gt;
    assert len(result) == expected_length&lt;br /&gt;
&lt;br /&gt;
    # Test that the values are numeric&lt;br /&gt;
    assert result.dtype == 'float64'&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*constructMetricsTable:&lt;br /&gt;
&lt;br /&gt;
This test validates whether the Metrics class' constructMetricsTable method correctly creates a Pandas DataFrame from the imported data. It first creates a Metrics object using the metrics_obj fixture mentioned above before invoking the constructMetricsTable function. The resulting Pandas DataFrame is tested to determine if the intended column names and shape are there.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test for constructMetricsTable method&lt;br /&gt;
def test_constructMetricsTable(metrics_obj):&lt;br /&gt;
    # Test the output of the method&lt;br /&gt;
    metrics_obj.constructMetricsTable()&lt;br /&gt;
    df = metrics_obj.get_datatable()&lt;br /&gt;
    assert 'lifespan' in df.columns&lt;br /&gt;
    assert 'A' in df.columns&lt;br /&gt;
    assert 'B' in df.columns&lt;br /&gt;
    assert 'C' in df.columns&lt;br /&gt;
    assert 'D' in df.columns&lt;br /&gt;
    assert 'E' in df.columns&lt;br /&gt;
    assert 'F' in df.columns&lt;br /&gt;
    assert 'G' in df.columns&lt;br /&gt;
    assert 'H' in df.columns&lt;br /&gt;
    assert 'I' in df.columns&lt;br /&gt;
    assert 'J' in df.columns&lt;br /&gt;
    assert 'K' in df.columns&lt;br /&gt;
    assert 'L' in df.columns&lt;br /&gt;
    assert 'M' in df.columns&lt;br /&gt;
    assert df.shape == (52, 15)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Test Result ===&lt;br /&gt;
[[File:TestResult.png |1000px|]]&lt;br /&gt;
&lt;br /&gt;
== End point summary ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case, the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
Additionally, our current code reads and processes .ui files (which are XML files of our app interface generated by the Qt Designer app) and then adds functions to the UI using Python code.  The fact that our code has to read and process XML files first slows down our app on start-up.  In the future, this could be avoided by converting the XML files into Python codes and objects and work with them directly without having to read and process the .ui files&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1C2YpQUS8T-329YTJDOE8aK0gZuVzYr2F/view?usp=sharing Demonstration Video]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150103</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150103"/>
		<updated>2023-04-25T00:53:13Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Visualization */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Definition&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface. The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_app_workflow_updated.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== App workflow ===&lt;br /&gt;
The visualization below presents the general workflow of our app.  &lt;br /&gt;
&lt;br /&gt;
Upon launch, the app starts with a open_file window dialog box which prompts user to load a local file (or enter query input, in the future).  The open_file window allows file browsing (in the future, query to GitHub GraphQL API), and upon loading the data, the app will load and preprocess the data for display and computation.  Once data is loaded and prepare, main_window opens and this event will close the initial ope_file dialog.  &lt;br /&gt;
&lt;br /&gt;
The main window contains two tabs: one for metrics summary statistics and one for ranking users (returned by the query or data file) for each metric. Next to each metric in the summary statistics tab, users will have the option to visualize the metric.  Additionally, the main window contains a menu option that allows loading another file (or performing another query).  This action will close the report and visualization of current data and load up the new data.&lt;br /&gt;
&lt;br /&gt;
The visualization window contains two tabs: one for box plot and one for histogram.  These visualizations provide graphical insights into the chosen metric.&lt;br /&gt;
&lt;br /&gt;
[[File: G2335_app_flow.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate  ## mac and linux&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; venv\Scripts\activate  ## windows&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Run the Application ====&lt;br /&gt;
From within the virtual environment in the root directory (PYQT_UI) run the following command to start the application:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;python run.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
&lt;br /&gt;
===== GUI =====&lt;br /&gt;
We implemented the graphical user interface of our application using PyQt6. Our interface consists of three main windows:&lt;br /&gt;
&lt;br /&gt;
* File Browser Window -- the interface allowing the user to browse and load an input CSV file&lt;br /&gt;
* Report Window -- the interface showing two tabs, one for summary statistics for all metrics and one for the lowest ranked users in each metric &lt;br /&gt;
* Metric Detail Window -- the interface showing two tabs, one for an boxplot of the selected metric data, and one for a histogram of the selected metric data&lt;br /&gt;
&lt;br /&gt;
We constructed our three windows using the Qt Designer [https://build-system.fman.io/qt-designer-download] application which allows the laying out and building of a user interface via graphical drag-and-drop methods. The output of Qt Designer are .ui files which are an XML format file that describes the user interface elements and properties.&lt;br /&gt;
&lt;br /&gt;
With the .ui files, there are two methods to using them within the PyQt library:&lt;br /&gt;
* Generating python class source code directly from the .ui file(s) for insertion into the application or,&lt;br /&gt;
* Reading the .ui files in directly during the execution of the python application which constructs the GUI objects on the fly at runtime.&lt;br /&gt;
&lt;br /&gt;
Since the application is small and performance with our sample data was not an issue, we elected for the second method to generate the GUI objects on the fly. Additionally we are able to version the XML .ui files and directly tweak elements within the XML to manipulate the interface.&lt;br /&gt;
&lt;br /&gt;
Each of our windows has a python class and a paired .ui file that is loaded by the class when it is instantiated. An example of this from the FileWindow class within app/gui/file_window.py:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;
  DESIGNER_OPEN_FILE = os.path.join(ROOT_DIR,&amp;quot;designer&amp;quot;,&amp;quot;file_window.ui&amp;quot;)&lt;br /&gt;
  self.ui = uic.loadUi(DESIGNER_OPEN_FILE, self)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From there the ui objects can be referenced to hook up interaction events with the rest of the application. Continuing the above example, the file browse and load buttons are hooked to downstream browseFile() and openReport() functions:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  self.ui.browseButton.clicked.connect(self.browseFile)&lt;br /&gt;
  self.ui.loadFileButton.clicked.connect(self.openReport)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to our three main windows, we use a singleton class called Main (app/gui/main.py) that is responsible for all programmatic opening/closing of windows such that a single version of the current state of the application is kept by that object. Instantiated window objects have a pointer to this Main class which allows windows to interact with one another, for instance to single the report window to open once the file window has loaded a CSV.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== File browsing/loading and summary statistics tables =====&lt;br /&gt;
The app starts with a browse/load file dialog box that prompts the user to provide data for the report.  ''Browse'' button opens file explorer on the user's machine and allows the user to search for the file they need.  Based on how the data is organized behind the scene and the tools we're currently using, our app only accepts files with CSV extension.  Our app accesses the machine's file explorer system and safeguards the file type using the following code.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## browse csv file using file explorer&lt;br /&gt;
  def browseFile(self):&lt;br /&gt;
      filename = QFileDialog.getOpenFileName(self, 'Open File', '.', 'CSV Files (*.csv)')&lt;br /&gt;
      self.filename = filename[0]&lt;br /&gt;
      self.ui.pathToFileTextbox.setText(self.filename)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The text box next to ''browse'' button display the path of the chosen file on the user's machine.  This text box will not allow the user to manually enter the path to the file or edit the path of the file browsed.  This is to avoid human error while interacting with the file's path. The load function of the ''open_file'' window also further checks the validity of the path using the following code.  If the path of a file is invalid, a dialog box will pop up to notify the user as such.  Note that, the validity of the path to a file is not checked until the ''load'' button is clicked.  This is to prevent situations in which the user browses a valid file through the machine's file explorer, but the file, under whatever circumstances, gets deleted between after browsing is done and before loading is finished.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## check the validity of user-entered filename&lt;br /&gt;
  def _isValidFile(self):&lt;br /&gt;
      return self.filename and os.path.isfile(self.filename) and self.filename[-4:] == '.csv'&lt;br /&gt;
&lt;br /&gt;
  ## load csv file &lt;br /&gt;
  def loadFile(self):&lt;br /&gt;
      if not self._isValidFile():&lt;br /&gt;
          return self.raiseMissingFilenameError()&lt;br /&gt;
      metric_constructor = Metrics(self.filename)&lt;br /&gt;
      metric_constructor.constructMetricsTable()&lt;br /&gt;
      self.data = metric_constructor.get_datatable()&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Behind the scene, the ''load'' button also performs the following functionalities upon activation and before the main window loads&lt;br /&gt;
* Computes the metric values (columns) for each user (row) [1]&lt;br /&gt;
* Compute summary statistics for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Compute user ranking for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Organize the summary statistics into pandas dataframe for display &lt;br /&gt;
* Organize the ranking data into pandas dataframe for display&lt;br /&gt;
&lt;br /&gt;
===== Visualization =====&lt;br /&gt;
Once a CSV has been loaded and the Report Window appears, the detail icon to the left of each metric row can be selected to launch the Metric Detail Window. This window consists of a tabbed interface allowing the toggling between Boxplot and Histogram visual representations of the underlying metric data. The class responsible for initializing the window and visualizations is app.gui.metric_detail_window.MetricDetailWindow().&lt;br /&gt;
&lt;br /&gt;
The boxplot and histogram visualizations are constructed via the matplotlib library which is initialized in a special manner to allow use within the PyQt library. These imports were demonstrated in a useful example at https://www.pythonguis.com/tutorials/pyqt6-plotting-matplotlib/ and are included below for reference:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
import matplotlib&lt;br /&gt;
matplotlib.use('QtAgg')&lt;br /&gt;
&lt;br /&gt;
from PyQt6 import uic&lt;br /&gt;
from PyQt6.QtGui import QAction&lt;br /&gt;
from PyQt6.QtWidgets import (&lt;br /&gt;
    QDialog&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg&lt;br /&gt;
from matplotlib.figure import Figure&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The MetricDetailWindow class is instantiated with a pointer to the parent Main() as well as the associated metric identifier, summary data, and histogram data used to construct the visualizations. The window is labeled with the metric identifier and a short description of the metric from a currently hardcoded dictionary within the class. The boxplot and histogram matplotlib figures are initialized via the app.gui.metric_detail_window.MplCanvas() class from the input data which was derived within app/data/compute_statistics.py.&lt;br /&gt;
&lt;br /&gt;
The boxplot is constructed per the following:&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
boxplot_data = [{&lt;br /&gt;
    'label': &amp;quot;&amp;quot;,&lt;br /&gt;
   'whislo': float(metric_summary_data['Min']), &lt;br /&gt;
   'q1': float(metric_summary_data['25th']), &lt;br /&gt;
   'med': float(metric_summary_data['Median']), &lt;br /&gt;
   'q3': float(metric_summary_data['75th']), &lt;br /&gt;
    whishi': float(metric_summary_data['Max']), &lt;br /&gt;
    'fliers': []&lt;br /&gt;
}]&lt;br /&gt;
sc.axes.bxp(boxplot_data, showfliers=False) &lt;br /&gt;
  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The histogram is constructed per the following:&lt;br /&gt;
   &amp;lt;nowiki&amp;gt;&lt;br /&gt;
sc.axes.hist(self.histogram_data)&lt;br /&gt;
   &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Default matplotlib settings are used for both visualizations -- this is an area where possible tweaks to the application could be beneficial.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
&lt;br /&gt;
*Before you run your test make sure you Install Prereq Libraries:&lt;br /&gt;
 &amp;lt;code&amp;gt; pip install -r requirements.txt &amp;lt;/code&amp;gt;&lt;br /&gt;
*To run different test cases:&lt;br /&gt;
 &amp;lt;code&amp;gt; pytest tests/test.py &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left:40px&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Test No !! Test Description !! Expected Output || Test Result&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Test that the browseFile method opens the file dialog || A file dialog should appear and the file name should not be empty || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 1.2 || Test that the loadFile method loads the file correctly || User should be able to load csv file in their device || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Test if the file is valid || User should be able to select a valid CSV file is selected, Invalid files should trigger an error message || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Test getSummaryStatistics function || It verifies that the function returns a DataFrame, if DataFrame has the expected columns and also that the DataFrame has the expected index || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Test getHistogramData function || It verifies if function returns a Series, if series has the expected length and that the values are numeric || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Test constructMetricsTable method || It verifies if the constructMetricsTable method generates the expected data frame with the correct columns and dimensions || PASS&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Details====&lt;br /&gt;
&lt;br /&gt;
*Browse File:&lt;br /&gt;
&lt;br /&gt;
In this test, we verify that the browseFile() method of the FileWindow class opens a file dialog correctly. &lt;br /&gt;
A qtbot fixture is passed as an argument to this fixture. For testing PyQt-based GUI applications, the pytest-qt library provides the qtbot fixture. &lt;br /&gt;
It then asserts that the filename attribute of the FileWindow instance is not empty, indicating that the user has selected a file.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
@pytest.fixture&lt;br /&gt;
def file_window(qtbot):&lt;br /&gt;
# Create a FileWindow instance for testing and show it&lt;br /&gt;
    file_window = FileWindow(None)&lt;br /&gt;
    file_window.show()&lt;br /&gt;
# Test that the browseFile method opens the file dialog&lt;br /&gt;
    with qtbot.waitSignal(file_window.ui.pathToFileTextbox.textChanged):&lt;br /&gt;
        file_window.browseFile()&lt;br /&gt;
        assert file_window.filename != ''&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Load File:&lt;br /&gt;
&lt;br /&gt;
A file can be selected from a local machine and loaded into the application(particularly CSV file). This functionality is essential to the application's primary purpose, which is to analyze data for users.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test that the loadFile method loads the file correctly&lt;br /&gt;
    file_window.loadFile()&lt;br /&gt;
    assert file_window.data is not None&lt;br /&gt;
    qtbot.addWidget(file_window)&lt;br /&gt;
    return file_window&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Valid / Invalid files:&lt;br /&gt;
&lt;br /&gt;
The first test function, &amp;quot;test_is_valid_file&amp;quot;, checks if a CSV file exists by mocking the &amp;quot;isfile&amp;quot; function from the &amp;quot;os.path&amp;quot; module. And the second test function, &amp;quot;test_invalid_file&amp;quot;, checks if a file with a TXT format does not exist by mocking the &amp;quot;isfile&amp;quot; function to always return false. &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_is_valid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is valid&lt;br /&gt;
    filename = &amp;quot;CSV Files (*.csv)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return True&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
   &lt;br /&gt;
&lt;br /&gt;
def test_invalid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is invalid&lt;br /&gt;
    filename = &amp;quot;TXT Files (*.txt)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return False&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getSummaryStatistics:&lt;br /&gt;
&lt;br /&gt;
This test determines whether a given input dataset's summary statistics, such as the median, 25th percentile, mean, 75th percentile, standard deviation, minimum, and maximum, are accurately computed by the getSummaryStatistics function. It makes use of NumPy and Pandas for creating a random dataset, which it then gives to the getSummaryStatistics method. The test verifies whether a Pandas DataFrame with the anticipated columns and index is properly returned by the function.&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_getSummaryStatistics():&lt;br /&gt;
    # Test that the function returns a DataFrame&lt;br /&gt;
    result = getSummaryStatistics(data)&lt;br /&gt;
    assert isinstance(result, pd.DataFrame)&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected columns&lt;br /&gt;
    expected_columns = ['Median', '25th', 'Mean', '75th', 'Stdev', 'Min', 'Max']&lt;br /&gt;
    assert result.columns.tolist() == expected_columns&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected index&lt;br /&gt;
    expected_index = data.columns.tolist()&lt;br /&gt;
    assert result.index.tolist() == expected_index&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getHistogramData:&lt;br /&gt;
&lt;br /&gt;
This test checks whether a particular input dataset and metric has been utilized by the getHistogramData method to compute histogram data correctly. Using NumPy and Pandas, the program generates a random dataset and chooses the first column in that dataset as the metric. Following that, this dataset and metric are passed to the getHistogramData function. The function's accuracy in returning a Pandas Series with the desired length and data type is checked in this test. It also tests that the values are numeric.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
def test_getHistogramData():&lt;br /&gt;
    # Test that the function returns a Series&lt;br /&gt;
    metric = data.columns[0]&lt;br /&gt;
    result = getHistogramData(data, metric)&lt;br /&gt;
    assert isinstance(result, pd.Series)&lt;br /&gt;
&lt;br /&gt;
    # Test that the Series has the expected length&lt;br /&gt;
    expected_length = len(data)&lt;br /&gt;
    assert len(result) == expected_length&lt;br /&gt;
&lt;br /&gt;
    # Test that the values are numeric&lt;br /&gt;
    assert result.dtype == 'float64'&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*constructMetricsTable:&lt;br /&gt;
&lt;br /&gt;
This test validates whether the Metrics class' constructMetricsTable method correctly creates a Pandas DataFrame from the imported data. It first creates a Metrics object using the metrics_obj fixture mentioned above before invoking the constructMetricsTable function. The resulting Pandas DataFrame is tested to determine if the intended column names and shape are there.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test for constructMetricsTable method&lt;br /&gt;
def test_constructMetricsTable(metrics_obj):&lt;br /&gt;
    # Test the output of the method&lt;br /&gt;
    metrics_obj.constructMetricsTable()&lt;br /&gt;
    df = metrics_obj.get_datatable()&lt;br /&gt;
    assert 'lifespan' in df.columns&lt;br /&gt;
    assert 'A' in df.columns&lt;br /&gt;
    assert 'B' in df.columns&lt;br /&gt;
    assert 'C' in df.columns&lt;br /&gt;
    assert 'D' in df.columns&lt;br /&gt;
    assert 'E' in df.columns&lt;br /&gt;
    assert 'F' in df.columns&lt;br /&gt;
    assert 'G' in df.columns&lt;br /&gt;
    assert 'H' in df.columns&lt;br /&gt;
    assert 'I' in df.columns&lt;br /&gt;
    assert 'J' in df.columns&lt;br /&gt;
    assert 'K' in df.columns&lt;br /&gt;
    assert 'L' in df.columns&lt;br /&gt;
    assert 'M' in df.columns&lt;br /&gt;
    assert df.shape == (52, 15)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Test Result ===&lt;br /&gt;
[[File:TestResult.png |1000px|]]&lt;br /&gt;
&lt;br /&gt;
== End point summary ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case, the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
Additionally, our current code reads and processes .ui files (which are XML files of our app interface generated by the Qt Designer app) and then adds functions to the UI using Python code.  The fact that our code has to read and process XML files first slows down our app on start-up.  In the future, this could be avoided by converting the XML files into Python codes and objects and work with them directly without having to read and process the .ui files&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150102</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150102"/>
		<updated>2023-04-25T00:38:59Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Project Setup and Application Execution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Definition&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface. The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_app_workflow_updated.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== App workflow ===&lt;br /&gt;
The visualization below presents the general workflow of our app.  &lt;br /&gt;
&lt;br /&gt;
Upon launch, the app starts with a open_file window dialog box which prompts user to load a local file (or enter query input, in the future).  The open_file window allows file browsing (in the future, query to GitHub GraphQL API), and upon loading the data, the app will load and preprocess the data for display and computation.  Once data is loaded and prepare, main_window opens and this event will close the initial ope_file dialog.  &lt;br /&gt;
&lt;br /&gt;
The main window contains two tabs: one for metrics summary statistics and one for ranking users (returned by the query or data file) for each metric. Next to each metric in the summary statistics tab, users will have the option to visualize the metric.  Additionally, the main window contains a menu option that allows loading another file (or performing another query).  This action will close the report and visualization of current data and load up the new data.&lt;br /&gt;
&lt;br /&gt;
The visualization window contains two tabs: one for box plot and one for histogram.  These visualizations provide graphical insights into the chosen metric.&lt;br /&gt;
&lt;br /&gt;
[[File: G2335_app_flow.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate  ## mac and linux&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; venv\Scripts\activate  ## windows&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Run the Application ====&lt;br /&gt;
From within the virtual environment in the root directory (PYQT_UI) run the following command to start the application:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;python run.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
&lt;br /&gt;
===== GUI =====&lt;br /&gt;
We implemented the graphical user interface of our application using PyQt6. Our interface consists of three main windows:&lt;br /&gt;
&lt;br /&gt;
* File Browser Window -- the interface allowing the user to browse and load an input CSV file&lt;br /&gt;
* Report Window -- the interface showing two tabs, one for summary statistics for all metrics and one for the lowest ranked users in each metric &lt;br /&gt;
* Metric Detail Window -- the interface showing two tabs, one for an boxplot of the selected metric data, and one for a histogram of the selected metric data&lt;br /&gt;
&lt;br /&gt;
We constructed our three windows using the Qt Designer [https://build-system.fman.io/qt-designer-download] application which allows the laying out and building of a user interface via graphical drag-and-drop methods. The output of Qt Designer are .ui files which are an XML format file that describes the user interface elements and properties.&lt;br /&gt;
&lt;br /&gt;
With the .ui files, there are two methods to using them within the PyQt library:&lt;br /&gt;
* Generating python class source code directly from the .ui file(s) for insertion into the application or,&lt;br /&gt;
* Reading the .ui files in directly during the execution of the python application which constructs the GUI objects on the fly at runtime.&lt;br /&gt;
&lt;br /&gt;
Since the application is small and performance with our sample data was not an issue, we elected for the second method to generate the GUI objects on the fly. Additionally we are able to version the XML .ui files and directly tweak elements within the XML to manipulate the interface.&lt;br /&gt;
&lt;br /&gt;
Each of our windows has a python class and a paired .ui file that is loaded by the class when it is instantiated. An example of this from the FileWindow class within app/gui/file_window.py:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;
  DESIGNER_OPEN_FILE = os.path.join(ROOT_DIR,&amp;quot;designer&amp;quot;,&amp;quot;file_window.ui&amp;quot;)&lt;br /&gt;
  self.ui = uic.loadUi(DESIGNER_OPEN_FILE, self)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From there the ui objects can be referenced to hook up interaction events with the rest of the application. Continuing the above example, the file browse and load buttons are hooked to downstream browseFile() and openReport() functions:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  self.ui.browseButton.clicked.connect(self.browseFile)&lt;br /&gt;
  self.ui.loadFileButton.clicked.connect(self.openReport)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to our three main windows, we use a singleton class called Main (app/gui/main.py) that is responsible for all programmatic opening/closing of windows such that a single version of the current state of the application is kept by that object. Instantiated window objects have a pointer to this Main class which allows windows to interact with one another, for instance to single the report window to open once the file window has loaded a CSV.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== File browsing/loading and summary statistics tables =====&lt;br /&gt;
The app starts with a browse/load file dialog box that prompts the user to provide data for the report.  ''Browse'' button opens file explorer on the user's machine and allows the user to search for the file they need.  Based on how the data is organized behind the scene and the tools we're currently using, our app only accepts files with CSV extension.  Our app accesses the machine's file explorer system and safeguards the file type using the following code.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## browse csv file using file explorer&lt;br /&gt;
  def browseFile(self):&lt;br /&gt;
      filename = QFileDialog.getOpenFileName(self, 'Open File', '.', 'CSV Files (*.csv)')&lt;br /&gt;
      self.filename = filename[0]&lt;br /&gt;
      self.ui.pathToFileTextbox.setText(self.filename)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The text box next to ''browse'' button display the path of the chosen file on the user's machine.  This text box will not allow the user to manually enter the path to the file or edit the path of the file browsed.  This is to avoid human error while interacting with the file's path. The load function of the ''open_file'' window also further checks the validity of the path using the following code.  If the path of a file is invalid, a dialog box will pop up to notify the user as such.  Note that, the validity of the path to a file is not checked until the ''load'' button is clicked.  This is to prevent situations in which the user browses a valid file through the machine's file explorer, but the file, under whatever circumstances, gets deleted between after browsing is done and before loading is finished.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## check the validity of user-entered filename&lt;br /&gt;
  def _isValidFile(self):&lt;br /&gt;
      return self.filename and os.path.isfile(self.filename) and self.filename[-4:] == '.csv'&lt;br /&gt;
&lt;br /&gt;
  ## load csv file &lt;br /&gt;
  def loadFile(self):&lt;br /&gt;
      if not self._isValidFile():&lt;br /&gt;
          return self.raiseMissingFilenameError()&lt;br /&gt;
      metric_constructor = Metrics(self.filename)&lt;br /&gt;
      metric_constructor.constructMetricsTable()&lt;br /&gt;
      self.data = metric_constructor.get_datatable()&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Behind the scene, the ''load'' button also performs the following functionalities upon activation and before the main window loads&lt;br /&gt;
* Computes the metric values (columns) for each user (row) [1]&lt;br /&gt;
* Compute summary statistics for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Compute user ranking for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Organize the summary statistics into pandas dataframe for display &lt;br /&gt;
* Organize the ranking data into pandas dataframe for display&lt;br /&gt;
&lt;br /&gt;
===== Visualization =====&lt;br /&gt;
''' Todo: ''' add visualization implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
&lt;br /&gt;
*Before you run your test make sure you Install Prereq Libraries:&lt;br /&gt;
 &amp;lt;code&amp;gt; pip install -r requirements.txt &amp;lt;/code&amp;gt;&lt;br /&gt;
*To run different test cases:&lt;br /&gt;
 &amp;lt;code&amp;gt; pytest tests/test.py &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left:40px&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Test No !! Test Description !! Expected Output || Test Result&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Test that the browseFile method opens the file dialog || A file dialog should appear and the file name should not be empty || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 1.2 || Test that the loadFile method loads the file correctly || User should be able to load csv file in their device || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Test if the file is valid || User should be able to select a valid CSV file is selected, Invalid files should trigger an error message || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Test getSummaryStatistics function || It verifies that the function returns a DataFrame, if DataFrame has the expected columns and also that the DataFrame has the expected index || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Test getHistogramData function || It verifies if function returns a Series, if series has the expected length and that the values are numeric || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Test constructMetricsTable method || It verifies if the constructMetricsTable method generates the expected data frame with the correct columns and dimensions || PASS&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Details====&lt;br /&gt;
&lt;br /&gt;
*Browse File:&lt;br /&gt;
&lt;br /&gt;
In this test, we verify that the browseFile() method of the FileWindow class opens a file dialog correctly. &lt;br /&gt;
A qtbot fixture is passed as an argument to this fixture. For testing PyQt-based GUI applications, the pytest-qt library provides the qtbot fixture. &lt;br /&gt;
It then asserts that the filename attribute of the FileWindow instance is not empty, indicating that the user has selected a file.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
@pytest.fixture&lt;br /&gt;
def file_window(qtbot):&lt;br /&gt;
# Create a FileWindow instance for testing and show it&lt;br /&gt;
    file_window = FileWindow(None)&lt;br /&gt;
    file_window.show()&lt;br /&gt;
# Test that the browseFile method opens the file dialog&lt;br /&gt;
    with qtbot.waitSignal(file_window.ui.pathToFileTextbox.textChanged):&lt;br /&gt;
        file_window.browseFile()&lt;br /&gt;
        assert file_window.filename != ''&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Load File:&lt;br /&gt;
&lt;br /&gt;
A file can be selected from a local machine and loaded into the application(particularly CSV file). This functionality is essential to the application's primary purpose, which is to analyze data for users.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test that the loadFile method loads the file correctly&lt;br /&gt;
    file_window.loadFile()&lt;br /&gt;
    assert file_window.data is not None&lt;br /&gt;
    qtbot.addWidget(file_window)&lt;br /&gt;
    return file_window&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Valid / Invalid files:&lt;br /&gt;
&lt;br /&gt;
The first test function, &amp;quot;test_is_valid_file&amp;quot;, checks if a CSV file exists by mocking the &amp;quot;isfile&amp;quot; function from the &amp;quot;os.path&amp;quot; module. And the second test function, &amp;quot;test_invalid_file&amp;quot;, checks if a file with a TXT format does not exist by mocking the &amp;quot;isfile&amp;quot; function to always return false. &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_is_valid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is valid&lt;br /&gt;
    filename = &amp;quot;CSV Files (*.csv)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return True&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
   &lt;br /&gt;
&lt;br /&gt;
def test_invalid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is invalid&lt;br /&gt;
    filename = &amp;quot;TXT Files (*.txt)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return False&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getSummaryStatistics:&lt;br /&gt;
&lt;br /&gt;
This test determines whether a given input dataset's summary statistics, such as the median, 25th percentile, mean, 75th percentile, standard deviation, minimum, and maximum, are accurately computed by the getSummaryStatistics function. It makes use of NumPy and Pandas for creating a random dataset, which it then gives to the getSummaryStatistics method. The test verifies whether a Pandas DataFrame with the anticipated columns and index is properly returned by the function.&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_getSummaryStatistics():&lt;br /&gt;
    # Test that the function returns a DataFrame&lt;br /&gt;
    result = getSummaryStatistics(data)&lt;br /&gt;
    assert isinstance(result, pd.DataFrame)&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected columns&lt;br /&gt;
    expected_columns = ['Median', '25th', 'Mean', '75th', 'Stdev', 'Min', 'Max']&lt;br /&gt;
    assert result.columns.tolist() == expected_columns&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected index&lt;br /&gt;
    expected_index = data.columns.tolist()&lt;br /&gt;
    assert result.index.tolist() == expected_index&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getHistogramData:&lt;br /&gt;
&lt;br /&gt;
This test checks whether a particular input dataset and metric has been utilized by the getHistogramData method to compute histogram data correctly. Using NumPy and Pandas, the program generates a random dataset and chooses the first column in that dataset as the metric. Following that, this dataset and metric are passed to the getHistogramData function. The function's accuracy in returning a Pandas Series with the desired length and data type is checked in this test. It also tests that the values are numeric.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
def test_getHistogramData():&lt;br /&gt;
    # Test that the function returns a Series&lt;br /&gt;
    metric = data.columns[0]&lt;br /&gt;
    result = getHistogramData(data, metric)&lt;br /&gt;
    assert isinstance(result, pd.Series)&lt;br /&gt;
&lt;br /&gt;
    # Test that the Series has the expected length&lt;br /&gt;
    expected_length = len(data)&lt;br /&gt;
    assert len(result) == expected_length&lt;br /&gt;
&lt;br /&gt;
    # Test that the values are numeric&lt;br /&gt;
    assert result.dtype == 'float64'&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*constructMetricsTable:&lt;br /&gt;
&lt;br /&gt;
This test validates whether the Metrics class' constructMetricsTable method correctly creates a Pandas DataFrame from the imported data. It first creates a Metrics object using the metrics_obj fixture mentioned above before invoking the constructMetricsTable function. The resulting Pandas DataFrame is tested to determine if the intended column names and shape are there.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test for constructMetricsTable method&lt;br /&gt;
def test_constructMetricsTable(metrics_obj):&lt;br /&gt;
    # Test the output of the method&lt;br /&gt;
    metrics_obj.constructMetricsTable()&lt;br /&gt;
    df = metrics_obj.get_datatable()&lt;br /&gt;
    assert 'lifespan' in df.columns&lt;br /&gt;
    assert 'A' in df.columns&lt;br /&gt;
    assert 'B' in df.columns&lt;br /&gt;
    assert 'C' in df.columns&lt;br /&gt;
    assert 'D' in df.columns&lt;br /&gt;
    assert 'E' in df.columns&lt;br /&gt;
    assert 'F' in df.columns&lt;br /&gt;
    assert 'G' in df.columns&lt;br /&gt;
    assert 'H' in df.columns&lt;br /&gt;
    assert 'I' in df.columns&lt;br /&gt;
    assert 'J' in df.columns&lt;br /&gt;
    assert 'K' in df.columns&lt;br /&gt;
    assert 'L' in df.columns&lt;br /&gt;
    assert 'M' in df.columns&lt;br /&gt;
    assert df.shape == (52, 15)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Test Result ===&lt;br /&gt;
[[File:TestResult.png |1000px|]]&lt;br /&gt;
&lt;br /&gt;
== End point summary ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case, the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
Additionally, our current code reads and processes .ui files (which are XML files of our app interface generated by the Qt Designer app) and then adds functions to the UI using Python code.  The fact that our code has to read and process XML files first slows down our app on start-up.  In the future, this could be avoided by converting the XML files into Python codes and objects and work with them directly without having to read and process the .ui files&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150101</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=150101"/>
		<updated>2023-04-25T00:35:06Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: added info about how GUI is constructed&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Definition&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface. The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_app_workflow_updated.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== App workflow ===&lt;br /&gt;
The visualization below presents the general workflow of our app.  &lt;br /&gt;
&lt;br /&gt;
Upon launch, the app starts with a open_file window dialog box which prompts user to load a local file (or enter query input, in the future).  The open_file window allows file browsing (in the future, query to GitHub GraphQL API), and upon loading the data, the app will load and preprocess the data for display and computation.  Once data is loaded and prepare, main_window opens and this event will close the initial ope_file dialog.  &lt;br /&gt;
&lt;br /&gt;
The main window contains two tabs: one for metrics summary statistics and one for ranking users (returned by the query or data file) for each metric. Next to each metric in the summary statistics tab, users will have the option to visualize the metric.  Additionally, the main window contains a menu option that allows loading another file (or performing another query).  This action will close the report and visualization of current data and load up the new data.&lt;br /&gt;
&lt;br /&gt;
The visualization window contains two tabs: one for box plot and one for histogram.  These visualizations provide graphical insights into the chosen metric.&lt;br /&gt;
&lt;br /&gt;
[[File: G2335_app_flow.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate  ## mac and linux&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; venv\Scripts\activate  ## windows&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
&lt;br /&gt;
===== GUI =====&lt;br /&gt;
We implemented the graphical user interface of our application using PyQt6. Our interface consists of three main windows:&lt;br /&gt;
&lt;br /&gt;
* File Browser Window -- the interface allowing the user to browse and load an input CSV file&lt;br /&gt;
* Report Window -- the interface showing two tabs, one for summary statistics for all metrics and one for the lowest ranked users in each metric &lt;br /&gt;
* Metric Detail Window -- the interface showing two tabs, one for an boxplot of the selected metric data, and one for a histogram of the selected metric data&lt;br /&gt;
&lt;br /&gt;
We constructed our three windows using the Qt Designer [https://build-system.fman.io/qt-designer-download] application which allows the laying out and building of a user interface via graphical drag-and-drop methods. The output of Qt Designer are .ui files which are an XML format file that describes the user interface elements and properties.&lt;br /&gt;
&lt;br /&gt;
With the .ui files, there are two methods to using them within the PyQt library:&lt;br /&gt;
* Generating python class source code directly from the .ui file(s) for insertion into the application or,&lt;br /&gt;
* Reading the .ui files in directly during the execution of the python application which constructs the GUI objects on the fly at runtime.&lt;br /&gt;
&lt;br /&gt;
Since the application is small and performance with our sample data was not an issue, we elected for the second method to generate the GUI objects on the fly. Additionally we are able to version the XML .ui files and directly tweak elements within the XML to manipulate the interface.&lt;br /&gt;
&lt;br /&gt;
Each of our windows has a python class and a paired .ui file that is loaded by the class when it is instantiated. An example of this from the FileWindow class within app/gui/file_window.py:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ROOT_DIR = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;
  DESIGNER_OPEN_FILE = os.path.join(ROOT_DIR,&amp;quot;designer&amp;quot;,&amp;quot;file_window.ui&amp;quot;)&lt;br /&gt;
  self.ui = uic.loadUi(DESIGNER_OPEN_FILE, self)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
From there the ui objects can be referenced to hook up interaction events with the rest of the application. Continuing the above example, the file browse and load buttons are hooked to downstream browseFile() and openReport() functions:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  self.ui.browseButton.clicked.connect(self.browseFile)&lt;br /&gt;
  self.ui.loadFileButton.clicked.connect(self.openReport)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition to our three main windows, we use a singleton class called Main (app/gui/main.py) that is responsible for all programmatic opening/closing of windows such that a single version of the current state of the application is kept by that object. Instantiated window objects have a pointer to this Main class which allows windows to interact with one another, for instance to single the report window to open once the file window has loaded a CSV.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===== File browsing/loading and summary statistics tables =====&lt;br /&gt;
The app starts with a browse/load file dialog box that prompts the user to provide data for the report.  ''Browse'' button opens file explorer on the user's machine and allows the user to search for the file they need.  Based on how the data is organized behind the scene and the tools we're currently using, our app only accepts files with CSV extension.  Our app accesses the machine's file explorer system and safeguards the file type using the following code.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## browse csv file using file explorer&lt;br /&gt;
  def browseFile(self):&lt;br /&gt;
      filename = QFileDialog.getOpenFileName(self, 'Open File', '.', 'CSV Files (*.csv)')&lt;br /&gt;
      self.filename = filename[0]&lt;br /&gt;
      self.ui.pathToFileTextbox.setText(self.filename)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The text box next to ''browse'' button display the path of the chosen file on the user's machine.  This text box will not allow the user to manually enter the path to the file or edit the path of the file browsed.  This is to avoid human error while interacting with the file's path. The load function of the ''open_file'' window also further checks the validity of the path using the following code.  If the path of a file is invalid, a dialog box will pop up to notify the user as such.  Note that, the validity of the path to a file is not checked until the ''load'' button is clicked.  This is to prevent situations in which the user browses a valid file through the machine's file explorer, but the file, under whatever circumstances, gets deleted between after browsing is done and before loading is finished.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  ## check the validity of user-entered filename&lt;br /&gt;
  def _isValidFile(self):&lt;br /&gt;
      return self.filename and os.path.isfile(self.filename) and self.filename[-4:] == '.csv'&lt;br /&gt;
&lt;br /&gt;
  ## load csv file &lt;br /&gt;
  def loadFile(self):&lt;br /&gt;
      if not self._isValidFile():&lt;br /&gt;
          return self.raiseMissingFilenameError()&lt;br /&gt;
      metric_constructor = Metrics(self.filename)&lt;br /&gt;
      metric_constructor.constructMetricsTable()&lt;br /&gt;
      self.data = metric_constructor.get_datatable()&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Behind the scene, the ''load'' button also performs the following functionalities upon activation and before the main window loads&lt;br /&gt;
* Computes the metric values (columns) for each user (row) [1]&lt;br /&gt;
* Compute summary statistics for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Compute user ranking for each metric using the data resulted from computation in [1]&lt;br /&gt;
* Organize the summary statistics into pandas dataframe for display &lt;br /&gt;
* Organize the ranking data into pandas dataframe for display&lt;br /&gt;
&lt;br /&gt;
===== Visualization =====&lt;br /&gt;
''' Todo: ''' add visualization implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
&lt;br /&gt;
*Before you run your test make sure you Install Prereq Libraries:&lt;br /&gt;
 &amp;lt;code&amp;gt; pip install -r requirements.txt &amp;lt;/code&amp;gt;&lt;br /&gt;
*To run different test cases:&lt;br /&gt;
 &amp;lt;code&amp;gt; pytest tests/test.py &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin-left:40px&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Test No !! Test Description !! Expected Output || Test Result&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Test that the browseFile method opens the file dialog || A file dialog should appear and the file name should not be empty || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 1.2 || Test that the loadFile method loads the file correctly || User should be able to load csv file in their device || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Test if the file is valid || User should be able to select a valid CSV file is selected, Invalid files should trigger an error message || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Test getSummaryStatistics function || It verifies that the function returns a DataFrame, if DataFrame has the expected columns and also that the DataFrame has the expected index || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Test getHistogramData function || It verifies if function returns a Series, if series has the expected length and that the values are numeric || PASS&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Test constructMetricsTable method || It verifies if the constructMetricsTable method generates the expected data frame with the correct columns and dimensions || PASS&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Details====&lt;br /&gt;
&lt;br /&gt;
*Browse File:&lt;br /&gt;
&lt;br /&gt;
In this test, we verify that the browseFile() method of the FileWindow class opens a file dialog correctly. &lt;br /&gt;
A qtbot fixture is passed as an argument to this fixture. For testing PyQt-based GUI applications, the pytest-qt library provides the qtbot fixture. &lt;br /&gt;
It then asserts that the filename attribute of the FileWindow instance is not empty, indicating that the user has selected a file.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
@pytest.fixture&lt;br /&gt;
def file_window(qtbot):&lt;br /&gt;
# Create a FileWindow instance for testing and show it&lt;br /&gt;
    file_window = FileWindow(None)&lt;br /&gt;
    file_window.show()&lt;br /&gt;
# Test that the browseFile method opens the file dialog&lt;br /&gt;
    with qtbot.waitSignal(file_window.ui.pathToFileTextbox.textChanged):&lt;br /&gt;
        file_window.browseFile()&lt;br /&gt;
        assert file_window.filename != ''&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Load File:&lt;br /&gt;
&lt;br /&gt;
A file can be selected from a local machine and loaded into the application(particularly CSV file). This functionality is essential to the application's primary purpose, which is to analyze data for users.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test that the loadFile method loads the file correctly&lt;br /&gt;
    file_window.loadFile()&lt;br /&gt;
    assert file_window.data is not None&lt;br /&gt;
    qtbot.addWidget(file_window)&lt;br /&gt;
    return file_window&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Valid / Invalid files:&lt;br /&gt;
&lt;br /&gt;
The first test function, &amp;quot;test_is_valid_file&amp;quot;, checks if a CSV file exists by mocking the &amp;quot;isfile&amp;quot; function from the &amp;quot;os.path&amp;quot; module. And the second test function, &amp;quot;test_invalid_file&amp;quot;, checks if a file with a TXT format does not exist by mocking the &amp;quot;isfile&amp;quot; function to always return false. &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_is_valid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is valid&lt;br /&gt;
    filename = &amp;quot;CSV Files (*.csv)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return True&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
   &lt;br /&gt;
&lt;br /&gt;
def test_invalid_file(qtbot, file_window, monkeypatch):&lt;br /&gt;
    # Test if the file is invalid&lt;br /&gt;
    filename = &amp;quot;TXT Files (*.txt)&amp;quot;&lt;br /&gt;
    file_window.filename = filename&lt;br /&gt;
    def mock_return(*args, **kwargs):&lt;br /&gt;
        return False&lt;br /&gt;
    monkeypatch.setattr(os.path, &amp;quot;isfile&amp;quot;, mock_return)&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getSummaryStatistics:&lt;br /&gt;
&lt;br /&gt;
This test determines whether a given input dataset's summary statistics, such as the median, 25th percentile, mean, 75th percentile, standard deviation, minimum, and maximum, are accurately computed by the getSummaryStatistics function. It makes use of NumPy and Pandas for creating a random dataset, which it then gives to the getSummaryStatistics method. The test verifies whether a Pandas DataFrame with the anticipated columns and index is properly returned by the function.&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
def test_getSummaryStatistics():&lt;br /&gt;
    # Test that the function returns a DataFrame&lt;br /&gt;
    result = getSummaryStatistics(data)&lt;br /&gt;
    assert isinstance(result, pd.DataFrame)&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected columns&lt;br /&gt;
    expected_columns = ['Median', '25th', 'Mean', '75th', 'Stdev', 'Min', 'Max']&lt;br /&gt;
    assert result.columns.tolist() == expected_columns&lt;br /&gt;
&lt;br /&gt;
    # Test that the DataFrame has the expected index&lt;br /&gt;
    expected_index = data.columns.tolist()&lt;br /&gt;
    assert result.index.tolist() == expected_index&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* getHistogramData:&lt;br /&gt;
&lt;br /&gt;
This test checks whether a particular input dataset and metric has been utilized by the getHistogramData method to compute histogram data correctly. Using NumPy and Pandas, the program generates a random dataset and chooses the first column in that dataset as the metric. Following that, this dataset and metric are passed to the getHistogramData function. The function's accuracy in returning a Pandas Series with the desired length and data type is checked in this test. It also tests that the values are numeric.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
def test_getHistogramData():&lt;br /&gt;
    # Test that the function returns a Series&lt;br /&gt;
    metric = data.columns[0]&lt;br /&gt;
    result = getHistogramData(data, metric)&lt;br /&gt;
    assert isinstance(result, pd.Series)&lt;br /&gt;
&lt;br /&gt;
    # Test that the Series has the expected length&lt;br /&gt;
    expected_length = len(data)&lt;br /&gt;
    assert len(result) == expected_length&lt;br /&gt;
&lt;br /&gt;
    # Test that the values are numeric&lt;br /&gt;
    assert result.dtype == 'float64'&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*constructMetricsTable:&lt;br /&gt;
&lt;br /&gt;
This test validates whether the Metrics class' constructMetricsTable method correctly creates a Pandas DataFrame from the imported data. It first creates a Metrics object using the metrics_obj fixture mentioned above before invoking the constructMetricsTable function. The resulting Pandas DataFrame is tested to determine if the intended column names and shape are there.&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# Test for constructMetricsTable method&lt;br /&gt;
def test_constructMetricsTable(metrics_obj):&lt;br /&gt;
    # Test the output of the method&lt;br /&gt;
    metrics_obj.constructMetricsTable()&lt;br /&gt;
    df = metrics_obj.get_datatable()&lt;br /&gt;
    assert 'lifespan' in df.columns&lt;br /&gt;
    assert 'A' in df.columns&lt;br /&gt;
    assert 'B' in df.columns&lt;br /&gt;
    assert 'C' in df.columns&lt;br /&gt;
    assert 'D' in df.columns&lt;br /&gt;
    assert 'E' in df.columns&lt;br /&gt;
    assert 'F' in df.columns&lt;br /&gt;
    assert 'G' in df.columns&lt;br /&gt;
    assert 'H' in df.columns&lt;br /&gt;
    assert 'I' in df.columns&lt;br /&gt;
    assert 'J' in df.columns&lt;br /&gt;
    assert 'K' in df.columns&lt;br /&gt;
    assert 'L' in df.columns&lt;br /&gt;
    assert 'M' in df.columns&lt;br /&gt;
    assert df.shape == (52, 15)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
&lt;br /&gt;
=== Test Result ===&lt;br /&gt;
[[File:TestResult.png |1000px|]]&lt;br /&gt;
&lt;br /&gt;
== End point summary ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case, the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
Additionally, our current code reads and processes .ui files (which are XML files of our app interface generated by the Qt Designer app) and then adds functions to the UI using Python code.  The fact that our code has to read and process XML files first slows down our app on start-up.  In the future, this could be avoided by converting the XML files into Python codes and objects and work with them directly without having to read and process the .ui files&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149619</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149619"/>
		<updated>2023-04-13T00:24:55Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* PyQt Interface */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
&lt;br /&gt;
Our test plan can be broken into two main pieces -- testing our 'backend' data ingestion and processing classes/methods, and testing the PyQt user interface classes. The overall plan and libraries used for each are described below.&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
As we implement our application and associated test, we will indicate how to execute the testing framework(s) here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
As we finalize development of the application and associated tests, we will indicate our application ultimate test coverage and where remaining work could be done to enhance testing.&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149618</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149618"/>
		<updated>2023-04-13T00:24:36Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
&lt;br /&gt;
Our test plan can be broken into two main pieces -- testing our 'backend' data ingestion and processing classes/methods, and testing the PyQt user interface classes. The overall plan and libraries used for each are described below.&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
See also: * [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
As we implement our application and associated test, we will indicate how to execute the testing framework(s) here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
As we finalize development of the application and associated tests, we will indicate our application ultimate test coverage and where remaining work could be done to enhance testing.&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149612</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149612"/>
		<updated>2023-04-13T00:23:49Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Test results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
&lt;br /&gt;
Our test plan can be broken into two main pieces -- testing our 'backend' data ingestion and processing classes/methods, and testing the PyQt user interface classes. The overall plan and libraries used for each are described below.&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
=== Test execution ===&lt;br /&gt;
As we implement our application and associated test, we will indicate how to execute the testing framework(s) here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
As we finalize development of the application and associated tests, we will indicate our application ultimate test coverage and where remaining work could be done to enhance testing.&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149608</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149608"/>
		<updated>2023-04-13T00:21:58Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Test plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
&lt;br /&gt;
Our test plan can be broken into two main pieces -- testing our 'backend' data ingestion and processing classes/methods, and testing the PyQt user interface classes. The overall plan and libraries used for each are described below.&lt;br /&gt;
&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149603</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149603"/>
		<updated>2023-04-13T00:19:38Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Test plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
==== Data Ingestion and Processing ====&lt;br /&gt;
We will use the python [https://docs.python.org/3/library/unittest.html unittest] framework to implement unit testing of classes responsible for reading in the input CSVs and executing the metrics within including the functions supporting boxplot, histogram, and basic statistics generation/calculations.&lt;br /&gt;
&lt;br /&gt;
==== PyQt Interface ====&lt;br /&gt;
We will use the [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt] package to implement our testing of the user interface. We expect that given the nature of UI testing, our tests may not be fully comprehensive of all situations/environments, but we will attempt to test all major events within the UI (e.g., button presses triggering appropriate actions, data appearing correctly in tables).&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149599</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149599"/>
		<updated>2023-04-13T00:12:18Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Future Work */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
Keeping the future direction in mind, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149598</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149598"/>
		<updated>2023-04-13T00:11:45Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Future Direction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149596</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149596"/>
		<updated>2023-04-13T00:11:08Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Topic Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
This project will ultimately be used in conjunction with future work to pull metrics directly from GitHub APIs based on a given set of GitHub usernames. As such, we will design and build the UI to enable as clean as possible of a switch between loading the data from CSV to loading the data via API. One such area of potential reuse/repurpose would be the portions of the application (GUI, controller, and model classes) responsible for taking in an input file -- in our case the data itself will be ingested, but in the future state, a CSV of GitHub usernames may serve as the entry point.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python (v3)] to compute statistics from raw data&lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Project Setup and Application Execution ===&lt;br /&gt;
To set up and execute this project follow the instructions below:&lt;br /&gt;
&lt;br /&gt;
*Create a new virtual environment&lt;br /&gt;
*Enter the virtual environment&lt;br /&gt;
*Install prerequisite Python libraries&lt;br /&gt;
&lt;br /&gt;
===== Create Virtual Environment =====&lt;br /&gt;
*It is recommended to create a python3 virtual environment to set up and run this application within. To create a virtual environment, from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; python3 -m venv ./venv &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Enter the virtual environment by running:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt; source venv/bin/activate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*You can exit the virtual environment at any time by running:&lt;br /&gt;
 &amp;lt;code&amp;gt; deactivate &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Install Prereq Libraries ====&lt;br /&gt;
Once within the virtual environment, at initial setup, the prereq Python libraries need to be installed within the environment. To do this run the following from the root of this project:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;pip install -r requirements.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149256</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149256"/>
		<updated>2023-04-08T01:44:30Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Mockup Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file (1), loads and processes the data from the csv into tables for summary stats (2) and the lowest ranked users (3) for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot (4) and histogram (5) for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== 1. Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
==== 2. Summary Stats ====&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
==== 3. Lowest Rank ====&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
==== 4. Boxplot ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
==== 5. Histogram ====&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python] to compute statistics from raw data, &lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149255</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149255"/>
		<updated>2023-04-08T01:42:20Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Mockup Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
The mockups below were created using Qt Designer, a graphical interface builder that produces XML-based mockups that can be converted into working python-based pyQt code.&lt;br /&gt;
&lt;br /&gt;
The current design consists of a desktop application that allows the user to browse for and select a csv-based data file, loads and processes the data from the csv into tables for summary stats and the lowest ranked users for each metric. On the summary stats table, the user can select the chart icon to bring up a popup that has details about the metric and tabs to show a boxplot and histogram for the selected metric.  &lt;br /&gt;
&lt;br /&gt;
==== Open File ====&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python] to compute statistics from raw data, &lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149249</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149249"/>
		<updated>2023-04-08T01:26:49Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Mockup Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
'''Todo:''' Include mockup design for the UI&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_openfile.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_table_lowest_ranked.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_chart_boxplot.png]]&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_chart_histogram.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python] to compute statistics from raw data, &lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_openfile.png&amp;diff=149248</id>
		<title>File:G2335 - mockup openfile.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_openfile.png&amp;diff=149248"/>
		<updated>2023-04-08T01:26:11Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: Mockup showing file open dialogue&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mockup showing file open dialogue&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_chart_histogram.png&amp;diff=149246</id>
		<title>File:G2335 - mockup chart histogram.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_chart_histogram.png&amp;diff=149246"/>
		<updated>2023-04-08T01:24:35Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: Mockup of chart popup showing histogram&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mockup of chart popup showing histogram&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_chart_boxplot.png&amp;diff=149244</id>
		<title>File:G2335 - mockup chart boxplot.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_chart_boxplot.png&amp;diff=149244"/>
		<updated>2023-04-08T01:20:58Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: Mockup of chart popup showing matplotlib generated boxplot&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mockup of chart popup showing matplotlib generated boxplot&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_table_lowest_ranked.png&amp;diff=149243</id>
		<title>File:G2335 - mockup table lowest ranked.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_table_lowest_ranked.png&amp;diff=149243"/>
		<updated>2023-04-08T01:20:14Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: Mockup table showing lowest ranked users&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mockup table showing lowest ranked users&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149240</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=149240"/>
		<updated>2023-04-08T01:16:44Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Mockup Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Topic Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Numerical data are important in providing insights. However, in raw format, numerical data are hard to grasp and introduce more confusion than information. Graphical presentations of numerical data are needed to draw insights and focus from the available data. &lt;br /&gt;
&lt;br /&gt;
'''For this project, we are developing the front-end user interface for GraphQL query for a desktop application to display summary statistics of Github repository and Github users' contribution to said repository.''' Github provides [https://docs.github.com/en/graphql GraphQL API] for developers who are interested in the raw data surrounding each Github repository and user. Such data can be used to develop metrics to monitor a repository's activities, performance, community involvement, community usage, and contribution from the community and collaborators. The main focus of this project is developing a UI to display these metrics and summary statistics in graphical and tabular formats to provide clients with insights into their own Github repository or any Github repository they are interested in. &lt;br /&gt;
&lt;br /&gt;
Our interface design will be developed from scratch based on client requirements ([https://expertiza.csc.ncsu.edu/index.php/CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query#Feature_Requirements project requirements]). We aim to make this design and interface a building block and sound foundation for future projects that want to extend our use case or build similar applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
As our project focuses on the design and front-end interface of the application, back-end calls to fetch and methods organize data from Github GraphQL API will not be handled. Instead, the data will be stubbed using pre-process datasets in CSV files. These files are the products of data fetched from Github API and organized for ease of accessing and rapid development of the front-end.&lt;br /&gt;
&lt;br /&gt;
Additionally, as the interface will be developed from scratch and the time allotted for this project, we will be focusing on basic displays and graphs of the data.&lt;br /&gt;
&lt;br /&gt;
== Feature Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Raw Data ===&lt;br /&gt;
The raw data we are working with contain these fields&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! Metrics !! Column Names !! Explanation&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&amp;lt;code&amp;gt;lifeSpan&amp;lt;/code&amp;gt;&lt;br /&gt;
|Days of Github experience before class.&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&amp;lt;code&amp;gt;commitContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of private and public commits made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|&amp;lt;code&amp;gt;commitComments, issueComments, gistComments, repositoryDiscussionComments, repositoryDiscussions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of comments made by this user in commits, issues, and pull request discussions.&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|&amp;lt;code&amp;gt;pullRequests, pullRequestReviewContributions&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of PR and PR reviews made by this user.&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|&amp;lt;code&amp;gt;issues, projects&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of issues, and projects created by this user.&lt;br /&gt;
|-&lt;br /&gt;
|E&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagecount + Clanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|&amp;lt;code&amp;gt;Alanguagesizet + Clanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|G&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagecount + Dlanguagecount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The number of different languages used in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|H&lt;br /&gt;
|&amp;lt;code&amp;gt;Blanguagesizet + Dlanguagesize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The size of code written in Github popular languages in user's type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|I&lt;br /&gt;
|&amp;lt;code&amp;gt;repoACount, repoBCount, repoCCount, repoDCount&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of type I, and type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|J&lt;br /&gt;
|&amp;lt;code&amp;gt;forkACount, stargazerACount, Awatchers, forkCCount, stargazerCCount, Cwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|K&lt;br /&gt;
|&amp;lt;code&amp;gt; forkBCount, stargazerBCount, Bwatchers, forkDCount, stargazerDCount, Dwatchers&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total number of forks, stars, and watchers in type II repositories.&lt;br /&gt;
|-&lt;br /&gt;
|L&lt;br /&gt;
|&amp;lt;code&amp;gt;repoASize, repoCSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type I repositories.&lt;br /&gt;
|-&lt;br /&gt;
|M&lt;br /&gt;
|&amp;lt;code&amp;gt;repoBSize, repoDSize&amp;lt;/code&amp;gt;&lt;br /&gt;
|The total code size of type II repositories.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
The followings are the features that the desktop application is required to have&lt;br /&gt;
* A table for basic statistics ((median, 25th, mean, 75th percentile, stdev) in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! median!! 25th !! mean !! 75th !! stdev&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A table for the lowest ranked 1/5 username in the following format&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: 100%;&lt;br /&gt;
! !! 1st!! 2nd !! 3rd !! 4th !! 5th&lt;br /&gt;
|-&lt;br /&gt;
|Days&lt;br /&gt;
|USER 10&lt;br /&gt;
|USER 2&lt;br /&gt;
|USER 6&lt;br /&gt;
|USER 18&lt;br /&gt;
|USER 21&lt;br /&gt;
|-&lt;br /&gt;
|A&lt;br /&gt;
|USER 5&lt;br /&gt;
|USER 1&lt;br /&gt;
|USER 13&lt;br /&gt;
|USER 22&lt;br /&gt;
|USER 19&lt;br /&gt;
|-&lt;br /&gt;
|...''other metrics''...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|...&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* A boxplot for each of the metrics listed&lt;br /&gt;
* A histogram for each of the metrics listed&lt;br /&gt;
&lt;br /&gt;
== Interface Design ==&lt;br /&gt;
&lt;br /&gt;
=== User Interaction ===&lt;br /&gt;
&lt;br /&gt;
With the GUI of our application, we aim to create an interactive workflow where users can drill into each metric, graph, and summary statistics table by clicking. Thus, prevent a ton of scrolling or production of unnecessary charts that flood the interface.  Additionally, if users choose to have an extensive report generated with all the information included, they can. This report will include all the metrics, graphs, and summary statistics tables that users can generate individually.  The following graph shows the workflow of our ideal application design&lt;br /&gt;
&lt;br /&gt;
[[File:g2335_workflow_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Mockup Design ===&lt;br /&gt;
'''Todo:''' Include mockup design for the UI&lt;br /&gt;
&lt;br /&gt;
[[File:G2335_-_mockup_table_summary_stats.png]]&lt;br /&gt;
&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
&lt;br /&gt;
=== Tools ===&lt;br /&gt;
The interface for the desktop application will be developed using &lt;br /&gt;
* [https://www.riverbankcomputing.com/software/pyqt/ pyQt6] to create front-end components, &lt;br /&gt;
* [https://www.python.org/ Python] to compute statistics from raw data, &lt;br /&gt;
* [https://matplotlib.org/ matplotlib] to render graphs&lt;br /&gt;
* [https://build-system.fman.io/qt-designer-download Qt Designer] to mock-up the application UI using a drag-drop interface of pyQt&lt;br /&gt;
&lt;br /&gt;
=== Implementation ===&lt;br /&gt;
'''Todo:''' Inlcude implementation details&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
For this project, we planned to use use the following tools to test the GUI of our application&lt;br /&gt;
* [https://doc.qt.io/qtforpython/overviews/qtest-overview.html Python unit testing for pyQt]&lt;br /&gt;
* [https://pytest-qt.readthedocs.io/en/latest/intro.html pytest-qt]&lt;br /&gt;
&lt;br /&gt;
=== Test plan ===&lt;br /&gt;
'''Todo:''' include testing plans&lt;br /&gt;
&lt;br /&gt;
=== Test results ===&lt;br /&gt;
'''Todo:''' include testing results&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
For this project, our goal is to develop a solid foundation for a desktop application that reports Github data in an informative manner. Our design focuses on displaying the most imperative information to users while avoiding requiring users to engage in complex and unnecessary interactions. We focus on a simple graphical view to avoid overwhelming users with multiple graphs and data. &lt;br /&gt;
&lt;br /&gt;
As a front-end project, our application contains mainly front-end components with a few basic functions behind the scene that do computation and rendering graphs. The backend data pipeline is stubbed using pre-processed CSV files of data fetched manually from Github GraphQL API. However, the connection between the data and the GUI is designed so that the GUI can work with a full-fledged data pipeline. As a result, for future work, we hope to extend our application functionality so that it has a functional backend data pipeline that can take in a user input command to fetch data directly from Github API, process it, and then display information.&lt;br /&gt;
&lt;br /&gt;
Besides having a fully functional application with a backend data pipeline, the following features can be added to the application:&lt;br /&gt;
* Users can combine graphs and compare metrics.&lt;br /&gt;
* Users can compare the performance and metrics of a Github account to another (or multiple other) account(s).&lt;br /&gt;
* Users can manually add texts and annotations to graphs generated by the application.&lt;br /&gt;
* Users can export graphs and reports into pdf or image formats (e.g., PNG, and JPEG)&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Related Links ==&lt;br /&gt;
* [https://github.ncsu.edu/anguyen9/pyqt_UI Github repo] '''NEED TO BE MADE PUBLIC'''&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_table_summary_stats.png&amp;diff=149239</id>
		<title>File:G2335 - mockup table summary stats.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:G2335_-_mockup_table_summary_stats.png&amp;diff=149239"/>
		<updated>2023-04-08T01:15:31Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: Mockup of summary stats panel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Mockup of summary stats panel&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=148723</id>
		<title>CSC/ECE 517 Spring 2023 -G2335. Develop Frontend UI Interface for GraphQL Query</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-G2335._Develop_Frontend_UI_Interface_for_GraphQL_Query&amp;diff=148723"/>
		<updated>2023-04-04T11:50:25Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: page created&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148548</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148548"/>
		<updated>2023-03-28T01:57:11Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* edit */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view here]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft pull request on our [https://github.com/amjacob2/reimplementation-back-end/pull/10 repository]&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Multiple Choice Checkbox]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio Multiple Choice Radio]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse True False]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Reimplementation Details==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid = 'Please select only one correct answer for all questions' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'valid' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
=== complete ===&lt;br /&gt;
&lt;br /&gt;
The complete method was initially not implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, I removed it from its subclass and moved it to the superclass in quiz_question.rb. It generates HTML code for completing a question. The quiz question's associated answer alternatives are displayed and can be selected using radio buttons.&lt;br /&gt;
The implementation of '''complete''' is shown below-&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', &lt;br /&gt;
foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
def complete&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
    html = '&amp;lt;label for=&amp;quot;' + id.to_s + '&amp;quot;&amp;gt;' + txt + '&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;'&lt;br /&gt;
    [0, 1, 2, 3].each do |i|&lt;br /&gt;
      html += '&amp;lt;input name = ' + &amp;quot;\&amp;quot;#{id}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'id = ' + &amp;quot;\&amp;quot;#{id}&amp;quot; + '_' + &amp;quot;#{i + 1}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'value = ' + &amp;quot;\&amp;quot;#{quiz_question_choices[i].txt}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'type=&amp;quot;radio&amp;quot;/&amp;gt;'&lt;br /&gt;
      html += quiz_question_choices[i].txt.to_s&lt;br /&gt;
      html += '&amp;lt;/br&amp;gt;'&lt;br /&gt;
    end&lt;br /&gt;
    html&lt;br /&gt;
  end&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test complete method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
describe &amp;quot;#complete&amp;quot; do&lt;br /&gt;
    before do&lt;br /&gt;
      id = quiz_question.id&lt;br /&gt;
      expected_html = &amp;quot;&amp;lt;label for=\&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot;&amp;gt;Question Text&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_1\&amp;quot; value = \&amp;quot;Choice 1\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 1&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_2\&amp;quot; value = \&amp;quot;Choice 2\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 2&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_3\&amp;quot; value = \&amp;quot;Choice 3\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 3&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_4\&amp;quot; value = \&amp;quot;Choice 4\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 4&amp;lt;/br&amp;gt;&amp;quot;&lt;br /&gt;
      expect(quiz_question.complete).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns the completed HTML for the quiz question&amp;quot; do&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_completed_question ===&lt;br /&gt;
&lt;br /&gt;
The view_completed_question method was also not initially implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, we removed it from its subclass and moved it to the superclass in quiz_question.rb. The HTML code generated by this program is used to check the completed question and also to verify the response of the user. An indication is provided that indicates whether the user's response was right or wrong, and it is highlighted with help of check or delete icon which you can find here-[https://github.com/amjacob2/reimplementation-back-end/tree/main/app/assets/images assets/images].&lt;br /&gt;
&lt;br /&gt;
The implementation of '''view_completed_question''' is shown below-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def view_completed_question(user_answer)&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
&lt;br /&gt;
    html = ''&lt;br /&gt;
    quiz_question_choices.each do |answer|&lt;br /&gt;
      html += if answer.iscorrect&lt;br /&gt;
                '&amp;lt;b&amp;gt;' + answer.txt + '&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;'&lt;br /&gt;
              else&lt;br /&gt;
                answer.txt + '&amp;lt;br&amp;gt;'&lt;br /&gt;
              end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;Your answer is: '&lt;br /&gt;
    html += '&amp;lt;b&amp;gt;' + user_answer.first.comments.to_s + '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += if user_answer.first.answer == 1&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            else&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/delete_icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            end&lt;br /&gt;
    html += '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test view_completed_question method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_completed_question' do&lt;br /&gt;
    it 'returns the correct HTML for a completed question' do&lt;br /&gt;
      quiz_question = QuizQuestion.new(txt: 'which is the latest Iphone?')&lt;br /&gt;
      quiz_question_choice_1 = QuizQuestionChoice.new(txt: 'Iphone14', iscorrect: true)&lt;br /&gt;
      quiz_question_choice_2 = QuizQuestionChoice.new(txt: 'Iphone13', iscorrect: false)&lt;br /&gt;
      quiz_question_choice_3 = QuizQuestionChoice.new(txt: 'Iphone12', iscorrect: false)&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_1&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_2&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_3&lt;br /&gt;
      user_answer = double('user_answer')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :comments).and_return('Iphone14')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :answer).and_return(1)&lt;br /&gt;
            expected_html = '&amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;Iphone13&amp;lt;br&amp;gt;Iphone12&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Your answer is: &amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt;&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
            expect(quiz_question.view_completed_question(user_answer)).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== QuizQuestionChoice ===&lt;br /&gt;
The ''QuizQuestionChoice'' class represents a single choice in a true/false or multiple choice question. A QuizQuestion or QuizQuestion subclass has a 'has_many' relationship with QuizQuestionChoice. In the original implementation, every method that referenced the QuizQuestion's QuizQuestionChoices would do a retrieve -- this single line of code was duplicated multiple times through the four classes.&lt;br /&gt;
&lt;br /&gt;
In order to improve this, we moved the fetching of a QuizQuestion's QuizQuestionChoices into a method that is run after an instance is initialized. From there the QuizQuestionChoices can be referenced as an instance variable instead of having the same fetch referenced over and over.&lt;br /&gt;
&lt;br /&gt;
The referenced method from the new implementation of QuizQuestion:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  after_initialize :load_choices&lt;br /&gt;
  def load_choices&lt;br /&gt;
    @quiz_question_choices = QuizQuestionChoice.where(question_id: id)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Overall Plan ===&lt;br /&gt;
The overall plan is to perform automated RSpec testing of all the methods that have been moved to the superclass, namely ''QuizQuestion''.  These methods are:&lt;br /&gt;
* ''edit''&lt;br /&gt;
* ''view_question_text''&lt;br /&gt;
* ''complete''&lt;br /&gt;
* ''view_completed_question''&lt;br /&gt;
* ''isvalid''&lt;br /&gt;
&lt;br /&gt;
Most tests will test the methods in ''QuizQuestion'' class as the subclasses mainly inherit these methods without extension.  These test will be included in ''quiz_question_spec.rb'' (full path: /spec/requests/model/quiz_question_spec.rb).  Methods that are extended by subclasses will also be tested in a separate spec file.  For example, ''isvalid'' method is fully tested for ''QuizQuestion'' model in ''quiz_question_spec.rb''.  Since ''TrueFalse'' and ''MultipleChoiceRadio'' inherit this method fully from the superclass without extension, no extra test is created to test the method for the two subclasses.  However, ''MultipleChoiceCheckbox'' extended the ''isvalid'' method.  As a result, extra tests are created to test ''isvalid'' for ''MultipleChoiceCheckbox''; these tests are then included in ''multiple_choice_checkbox_spec.rb''&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The goal in our automated testing of the edit function is to ensure that, given a QuizQuestion or QuizQuestion subclass object, the edit method of the class should display the HTML returned for the edit functionality as expected. In each of our Rspec test files, an object of the class under test (QuizQuestion or subclasses) is created to simulate what a similar database-driven object would appear like to our model classes. &lt;br /&gt;
&lt;br /&gt;
As the edit function in each class is slightly different, we implemented specific tests for each of the classes we reimplemented.&lt;br /&gt;
&lt;br /&gt;
* Within ''spec/requests/model/quiz_question_spec.rb'' the edit method is tested to ensure the default question prefix HTML is returned. &lt;br /&gt;
* Within ''spec/requests/model/true_false_spec.rb'', ''spec/requests/model/multiple_choice_radio_spec.rb'', and ''spec/requests/model/multiple_choice_checkbox_spec.rb'' the edit method is tested to ensure given an object filled out with QuizQuestionChoices, question text, question id, and weight the HTML is rendered appropriately.&lt;br /&gt;
&lt;br /&gt;
These tests will fail if the underlying HTML returned from the model changes, which would be indicative of possible negative downstream effects in the application where the QuizQuestion subclasses are used.&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method:&lt;br /&gt;
* Test the return message if the input contains text for the question prompt, text for all question choices, and only one of the choices is correct '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for the question prompt '''(expect &amp;quot;Please make sure all questions have text&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for at least one of the question choices '''(expect &amp;quot;Please make sure every question has text for all options&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides no correct answer '''(expect &amp;quot;Please select a correct answer for all questions&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;Please select only one correct answer for all questions&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
'''isvalid''' is extended in '''MultipleChoiceCheckbox''' as the requirements for '''MultipleChoiceCheckbox''' to be valid are slightly different from those of '''QuizQuestion''', '''TrueFalse''', and '''MultipleChoiceRadio'''&lt;br /&gt;
* Test the return message if the input provides only 1 correct answer '''(expect &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there are more than one correct choices' do&lt;br /&gt;
      it 'returns &amp;quot;Please select only one correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                                  '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select only one correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_question_text ===&lt;br /&gt;
The test for '''view_question_text''' is fairly straight forward as once the question input passes '''isvalid''', we only need to make sure that view_question_text display the information in a predetermined format.  The test for '''view_question_text''' is shown below&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_question_text' do&lt;br /&gt;
    it 'returns the text of the questions' do&lt;br /&gt;
      weight = quiz_question.weight&lt;br /&gt;
      expect(quiz_question.view_question_text).to eq('&amp;lt;b&amp;gt;Question Text&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;Question Type: MultipleChoiceRadio&amp;lt;br /&amp;gt;Question Weight: ' + &lt;br /&gt;
                                                      weight.to_s + '&amp;lt;br /&amp;gt;  - &amp;lt;b&amp;gt;Choice 1&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;   - Choice 2&amp;lt;br /&amp;gt;   - Choice 3&amp;lt;br /&amp;gt;   - &lt;br /&gt;
                                                      Choice 4&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;')&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== RSpec Test Setup ===&lt;br /&gt;
Note that for each test file we included a ''before'' block that setup a QuizQuestion subclass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;br /&gt;
* [https://github.com/expertiza/reimplementation-back-end/pull/6 Pull request]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148546</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148546"/>
		<updated>2023-03-28T01:56:21Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Overall Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view here]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft pull request on our [https://github.com/amjacob2/reimplementation-back-end/pull/10 repository]&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Multiple Choice Checkbox]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio Multiple Choice Radio]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse True False]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Reimplementation Details==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid = 'Please select only one correct answer for all questions' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'valid' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
=== complete ===&lt;br /&gt;
&lt;br /&gt;
The complete method was initially not implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, I removed it from its subclass and moved it to the superclass in quiz_question.rb. It generates HTML code for completing a question. The quiz question's associated answer alternatives are displayed and can be selected using radio buttons.&lt;br /&gt;
The implementation of '''complete''' is shown below-&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', &lt;br /&gt;
foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
def complete&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
    html = '&amp;lt;label for=&amp;quot;' + id.to_s + '&amp;quot;&amp;gt;' + txt + '&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;'&lt;br /&gt;
    [0, 1, 2, 3].each do |i|&lt;br /&gt;
      html += '&amp;lt;input name = ' + &amp;quot;\&amp;quot;#{id}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'id = ' + &amp;quot;\&amp;quot;#{id}&amp;quot; + '_' + &amp;quot;#{i + 1}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'value = ' + &amp;quot;\&amp;quot;#{quiz_question_choices[i].txt}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'type=&amp;quot;radio&amp;quot;/&amp;gt;'&lt;br /&gt;
      html += quiz_question_choices[i].txt.to_s&lt;br /&gt;
      html += '&amp;lt;/br&amp;gt;'&lt;br /&gt;
    end&lt;br /&gt;
    html&lt;br /&gt;
  end&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test complete method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
describe &amp;quot;#complete&amp;quot; do&lt;br /&gt;
    before do&lt;br /&gt;
      id = quiz_question.id&lt;br /&gt;
      expected_html = &amp;quot;&amp;lt;label for=\&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot;&amp;gt;Question Text&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_1\&amp;quot; value = \&amp;quot;Choice 1\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 1&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_2\&amp;quot; value = \&amp;quot;Choice 2\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 2&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_3\&amp;quot; value = \&amp;quot;Choice 3\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 3&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_4\&amp;quot; value = \&amp;quot;Choice 4\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 4&amp;lt;/br&amp;gt;&amp;quot;&lt;br /&gt;
      expect(quiz_question.complete).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns the completed HTML for the quiz question&amp;quot; do&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_completed_question ===&lt;br /&gt;
&lt;br /&gt;
The view_completed_question method was also not initially implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, we removed it from its subclass and moved it to the superclass in quiz_question.rb. The HTML code generated by this program is used to check the completed question and also to verify the response of the user. An indication is provided that indicates whether the user's response was right or wrong, and it is highlighted with help of check or delete icon which you can find here-[https://github.com/amjacob2/reimplementation-back-end/tree/main/app/assets/images assets/images].&lt;br /&gt;
&lt;br /&gt;
The implementation of '''view_completed_question''' is shown below-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def view_completed_question(user_answer)&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
&lt;br /&gt;
    html = ''&lt;br /&gt;
    quiz_question_choices.each do |answer|&lt;br /&gt;
      html += if answer.iscorrect&lt;br /&gt;
                '&amp;lt;b&amp;gt;' + answer.txt + '&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;'&lt;br /&gt;
              else&lt;br /&gt;
                answer.txt + '&amp;lt;br&amp;gt;'&lt;br /&gt;
              end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;Your answer is: '&lt;br /&gt;
    html += '&amp;lt;b&amp;gt;' + user_answer.first.comments.to_s + '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += if user_answer.first.answer == 1&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            else&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/delete_icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            end&lt;br /&gt;
    html += '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test view_completed_question method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_completed_question' do&lt;br /&gt;
    it 'returns the correct HTML for a completed question' do&lt;br /&gt;
      quiz_question = QuizQuestion.new(txt: 'which is the latest Iphone?')&lt;br /&gt;
      quiz_question_choice_1 = QuizQuestionChoice.new(txt: 'Iphone14', iscorrect: true)&lt;br /&gt;
      quiz_question_choice_2 = QuizQuestionChoice.new(txt: 'Iphone13', iscorrect: false)&lt;br /&gt;
      quiz_question_choice_3 = QuizQuestionChoice.new(txt: 'Iphone12', iscorrect: false)&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_1&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_2&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_3&lt;br /&gt;
      user_answer = double('user_answer')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :comments).and_return('Iphone14')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :answer).and_return(1)&lt;br /&gt;
            expected_html = '&amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;Iphone13&amp;lt;br&amp;gt;Iphone12&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Your answer is: &amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt;&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
            expect(quiz_question.view_completed_question(user_answer)).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== QuizQuestionChoice ===&lt;br /&gt;
The ''QuizQuestionChoice'' class represents a single choice in a true/false or multiple choice question. A QuizQuestion or QuizQuestion subclass has a 'has_many' relationship with QuizQuestionChoice. In the original implementation, every method that referenced the QuizQuestion's QuizQuestionChoices would do a retrieve -- this single line of code was duplicated multiple times through the four classes.&lt;br /&gt;
&lt;br /&gt;
In order to improve this, we moved the fetching of a QuizQuestion's QuizQuestionChoices into a method that is run after an instance is initialized. From there the QuizQuestionChoices can be referenced as an instance variable instead of having the same fetch referenced over and over.&lt;br /&gt;
&lt;br /&gt;
The referenced method from the new implementation of QuizQuestion:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  after_initialize :load_choices&lt;br /&gt;
  def load_choices&lt;br /&gt;
    @quiz_question_choices = QuizQuestionChoice.where(question_id: id)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Overall Plan ===&lt;br /&gt;
The overall plan is to perform automated RSpec testing of all the methods that have been moved to the superclass, namely ''QuizQuestion''.  These methods are:&lt;br /&gt;
* ''edit''&lt;br /&gt;
* ''view_question_text''&lt;br /&gt;
* ''complete''&lt;br /&gt;
* ''view_completed_question''&lt;br /&gt;
* ''isvalid''&lt;br /&gt;
&lt;br /&gt;
Most tests will test the methods in ''QuizQuestion'' class as the subclasses mainly inherit these methods without extension.  These test will be included in ''quiz_question_spec.rb'' (full path: /spec/requests/model/quiz_question_spec.rb).  Methods that are extended by subclasses will also be tested in a separate spec file.  For example, ''isvalid'' method is fully tested for ''QuizQuestion'' model in ''quiz_question_spec.rb''.  Since ''TrueFalse'' and ''MultipleChoiceRadio'' inherit this method fully from the superclass without extension, no extra test is created to test the method for the two subclasses.  However, ''MultipleChoiceCheckbox'' extended the ''isvalid'' method.  As a result, extra tests are created to test ''isvalid'' for ''MultipleChoiceCheckbox''; these tests are then included in ''multiple_choice_checkbox_spec.rb''&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The goal in our automated testing of the edit function is to ensure that, given a QuizQuestion or QuizQuestion subclass object, the edit method of the class should display the HTML returned for the edit functionality as expected. In each of our Rspec test files, an object of the class under test (QuizQuestion or subclasses) is created to simulate what a similar database-driven object would appear like to our model classes.&lt;br /&gt;
&lt;br /&gt;
* Within ''spec/requests/model/quiz_question_spec.rb'' the edit method is tested to ensure the default question prefix HTML is returned. &lt;br /&gt;
* Within ''spec/requests/model/true_false_spec.rb'', ''spec/requests/model/multiple_choice_radio_spec.rb'', and ''spec/requests/model/multiple_choice_checkbox_spec.rb'' the edit method is tested to ensure given an object filled out with QuizQuestionChoices, question text, question id, and weight the HTML is rendered appropriately.&lt;br /&gt;
&lt;br /&gt;
These tests will fail if the underlying HTML returned from the model changes, which would be indicative of possible negative downstream effects in the application where the QuizQuestion subclasses are used.&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method:&lt;br /&gt;
* Test the return message if the input contains text for the question prompt, text for all question choices, and only one of the choices is correct '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for the question prompt '''(expect &amp;quot;Please make sure all questions have text&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for at least one of the question choices '''(expect &amp;quot;Please make sure every question has text for all options&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides no correct answer '''(expect &amp;quot;Please select a correct answer for all questions&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;Please select only one correct answer for all questions&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
'''isvalid''' is extended in '''MultipleChoiceCheckbox''' as the requirements for '''MultipleChoiceCheckbox''' to be valid are slightly different from those of '''QuizQuestion''', '''TrueFalse''', and '''MultipleChoiceRadio'''&lt;br /&gt;
* Test the return message if the input provides only 1 correct answer '''(expect &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there are more than one correct choices' do&lt;br /&gt;
      it 'returns &amp;quot;Please select only one correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                                  '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select only one correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_question_text ===&lt;br /&gt;
The test for '''view_question_text''' is fairly straight forward as once the question input passes '''isvalid''', we only need to make sure that view_question_text display the information in a predetermined format.  The test for '''view_question_text''' is shown below&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_question_text' do&lt;br /&gt;
    it 'returns the text of the questions' do&lt;br /&gt;
      weight = quiz_question.weight&lt;br /&gt;
      expect(quiz_question.view_question_text).to eq('&amp;lt;b&amp;gt;Question Text&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;Question Type: MultipleChoiceRadio&amp;lt;br /&amp;gt;Question Weight: ' + &lt;br /&gt;
                                                      weight.to_s + '&amp;lt;br /&amp;gt;  - &amp;lt;b&amp;gt;Choice 1&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;   - Choice 2&amp;lt;br /&amp;gt;   - Choice 3&amp;lt;br /&amp;gt;   - &lt;br /&gt;
                                                      Choice 4&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;')&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== RSpec Test Setup ===&lt;br /&gt;
Note that for each test file we included a ''before'' block that setup a QuizQuestion subclass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;br /&gt;
* [https://github.com/expertiza/reimplementation-back-end/pull/6 Pull request]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148545</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148545"/>
		<updated>2023-03-28T01:55:15Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* edit */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view here]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft pull request on our [https://github.com/amjacob2/reimplementation-back-end/pull/10 repository]&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Multiple Choice Checkbox]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio Multiple Choice Radio]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse True False]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Reimplementation Details==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid = 'Please select only one correct answer for all questions' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'valid' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
=== complete ===&lt;br /&gt;
&lt;br /&gt;
The complete method was initially not implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, I removed it from its subclass and moved it to the superclass in quiz_question.rb. It generates HTML code for completing a question. The quiz question's associated answer alternatives are displayed and can be selected using radio buttons.&lt;br /&gt;
The implementation of '''complete''' is shown below-&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', &lt;br /&gt;
foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
def complete&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
    html = '&amp;lt;label for=&amp;quot;' + id.to_s + '&amp;quot;&amp;gt;' + txt + '&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;'&lt;br /&gt;
    [0, 1, 2, 3].each do |i|&lt;br /&gt;
      html += '&amp;lt;input name = ' + &amp;quot;\&amp;quot;#{id}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'id = ' + &amp;quot;\&amp;quot;#{id}&amp;quot; + '_' + &amp;quot;#{i + 1}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'value = ' + &amp;quot;\&amp;quot;#{quiz_question_choices[i].txt}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'type=&amp;quot;radio&amp;quot;/&amp;gt;'&lt;br /&gt;
      html += quiz_question_choices[i].txt.to_s&lt;br /&gt;
      html += '&amp;lt;/br&amp;gt;'&lt;br /&gt;
    end&lt;br /&gt;
    html&lt;br /&gt;
  end&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test complete method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
describe &amp;quot;#complete&amp;quot; do&lt;br /&gt;
    before do&lt;br /&gt;
      id = quiz_question.id&lt;br /&gt;
      expected_html = &amp;quot;&amp;lt;label for=\&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot;&amp;gt;Question Text&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_1\&amp;quot; value = \&amp;quot;Choice 1\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 1&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_2\&amp;quot; value = \&amp;quot;Choice 2\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 2&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_3\&amp;quot; value = \&amp;quot;Choice 3\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 3&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_4\&amp;quot; value = \&amp;quot;Choice 4\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 4&amp;lt;/br&amp;gt;&amp;quot;&lt;br /&gt;
      expect(quiz_question.complete).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns the completed HTML for the quiz question&amp;quot; do&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_completed_question ===&lt;br /&gt;
&lt;br /&gt;
The view_completed_question method was also not initially implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, we removed it from its subclass and moved it to the superclass in quiz_question.rb. The HTML code generated by this program is used to check the completed question and also to verify the response of the user. An indication is provided that indicates whether the user's response was right or wrong, and it is highlighted with help of check or delete icon which you can find here-[https://github.com/amjacob2/reimplementation-back-end/tree/main/app/assets/images assets/images].&lt;br /&gt;
&lt;br /&gt;
The implementation of '''view_completed_question''' is shown below-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def view_completed_question(user_answer)&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
&lt;br /&gt;
    html = ''&lt;br /&gt;
    quiz_question_choices.each do |answer|&lt;br /&gt;
      html += if answer.iscorrect&lt;br /&gt;
                '&amp;lt;b&amp;gt;' + answer.txt + '&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;'&lt;br /&gt;
              else&lt;br /&gt;
                answer.txt + '&amp;lt;br&amp;gt;'&lt;br /&gt;
              end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;Your answer is: '&lt;br /&gt;
    html += '&amp;lt;b&amp;gt;' + user_answer.first.comments.to_s + '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += if user_answer.first.answer == 1&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            else&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/delete_icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            end&lt;br /&gt;
    html += '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test view_completed_question method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_completed_question' do&lt;br /&gt;
    it 'returns the correct HTML for a completed question' do&lt;br /&gt;
      quiz_question = QuizQuestion.new(txt: 'which is the latest Iphone?')&lt;br /&gt;
      quiz_question_choice_1 = QuizQuestionChoice.new(txt: 'Iphone14', iscorrect: true)&lt;br /&gt;
      quiz_question_choice_2 = QuizQuestionChoice.new(txt: 'Iphone13', iscorrect: false)&lt;br /&gt;
      quiz_question_choice_3 = QuizQuestionChoice.new(txt: 'Iphone12', iscorrect: false)&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_1&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_2&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_3&lt;br /&gt;
      user_answer = double('user_answer')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :comments).and_return('Iphone14')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :answer).and_return(1)&lt;br /&gt;
            expected_html = '&amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;Iphone13&amp;lt;br&amp;gt;Iphone12&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Your answer is: &amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt;&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
            expect(quiz_question.view_completed_question(user_answer)).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== QuizQuestionChoice ===&lt;br /&gt;
The ''QuizQuestionChoice'' class represents a single choice in a true/false or multiple choice question. A QuizQuestion or QuizQuestion subclass has a 'has_many' relationship with QuizQuestionChoice. In the original implementation, every method that referenced the QuizQuestion's QuizQuestionChoices would do a retrieve -- this single line of code was duplicated multiple times through the four classes.&lt;br /&gt;
&lt;br /&gt;
In order to improve this, we moved the fetching of a QuizQuestion's QuizQuestionChoices into a method that is run after an instance is initialized. From there the QuizQuestionChoices can be referenced as an instance variable instead of having the same fetch referenced over and over.&lt;br /&gt;
&lt;br /&gt;
The referenced method from the new implementation of QuizQuestion:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  after_initialize :load_choices&lt;br /&gt;
  def load_choices&lt;br /&gt;
    @quiz_question_choices = QuizQuestionChoice.where(question_id: id)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Overall Plan ===&lt;br /&gt;
The overall plan is to test all the methods that have been moved to the superclass, namely ''QuizQuestion''.  These methods are:&lt;br /&gt;
* ''edit''&lt;br /&gt;
* ''view_question_text''&lt;br /&gt;
* ''complete''&lt;br /&gt;
* ''view_completed_question''&lt;br /&gt;
* ''isvalid''&lt;br /&gt;
&lt;br /&gt;
Most tests will test the methods in ''QuizQuestion'' class as the subclasses mainly inherit these methods without extension.  These test will be included in ''quiz_question_spec.rb'' (full path: /spec/requests/model/quiz_question_spec.rb).  Methods that are extended by subclasses will also be tested in a separate spec file.  For example, ''isvalid'' method is fully tested for ''QuizQuestion'' model in ''quiz_question_spec.rb''.  Since ''TrueFalse'' and ''MultipleChoiceRadio'' inherit this method fully from the superclass without extension, no extra test is created to test the method for the two subclasses.  However, ''MultipleChoiceCheckbox'' extended the ''isvalid'' method.  As a result, extra tests are created to test ''isvalid'' for ''MultipleChoiceCheckbox''; these tests are then included in ''multiple_choice_checkbox_spec.rb''&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The goal in our automated testing of the edit function is to ensure that, given a QuizQuestion or QuizQuestion subclass object, the edit method of the class should display the HTML returned for the edit functionality as expected. In each of our Rspec test files, an object of the class under test (QuizQuestion or subclasses) is created to simulate what a similar database-driven object would appear like to our model classes.&lt;br /&gt;
&lt;br /&gt;
* Within ''spec/requests/model/quiz_question_spec.rb'' the edit method is tested to ensure the default question prefix HTML is returned. &lt;br /&gt;
* Within ''spec/requests/model/true_false_spec.rb'', ''spec/requests/model/multiple_choice_radio_spec.rb'', and ''spec/requests/model/multiple_choice_checkbox_spec.rb'' the edit method is tested to ensure given an object filled out with QuizQuestionChoices, question text, question id, and weight the HTML is rendered appropriately.&lt;br /&gt;
&lt;br /&gt;
These tests will fail if the underlying HTML returned from the model changes, which would be indicative of possible negative downstream effects in the application where the QuizQuestion subclasses are used.&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method:&lt;br /&gt;
* Test the return message if the input contains text for the question prompt, text for all question choices, and only one of the choices is correct '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for the question prompt '''(expect &amp;quot;Please make sure all questions have text&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for at least one of the question choices '''(expect &amp;quot;Please make sure every question has text for all options&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides no correct answer '''(expect &amp;quot;Please select a correct answer for all questions&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;Please select only one correct answer for all questions&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
'''isvalid''' is extended in '''MultipleChoiceCheckbox''' as the requirements for '''MultipleChoiceCheckbox''' to be valid are slightly different from those of '''QuizQuestion''', '''TrueFalse''', and '''MultipleChoiceRadio'''&lt;br /&gt;
* Test the return message if the input provides only 1 correct answer '''(expect &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there are more than one correct choices' do&lt;br /&gt;
      it 'returns &amp;quot;Please select only one correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                                  '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select only one correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_question_text ===&lt;br /&gt;
The test for '''view_question_text''' is fairly straight forward as once the question input passes '''isvalid''', we only need to make sure that view_question_text display the information in a predetermined format.  The test for '''view_question_text''' is shown below&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_question_text' do&lt;br /&gt;
    it 'returns the text of the questions' do&lt;br /&gt;
      weight = quiz_question.weight&lt;br /&gt;
      expect(quiz_question.view_question_text).to eq('&amp;lt;b&amp;gt;Question Text&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;Question Type: MultipleChoiceRadio&amp;lt;br /&amp;gt;Question Weight: ' + &lt;br /&gt;
                                                      weight.to_s + '&amp;lt;br /&amp;gt;  - &amp;lt;b&amp;gt;Choice 1&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;   - Choice 2&amp;lt;br /&amp;gt;   - Choice 3&amp;lt;br /&amp;gt;   - &lt;br /&gt;
                                                      Choice 4&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;')&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== RSpec Test Setup ===&lt;br /&gt;
Note that for each test file we included a ''before'' block that setup a QuizQuestion subclass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;br /&gt;
* [https://github.com/expertiza/reimplementation-back-end/pull/6 Pull request]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148544</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=148544"/>
		<updated>2023-03-28T01:54:48Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: added edit function details to test plan&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view here]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft pull request on our [https://github.com/amjacob2/reimplementation-back-end/pull/10 repository]&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Multiple Choice Checkbox]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio Multiple Choice Radio]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse True False]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Reimplementation Details==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid = 'Please select only one correct answer for all questions' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'valid' if @correct_count &amp;gt; 1&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
=== complete ===&lt;br /&gt;
&lt;br /&gt;
The complete method was initially not implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, I removed it from its subclass and moved it to the superclass in quiz_question.rb. It generates HTML code for completing a question. The quiz question's associated answer alternatives are displayed and can be selected using radio buttons.&lt;br /&gt;
The implementation of '''complete''' is shown below-&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', &lt;br /&gt;
foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
def complete&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
    html = '&amp;lt;label for=&amp;quot;' + id.to_s + '&amp;quot;&amp;gt;' + txt + '&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;'&lt;br /&gt;
    [0, 1, 2, 3].each do |i|&lt;br /&gt;
      html += '&amp;lt;input name = ' + &amp;quot;\&amp;quot;#{id}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'id = ' + &amp;quot;\&amp;quot;#{id}&amp;quot; + '_' + &amp;quot;#{i + 1}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'value = ' + &amp;quot;\&amp;quot;#{quiz_question_choices[i].txt}\&amp;quot; &amp;quot;&lt;br /&gt;
      html += 'type=&amp;quot;radio&amp;quot;/&amp;gt;'&lt;br /&gt;
      html += quiz_question_choices[i].txt.to_s&lt;br /&gt;
      html += '&amp;lt;/br&amp;gt;'&lt;br /&gt;
    end&lt;br /&gt;
    html&lt;br /&gt;
  end&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test complete method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
describe &amp;quot;#complete&amp;quot; do&lt;br /&gt;
    before do&lt;br /&gt;
      id = quiz_question.id&lt;br /&gt;
      expected_html = &amp;quot;&amp;lt;label for=\&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot;&amp;gt;Question Text&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_1\&amp;quot; value = \&amp;quot;Choice 1\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 1&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_2\&amp;quot; value = \&amp;quot;Choice 2\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 2&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_3\&amp;quot; value = \&amp;quot;Choice 3\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 3&amp;lt;/br&amp;gt;&amp;lt;input name = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; id = \&amp;quot;&amp;quot; + id.to_s + &amp;quot;_4\&amp;quot; value = \&amp;quot;Choice 4\&amp;quot; type=\&amp;quot;radio\&amp;quot;/&amp;gt;Choice 4&amp;lt;/br&amp;gt;&amp;quot;&lt;br /&gt;
      expect(quiz_question.complete).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns the completed HTML for the quiz question&amp;quot; do&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_completed_question ===&lt;br /&gt;
&lt;br /&gt;
The view_completed_question method was also not initially implemented within QuizQuestion and each of the child classes had its own implementation. To reimplement this method, we removed it from its subclass and moved it to the superclass in quiz_question.rb. The HTML code generated by this program is used to check the completed question and also to verify the response of the user. An indication is provided that indicates whether the user's response was right or wrong, and it is highlighted with help of check or delete icon which you can find here-[https://github.com/amjacob2/reimplementation-back-end/tree/main/app/assets/images assets/images].&lt;br /&gt;
&lt;br /&gt;
The implementation of '''view_completed_question''' is shown below-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def view_completed_question(user_answer)&lt;br /&gt;
    quiz_question_choices = self.quiz_question_choices&lt;br /&gt;
&lt;br /&gt;
    html = ''&lt;br /&gt;
    quiz_question_choices.each do |answer|&lt;br /&gt;
      html += if answer.iscorrect&lt;br /&gt;
                '&amp;lt;b&amp;gt;' + answer.txt + '&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;'&lt;br /&gt;
              else&lt;br /&gt;
                answer.txt + '&amp;lt;br&amp;gt;'&lt;br /&gt;
              end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;Your answer is: '&lt;br /&gt;
    html += '&amp;lt;b&amp;gt;' + user_answer.first.comments.to_s + '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += if user_answer.first.answer == 1&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            else&lt;br /&gt;
              '&amp;lt;img src=&amp;quot;/assets/delete_icon.png&amp;quot;/&amp;gt;'&lt;br /&gt;
            end&lt;br /&gt;
    html += '&amp;lt;/b&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following test case has been added to test view_completed_question method-&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_completed_question' do&lt;br /&gt;
    it 'returns the correct HTML for a completed question' do&lt;br /&gt;
      quiz_question = QuizQuestion.new(txt: 'which is the latest Iphone?')&lt;br /&gt;
      quiz_question_choice_1 = QuizQuestionChoice.new(txt: 'Iphone14', iscorrect: true)&lt;br /&gt;
      quiz_question_choice_2 = QuizQuestionChoice.new(txt: 'Iphone13', iscorrect: false)&lt;br /&gt;
      quiz_question_choice_3 = QuizQuestionChoice.new(txt: 'Iphone12', iscorrect: false)&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_1&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_2&lt;br /&gt;
      quiz_question.quiz_question_choices &amp;lt;&amp;lt; quiz_question_choice_3&lt;br /&gt;
      user_answer = double('user_answer')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :comments).and_return('Iphone14')&lt;br /&gt;
      allow(user_answer).to receive_message_chain(:first, :answer).and_return(1)&lt;br /&gt;
            expected_html = '&amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt; -- Correct &amp;lt;br&amp;gt;Iphone13&amp;lt;br&amp;gt;Iphone12&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Your answer is: &amp;lt;b&amp;gt;Iphone14&amp;lt;/b&amp;gt;&amp;lt;img src=&amp;quot;/assets/Check-icon.png&amp;quot;/&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;hr&amp;gt;'&lt;br /&gt;
            expect(quiz_question.view_completed_question(user_answer)).to eq(expected_html)&lt;br /&gt;
    end&lt;br /&gt;
    end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== QuizQuestionChoice ===&lt;br /&gt;
The ''QuizQuestionChoice'' class represents a single choice in a true/false or multiple choice question. A QuizQuestion or QuizQuestion subclass has a 'has_many' relationship with QuizQuestionChoice. In the original implementation, every method that referenced the QuizQuestion's QuizQuestionChoices would do a retrieve -- this single line of code was duplicated multiple times through the four classes.&lt;br /&gt;
&lt;br /&gt;
In order to improve this, we moved the fetching of a QuizQuestion's QuizQuestionChoices into a method that is run after an instance is initialized. From there the QuizQuestionChoices can be referenced as an instance variable instead of having the same fetch referenced over and over.&lt;br /&gt;
&lt;br /&gt;
The referenced method from the new implementation of QuizQuestion:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  after_initialize :load_choices&lt;br /&gt;
  def load_choices&lt;br /&gt;
    @quiz_question_choices = QuizQuestionChoice.where(question_id: id)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Overall Plan ===&lt;br /&gt;
The overall plan is to test all the methods that have been moved to the superclass, namely ''QuizQuestion''.  These methods are:&lt;br /&gt;
* ''edit''&lt;br /&gt;
* ''view_question_text''&lt;br /&gt;
* ''complete''&lt;br /&gt;
* ''view_completed_question''&lt;br /&gt;
* ''isvalid''&lt;br /&gt;
&lt;br /&gt;
Most tests will test the methods in ''QuizQuestion'' class as the subclasses mainly inherit these methods without extension.  These test will be included in ''quiz_question_spec.rb'' (full path: /spec/requests/model/quiz_question_spec.rb).  Methods that are extended by subclasses will also be tested in a separate spec file.  For example, ''isvalid'' method is fully tested for ''QuizQuestion'' model in ''quiz_question_spec.rb''.  Since ''TrueFalse'' and ''MultipleChoiceRadio'' inherit this method fully from the superclass without extension, no extra test is created to test the method for the two subclasses.  However, ''MultipleChoiceCheckbox'' extended the ''isvalid'' method.  As a result, extra tests are created to test ''isvalid'' for ''MultipleChoiceCheckbox''; these tests are then included in ''multiple_choice_checkbox_spec.rb''&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The goal in our automated testing of the edit function is to ensure that, given a QuizQuestion or QuizQuestion subclass object, the edit method of the class should display the HTML returned for the edit functionality as expected. In each of our Rspec test files, an object of the class under test (QuizQuestion or subclasses) is created to simulate what a similar database-driven object would appear like to our model classes.&lt;br /&gt;
&lt;br /&gt;
 * Within ''spec/requests/model/quiz_question_spec.rb'' the edit method is tested to ensure the default question prefix HTML is returned. &lt;br /&gt;
 * Within ''spec/requests/model/true_false_spec.rb'', ''spec/requests/model/multiple_choice_radio_spec.rb'', and ''spec/requests/model/multiple_choice_checkbox_spec.rb'' the edit method is tested to ensure given an object filled out with QuizQuestionChoices, question text, question id, and weight the HTML is rendered appropriately.&lt;br /&gt;
&lt;br /&gt;
These tests will fail if the underlying HTML returned from the model changes, which would be indicative of possible negative downstream effects in the application where the QuizQuestion subclasses are used.&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method:&lt;br /&gt;
* Test the return message if the input contains text for the question prompt, text for all question choices, and only one of the choices is correct '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for the question prompt '''(expect &amp;quot;Please make sure all questions have text&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input is missing text for at least one of the question choices '''(expect &amp;quot;Please make sure every question has text for all options&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides no correct answer '''(expect &amp;quot;Please select a correct answer for all questions&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;Please select only one correct answer for all questions&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
'''isvalid''' is extended in '''MultipleChoiceCheckbox''' as the requirements for '''MultipleChoiceCheckbox''' to be valid are slightly different from those of '''QuizQuestion''', '''TrueFalse''', and '''MultipleChoiceRadio'''&lt;br /&gt;
* Test the return message if the input provides only 1 correct answer '''(expect &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;)'''&lt;br /&gt;
* Test the return message if the input provides more than 1 correct answer '''(expect &amp;quot;valid&amp;quot;)'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there are more than one correct choices' do&lt;br /&gt;
      it 'returns &amp;quot;Please select only one correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                                  '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select only one correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== view_question_text ===&lt;br /&gt;
The test for '''view_question_text''' is fairly straight forward as once the question input passes '''isvalid''', we only need to make sure that view_question_text display the information in a predetermined format.  The test for '''view_question_text''' is shown below&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  describe '#view_question_text' do&lt;br /&gt;
    it 'returns the text of the questions' do&lt;br /&gt;
      weight = quiz_question.weight&lt;br /&gt;
      expect(quiz_question.view_question_text).to eq('&amp;lt;b&amp;gt;Question Text&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;Question Type: MultipleChoiceRadio&amp;lt;br /&amp;gt;Question Weight: ' + &lt;br /&gt;
                                                      weight.to_s + '&amp;lt;br /&amp;gt;  - &amp;lt;b&amp;gt;Choice 1&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;   - Choice 2&amp;lt;br /&amp;gt;   - Choice 3&amp;lt;br /&amp;gt;   - &lt;br /&gt;
                                                      Choice 4&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;')&lt;br /&gt;
    end&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== RSpec Test Setup ===&lt;br /&gt;
Note that for each test file we included a ''before'' block that setup a QuizQuestion subclass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;br /&gt;
* [https://github.com/expertiza/reimplementation-back-end/pull/6 Pull request]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147692</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147692"/>
		<updated>2023-03-22T00:51:58Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Detailed Description of Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
=== QuizQuestionChoice ===&lt;br /&gt;
The ''QuizQuestionChoice'' class represents a single choice in a true/false or multiple choice question. A QuizQuestion or QuizQuestion subclass has a 'has_many' relationship with QuizQuestionChoice. In the original implementation, every method that referenced the QuizQuestion's QuizQuestionChoices would do a retrieve -- this single line of code was duplicated multiple times through the four classes.&lt;br /&gt;
&lt;br /&gt;
In order to improve this, we moved the fetching of a QuizQuestion's QuizQuestionChoices into a method that is run after an instance is initialized. From there the QuizQuestionChoices can be referenced as an instance variable instead of having the same fetch referenced over and over.&lt;br /&gt;
&lt;br /&gt;
Referenced method from the new implementation of QuizQuestion:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  after_initialize :load_choices&lt;br /&gt;
  def load_choices&lt;br /&gt;
    @quiz_question_choices = QuizQuestionChoice.where(question_id: id)&lt;br /&gt;
  end&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== RSpec Test Setup ===&lt;br /&gt;
Note that for each test we included a ''before'' block that setup a QuizQuestion subclass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147682</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147682"/>
		<updated>2023-03-22T00:40:24Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Useful Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
Note that for each test we included a ''before'' block that setup a QuizQuestion sublcass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;br /&gt;
* [https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view Demo and Test Suite Video]&lt;br /&gt;
* [http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox Future Work API demo]&lt;br /&gt;
* [https://github.com/amjacob2/reimplementation-back-end/pull/10 Future Work API sample]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147676</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147676"/>
		<updated>2023-03-22T00:38:23Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Methods in Files Modified/Created */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
Note that for each test we included a ''before'' block that setup a QuizQuestion sublcass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Additional Files Created ==&lt;br /&gt;
As part of our reimplementation, we needed to pull in some existing model classes from Expertiza in order to allow us to fully test our reimplemented models. These files were copied from the latest Expteriza main branch as of Program 3 assignment and were not modified. These files include:&lt;br /&gt;
* app/models/question.rb&lt;br /&gt;
* app/models/questionnaire.rb&lt;br /&gt;
* app/models/quiz_quesiton_choice.rb&lt;br /&gt;
&lt;br /&gt;
Additionally we created migration from these models (and our reimplemented classes):&lt;br /&gt;
* db/migrate/20230311214056_create_quiz_questions.rb&lt;br /&gt;
* db/migrate/20230314152206_create_questionnaires.rb&lt;br /&gt;
* db/migrate/20230314152945_create_questions.rb&lt;br /&gt;
* db/migrate/20230314153211_add_key_to_questions.rb&lt;br /&gt;
* db/migrate/20230315172116_create_quiz_question_choices.rb&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147674</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147674"/>
		<updated>2023-03-22T00:28:58Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* edit */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
Note that for each test we included a ''before'' block that setup a QuizQuestion sublcass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
Example test setup from ''quiz_question_spec.rb'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
  let(:quiz_question_choice1) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice2) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice3) { QuizQuestionChoice.new }&lt;br /&gt;
  let(:quiz_question_choice4) { QuizQuestionChoice.new }&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    quiz_question.quiz_question_choices = [quiz_question_choice1, quiz_question_choice2, quiz_question_choice3, quiz_question_choice4]&lt;br /&gt;
    quiz_question.txt = 'Question Text'&lt;br /&gt;
    allow(quiz_question).to receive(:type).and_return('MultipleChoiceRadio')&lt;br /&gt;
    allow(quiz_question).to receive(:id).and_return(99)&lt;br /&gt;
    allow(quiz_question).to receive(:weight).and_return(5)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:txt).and_return('Choice 1')&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice1).to receive(:iscorrect?).and_return(true)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:txt).and_return('Choice 2')&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice2).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:txt).and_return('Choice 3')&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice3).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:txt).and_return('Choice 4')&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:question_id).and_return(99)&lt;br /&gt;
    allow(quiz_question_choice4).to receive(:iscorrect?).and_return(false)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147673</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147673"/>
		<updated>2023-03-22T00:27:43Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Detailed Description of Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== edit ===&lt;br /&gt;
The edit method was originally not implemented within ''QuizQuestion'' and each of the child classes had their own implementation. These implementations had the exact same beginning HTML block:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    html = '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;textarea cols=&amp;quot;100&amp;quot; name=&amp;quot;question[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_' + id.to_s + '_txt&amp;quot;&amp;gt;' + txt + '&amp;lt;/textarea&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += 'Question Weight: '&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;number&amp;quot; name=&amp;quot;question_weights[' + id.to_s + '][txt]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;question_wt_' + id.to_s + '_txt&amp;quot; '&lt;br /&gt;
    html += 'value=&amp;quot;' + weight.to_s + '&amp;quot; min=&amp;quot;0&amp;quot; /&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;' &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Given this, we were able to move this block up to the edit method within the parent ''QuizQuesiton'' class, and extend the method in the child classes. Each of the child classes differed in how their type of question was displayed, so each still has a custom implementation of the edit method.&lt;br /&gt;
&lt;br /&gt;
An example from ''TrueFalse'':&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
  def edit&lt;br /&gt;
    html = super&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;True&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[0].iscorrect&lt;br /&gt;
    html += '/&amp;gt;True'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html += '&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;'&lt;br /&gt;
    html += '&amp;lt;input type=&amp;quot;radio&amp;quot; name=&amp;quot;quiz_question_choices[' + id.to_s + '][TrueFalse][1][iscorrect]&amp;quot; '&lt;br /&gt;
    html += 'id=&amp;quot;quiz_question_choices_' + id.to_s + '_TrueFalse_1_iscorrect_True&amp;quot; value=&amp;quot;False&amp;quot; '&lt;br /&gt;
    html += 'checked=&amp;quot;checked&amp;quot; ' if self.quiz_question_choices[1].iscorrect&lt;br /&gt;
    html += '/&amp;gt;False'&lt;br /&gt;
    html += '&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
    html.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We implemented tests for the edit method in both the parent ''QuizQuestion'' class to verify the preamble HTML is being returned as expected, and in each test class for the three subclasses to verify their specific implementations of edit are returning the HTML as expected.&lt;br /&gt;
&lt;br /&gt;
Note that for each test we included a ''before'' block that setup a QuizQuestion sublcass object of the given type being tested, assigned it the required parameters, and assigned ''QuizQuestionChoices'' that matched the type of question. Doing this allowed us to simulate in the test how the object would be used within the greater context of the application.&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147666</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147666"/>
		<updated>2023-03-22T00:18:31Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Background */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Overview and Test Suite Video ===&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147665</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147665"/>
		<updated>2023-03-22T00:17:43Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Our Implementation Project */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
A video summarizing our work and display our successful run of RSpec test cases can be viewed here: https://drive.google.com/file/d/1WWgir8u_10FblUVBFtBPd7RRgiJVqbOA/view&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147664</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147664"/>
		<updated>2023-03-22T00:16:19Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Files Modified/Created */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods in Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''edit''' method&lt;br /&gt;
* Implemented in ''quiz_question.rb''&lt;br /&gt;
* Extended in ''multiple_choice_radio.rb'', ''multiple_choice_checkbox.rb'', and ''true_false.rb''&lt;br /&gt;
* Tests cases added in ''quiz_question_spec.rb'', ''multiple_choice_checkbox_spec.rb'', and ''true_false_spec.rb''&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147663</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147663"/>
		<updated>2023-03-22T00:14:08Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: /* Future Direction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in '''[https://github.com/expertiza/expertiza Expertiza]''' there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
Included below is the UML diagram of the design of the QuizQuestion and its child classes in the current state&lt;br /&gt;
&lt;br /&gt;
[[File:Old_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
For this project, we are tasked with '''redesigning the above UML diagram''' and '''reimplementing the five methods''' (mentioned in the previous section) in the superclass (''QuizQuestion'').  The reimplementation of the methods in the superclass will provide inheritances to the subclasses (''MultipleChoiceCheckbox'', ''MultipleChoiceRadio'', and ''TrueFalse''), and will use overriding/overloading to avoid type-checking.&lt;br /&gt;
&lt;br /&gt;
Included below is the redesign of the UML diagram of the relationship between ''QuizQuestion'' and its child classes.  As shown in the diagram, all methods are moved to the parent class.  Child classes either inherit purely from the parent class' methods or extend it through overriding/overloading.&lt;br /&gt;
&lt;br /&gt;
'''Note''' that the methods reimplemented in the parent class (''QuizQuestion'') still render HTML components to provide to the front-end (through the ''response_controller.rb'' and ''questionnaires_controller.rb'').&lt;br /&gt;
&lt;br /&gt;
[[File:Updated_UML_diagram.png|1000px|]]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
In the future, as the '''Expertiza''' app is being migrated to the combination of Ruby API and React front-end (instead of legacy Rails), these methods will be modified to use Ruby API, and to handle and return React objects for the front-end.&lt;br /&gt;
&lt;br /&gt;
As part of our work on this project, we implemented a sample API controller that is able to return QuizQuestion child classes in JSON form. We believe this would be an initial step in converting the QuizQuestion models for use with a Ruby back-end / React front-end approach.&lt;br /&gt;
&lt;br /&gt;
As this was not formally part of our project requirements, we have left our work in a draft PR on our repository fork, available here: https://github.com/amjacob2/reimplementation-back-end/pull/10&lt;br /&gt;
&lt;br /&gt;
The following links are to our demonstration API instance on the NCSU VCL:&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceCheckbox&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=MultipleChoiceRadio&lt;br /&gt;
* http://152.7.178.122:3000/api/v1/quiz_questions/show?type=TrueFalse&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
=== isvalid ===&lt;br /&gt;
'''isvalid''' method is implemented in all subclasses but not the superclass.  The majority of these implementations are repetitive.  Hence, the method is reimplemented and moved to the superclass (''quiz_question.rb'').  In the subclasses, this method is inherited ('''MultipleChoiceRadio''' and ''TrueFalse''') with the exception of '''MultipleChoiceCheckbox''' where the method is extended.  The implementation of '''isvalid''' is shown below&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question.rb&lt;br /&gt;
class QuizQuestion &amp;lt; Question&lt;br /&gt;
  has_many :quiz_question_choices, class_name: 'QuizQuestionChoice', foreign_key: 'question_id', inverse_of: false, dependent: :nullify&lt;br /&gt;
&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    @valid = 'valid'&lt;br /&gt;
    return @valid = 'Please make sure all questions have text' if txt == ''&lt;br /&gt;
    @correct_count = 0&lt;br /&gt;
    choice_info.each_value do |value|&lt;br /&gt;
      if (value[:txt] == '') || value[:txt].empty? || value[:txt].nil?&lt;br /&gt;
        @valid = 'Please make sure every question has text for all options'&lt;br /&gt;
        return @valid&lt;br /&gt;
      end&lt;br /&gt;
      @correct_count += 1 if value[:iscorrect]&lt;br /&gt;
    end&lt;br /&gt;
    @valid = 'Please select a correct answer for all questions' if @correct_count.zero?&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox.rb&lt;br /&gt;
class MultipleChoiceCheckbox &amp;lt; QuizQuestion&lt;br /&gt;
  ... [other methods] ...&lt;br /&gt;
&lt;br /&gt;
  def isvalid(choice_info)&lt;br /&gt;
    super&lt;br /&gt;
    if @correct_count == 1&lt;br /&gt;
      @valid = 'A multiple-choice checkbox question should have more than one correct answer.'&lt;br /&gt;
    end&lt;br /&gt;
    @valid&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To test '''isvalid''' several tests were written to check for the behavior and returns of the method.  Since the method is extended in '''MultipleChoiceCheckbox''', extra tests were written to make sure that the method correctly evaluate a question and its choices.  Below are the tests used for '''isvalid''' method&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# quiz_question_spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe QuizQuestion do&lt;br /&gt;
  let(:quiz_question) { QuizQuestion.new }&lt;br /&gt;
   ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
 &lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when the question and its choices have valid text' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvaid' do&lt;br /&gt;
    let(:no_text_question) {QuizQuestion.new}&lt;br /&gt;
    context 'when the question itself does not have txt' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure all questions have text&amp;quot;' do&lt;br /&gt;
        allow(no_text_question).to receive(:txt).and_return('')&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(no_text_question.isvalid(questions)).to eq('Please make sure all questions have text')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when a choice does not have text' do&lt;br /&gt;
      it 'returns &amp;quot;Please make sure every question has text for all options&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: '', iscorrect: true }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please make sure every question has text for all options')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when no choices are correct' do&lt;br /&gt;
      it 'returns &amp;quot;Please select a correct answer for all questions&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: false }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(quiz_question.isvalid(questions)).to eq('Please select a correct answer for all questions')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Extended tests for '''isvalid''' in '''MultipleChoiceCheckbox'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
# multiple_choice_checkbox+spec.rb&lt;br /&gt;
require 'swagger_helper'&lt;br /&gt;
&lt;br /&gt;
describe MultipleChoiceCheckbox do&lt;br /&gt;
  let(:multiple_choice_question) { MultipleChoiceCheckbox.new }&lt;br /&gt;
    ... [setup] ...&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  ... [other tests] ...&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is only 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;A multiple-choice checkbox question should have more than one correct answer.&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: false } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('A multiple-choice checkbox question should have more than one correct answer.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  describe '#isvalid' do&lt;br /&gt;
    context 'when there is more than 1 correct answer' do&lt;br /&gt;
      it 'returns &amp;quot;valid&amp;quot;' do&lt;br /&gt;
        questions = { '1' =&amp;gt; { txt: 'question text', iscorrect: true }, '2' =&amp;gt; { txt: 'question text', iscorrect: false }, &lt;br /&gt;
                      '3' =&amp;gt; { txt: 'question text', iscorrect: false }, '4' =&amp;gt; { txt: 'question text', iscorrect: true } }&lt;br /&gt;
        expect(multiple_choice_question.isvalid(questions)).to eq('valid')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Created ==&lt;br /&gt;
'''isvalid''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' and ''true_false.rb''&lt;br /&gt;
* Extended in ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Modified ''quiz_question_spec.rb'' and ''multiple_choice_checkbox_spec.rb'' to include tests for the method.&lt;br /&gt;
&lt;br /&gt;
'''view_question_text''' method&lt;br /&gt;
* left as is in ''quiz_question.rb''&lt;br /&gt;
&lt;br /&gt;
'''complete''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for complete method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
'''view_completed_question''' method&lt;br /&gt;
* Reimplemented the method in ''quiz_question.rb''&lt;br /&gt;
* Removed from ''multiple_choice_radio.rb'' &lt;br /&gt;
* Removed from ''multiple_choice_checkbox.rb''&lt;br /&gt;
* Added test case for view_completed_question method in ''quiz_question_spec.rb''.&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;br /&gt;
* To run the test suite written for our project, use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
* To run individual test files (separated by model that is being tested), use the following command: &lt;br /&gt;
 &amp;lt;nowiki&amp;gt; bundle exec rspec spec/requests/model/test_file_name.rb &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Useful Links==&lt;br /&gt;
[https://github.com/amjacob2/reimplementation-back-end.git Our Github Forked Repo]&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147409</id>
		<title>CSC/ECE 517 Spring 2023 - E2311. Reimplement QuizQuestion and its child classes</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_E2311._Reimplement_QuizQuestion_and_its_child_classes&amp;diff=147409"/>
		<updated>2023-03-19T02:45:01Z</updated>

		<summary type="html">&lt;p&gt;Jdjohns4: created outline of documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Background ==&lt;br /&gt;
&lt;br /&gt;
=== Current State ===&lt;br /&gt;
Currently, in Expertiza there is a model class called '''QuizQuestion''' which serves as a superclass of classes '''MultipleChoiceCheckbox''', '''MultipleChoiceRadio''', and '''TrueFalse'''. &lt;br /&gt;
&lt;br /&gt;
There are four methods in the '''QuizQuestion''' superclass which return HTML strings, depending on the question type:&lt;br /&gt;
* ''edit'' -- What to display if an instructor (etc.) is creating or editing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_question_text'' -- What to display if an instructor (etc.) is viewing a questionnaire (questionnaires_controller.rb)&lt;br /&gt;
* ''view_completed_question'' -- What to display if a student is viewing a filled-out questionnaire (response_controller.rb)&lt;br /&gt;
* ''complete'' -- What to display if a student is filling out a questionnaire (response_controller.rb)&lt;br /&gt;
&lt;br /&gt;
Additionally, the inheriting classes contain a method ''isvalid'' that is not in '''QuizQuestion'''.&lt;br /&gt;
&lt;br /&gt;
=== Our Implementation Project ===&lt;br /&gt;
[insert details about what we've been tasked in this project]&lt;br /&gt;
&lt;br /&gt;
=== Future Direction ===&lt;br /&gt;
[insert notes about ruby api + react, vs legacy rails]&lt;br /&gt;
&lt;br /&gt;
== Team ==&lt;br /&gt;
* Aileen Jacob (amjacob2)&lt;br /&gt;
* Anh Nguyen (anguyen9)&lt;br /&gt;
* Joe Johnson (jdjohns4)&lt;br /&gt;
* '''Mentor''' Jialin Cui (jcui9)&lt;br /&gt;
&lt;br /&gt;
== Detailed Description of Changes ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Files Modified/Created ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Executing Tests ==&lt;/div&gt;</summary>
		<author><name>Jdjohns4</name></author>
	</entry>
</feed>