<?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=Aapatel8</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=Aapatel8"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Aapatel8"/>
	<updated>2026-08-17T11:00:17Z</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_2017/final_T1701&amp;diff=108647</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108647"/>
		<updated>2017-04-28T02:01:57Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Services */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app. The main goal of the project is to port the current SDK written in NodeJS to one that supports Python.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
The SDK is broken into several modules, each of which represents a class. The class diagram shown below represents each of these classes and their methods. This project deals with the '''services''' and the '''extra''' modules. &lt;br /&gt;
&lt;br /&gt;
The SDK provides '''services''' module to enhance support for the application. These services can be triggered when the customer application wants to send an alert or update the customer about the status of the IoT system. &lt;br /&gt;
&lt;br /&gt;
The '''extra''' module provides some 3rd party functionalities which the customer can use in their application. Like the geolocation feature can be used along with GPS sensor to pin point a location on the map. &lt;br /&gt;
&lt;br /&gt;
[[File:Tago_uml_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token (token is analogous to API key) needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
====mqtt====&lt;br /&gt;
The mqtt class should provide a simple way to publish some data to the Tago broker.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* publish&lt;br /&gt;
** This method is used by the client to publish to the Tago broker. Check out [http://mqtt.org/ MQTT] and [http://sdk.js.tago.io/en/latest/analysis.html#mqtt MQTT in Tago] for more information on how it works.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***message&lt;br /&gt;
***[http://docs.tago.io/en/latest/concepts.html#bucket bucket]&lt;br /&gt;
&lt;br /&gt;
====socket====&lt;br /&gt;
The socket class should provide support to send some data to a bucket via an HTTP POST call.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send some JSON data to the bucket.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***bucket_id&lt;br /&gt;
***data_entry&lt;br /&gt;
&lt;br /&gt;
====console====&lt;br /&gt;
The console class should provide support to send some logged data to the Tago server.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send some message with a time-stamp to the server.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***message&lt;br /&gt;
***timestamp&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token(API Key) that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108646</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108646"/>
		<updated>2017-04-28T01:18:24Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app. The main goal of the project is to port the current SDK written in NodeJS to one that supports Python.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
The SDK is broken into several modules, each of which represents a class. The class diagram shown below represents each of these classes and their methods. This project deals with the '''services''' and the '''extra''' modules. &lt;br /&gt;
&lt;br /&gt;
The SDK provides '''services''' module to enhance support for the application. These services can be triggered when the customer application wants to send an alert or update the customer about the status of the IoT system. &lt;br /&gt;
&lt;br /&gt;
The '''extra''' module provides some 3rd party functionalities which the customer can use in their application. Like the geolocation feature can be used along with GPS sensor to pin point a location on the map. &lt;br /&gt;
&lt;br /&gt;
[[File:Tago_uml_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token (token is analogous to API key) needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token(API Key) that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Tago_uml_diagram.png&amp;diff=108645</id>
		<title>File:Tago uml diagram.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Tago_uml_diagram.png&amp;diff=108645"/>
		<updated>2017-04-28T01:17:58Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: uploaded a new version of &amp;amp;quot;File:Tago uml diagram.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108644</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108644"/>
		<updated>2017-04-28T01:15:31Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app. The main goal of the project is to port the current SDK written in NodeJS to one that supports Python.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
The SDK is broken into several modules, each of which represents a class. The class diagram shown below represents each of these classes and their methods. This project deals with the '''services''' and the '''extra''' modules. &lt;br /&gt;
&lt;br /&gt;
The SDK provides '''services''' module to enhance support for the application. These services can be triggered when the customer application wants to send an alert or update the customer about the status of the IoT system. &lt;br /&gt;
&lt;br /&gt;
The '''extra''' module provides some 3rd party functionalities which the customer can use in their application. Like the geolocation feature can be used along with GPS sensor to pin point a location on the map. &lt;br /&gt;
&lt;br /&gt;
[[File:Tago_uml_diagram.png|200px|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token (token is analogous to API key) needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token(API Key) that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108643</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108643"/>
		<updated>2017-04-28T01:13:37Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app. The main goal of the project is to port the current SDK written in NodeJS to one that supports Python.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
The SDK is broken into several modules, each of which represents a class. The class diagram shown below represents each of these classes and their methods. This project deals with the '''services''' and the '''extra''' modules. &lt;br /&gt;
&lt;br /&gt;
The SDK provides '''services''' module to enhance support for the application. These services can be triggered when the customer application wants to send an alert or update the customer about the status of the IoT system. &lt;br /&gt;
&lt;br /&gt;
The '''extra''' module provides some 3rd party functionalities which the customer can use in their application. Like the geolocation feature can be used along with GPS sensor to pin point a location on the map. &lt;br /&gt;
&lt;br /&gt;
[[File:Tago_uml_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token (token is analogous to API key) needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token(API Key) that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Tago_uml_diagram.png&amp;diff=108642</id>
		<title>File:Tago uml diagram.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Tago_uml_diagram.png&amp;diff=108642"/>
		<updated>2017-04-28T01:12:51Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108560</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108560"/>
		<updated>2017-04-13T04:21:32Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* What are we doing? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app. The main goal of the project is to port the current SDK written in NodeJS to one that supports Python.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
[[File:Tago_class_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108555</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108555"/>
		<updated>2017-04-13T04:07:19Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
[[File:Tago_class_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
Each of the classes and function in the Services module will be tested in the following manner (eg. function testing SMS.send):&lt;br /&gt;
# Object created with appropriate token parameter (eg. SMS s = new SMS(&amp;quot;123456&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. s.send(&amp;quot;+9195550100&amp;quot;, &amp;quot;Temperature over 100 degrees Celsius&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (as SMS) and message asserted with the correct expected value&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
Each of the classes and function in the Extra module will be tested in the following manner (eg. function testing Weather.forecast):&lt;br /&gt;
# Object created with appropriate API key (eg. Weather w = new Weather(&amp;quot;123abc&amp;quot;))&lt;br /&gt;
# Function called with appropriate parameters (eg. w.forecast(&amp;quot;27516&amp;quot;))&lt;br /&gt;
# Request sent to tago server&lt;br /&gt;
# Response returned (Promise - success) and asserted with the correct expected value(s)&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108533</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108533"/>
		<updated>2017-04-13T03:34:59Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
[[File:Tago_class_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. &lt;br /&gt;
&lt;br /&gt;
In the following, a '''Promise''' is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' || &lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Description''' ||&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108530</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108530"/>
		<updated>2017-04-13T03:25:57Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
[[File:Tago_class_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. In the following, a Promise is simply an object used for asynchronous computations, which represents the resulting status of the operation (success or failure), with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108529</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108529"/>
		<updated>2017-04-13T03:25:13Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[https://goo.gl/fw2fK4 '''Requirements''']&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014,[https://tago.io/ Tago]  is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago has a [https://github.com/tago-io/tago-sdk-js node.js-SDK], but they have begun work on a [https://github.com/tago-io/tago-sdk-python Python SDK]. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality. It is worth noting that we are building a Python interface for interacting with Tago’s RESTful API, not a web-based UI or a web-app.&lt;br /&gt;
&lt;br /&gt;
[[File:PythonAPI usage diagram.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Client - SDK - Server Workflow'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
[[File:Tago dashboard.png|center]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago Dashboard'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
[[File:Tago_class_diagram.png|center|alt=Class diagram|Class diagram for tago-python SDK]]&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;'''Tago-SDK Class Diagram'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
As can be seen in the design, there are plenty of modules in tago-sdk. The '''services''' and '''extra''' API modules are being implemented in Python (currently implemented using NodeJS) as a part of this project. These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients. In the following, a Promise is simply an object used for asynchronous computations. It represents the resulting status of the operation, success or failure, with each scenario having an appropriate callback function.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** '''Arguments'''&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** '''Arguments''' &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Unit Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''pytest'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Services Module'''      ||  [[File:Unit test service.jpg]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module'''      || [[File:Extra unit tests.png]]&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Service Module'''    ||  The manual testing will be done by providing example scripts.&lt;br /&gt;
For sms method, the tester can add his/her number and run the script and test whether sms is being sent or not.&lt;br /&gt;
&lt;br /&gt;
Similarly, for the email method the tester can add his/her email address and test whether email is sent or not after running the script.&lt;br /&gt;
|-&lt;br /&gt;
| '''Extra Module''' || Example scripts will be provided which will use the third party API calls using the SDK and print the response on console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* [https://tago.io/ Official Tago Website]&lt;br /&gt;
* [http://docs.tago.io/en/latest/ Tago official document]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-js Tago JS SDK]&lt;br /&gt;
* [https://github.com/tago-io/tago-sdk-python Tago Python SDK]&lt;br /&gt;
* [http://community.tago.io/ Tago Community]&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108245</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108245"/>
		<updated>2017-04-08T03:42:27Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
The following are API modules that will be implemented in Python (currently implemented using NodeJS). These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
In progress&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
* https://github.com/tago-io/tago-sdk-js&lt;br /&gt;
* https://www.npmjs.com/package/tago&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108244</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108244"/>
		<updated>2017-04-08T03:42:10Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* References */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
The following are API modules that will be implemented in Python (currently implemented using NodeJS). These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
In progress&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
https://github.com/tago-io/tago-sdk-js&lt;br /&gt;
&lt;br /&gt;
https://www.npmjs.com/package/tago&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108243</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108243"/>
		<updated>2017-04-08T03:41:56Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
The following are API modules that will be implemented in Python (currently implemented using NodeJS). These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
In progress&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
https://github.com/tago-io/tago-sdk-js&lt;br /&gt;
https://www.npmjs.com/package/tago&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108241</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108241"/>
		<updated>2017-04-08T03:37:30Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
The following are API modules that will be implemented in Python (currently implemented using NodeJS). These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- ==Test Plan== --&amp;gt;&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108238</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108238"/>
		<updated>2017-04-08T03:35:54Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
The following are API modules that will be implemented in Python (currently implemented using NodeJS). These modules will interact with Tago servers and third-party vendors to serve HTTP requests to clients.&lt;br /&gt;
&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- ==Test Plan== --&amp;gt;&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108233</id>
		<title>CSC/ECE 517 Spring 2017/final T1701</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/final_T1701&amp;diff=108233"/>
		<updated>2017-04-08T03:31:14Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
===What is Tago?===&lt;br /&gt;
Founded in 2014, Tago is startup based out of Centennial Campus. They have created a service that allows users to craft dashboards that are used by Internet of Things (IoT) devices for a variety of purposes. These include storing/visualizing data, running triggers that send emails, SMS messages, or data to other devices/http endpoints, and initiating custom analysis scripts to manipulate and analyze data. Along with the dashboard, Tago has developed an SDK that developers can utilize when programming on the IoT devices in order to make these various requests.&lt;br /&gt;
&lt;br /&gt;
===What are we doing?===&lt;br /&gt;
Currently, Tago’s SDK is only developed for NodeJS, but they have begun work on a Python SDK. We have decided to assist in order to speed up the the development and delivery of their Python SDK.  We have broken down the project into two teams, T1701 and T1702, and split the work by assigning different subsets of functionality.&lt;br /&gt;
&lt;br /&gt;
===Why are we doing it?===&lt;br /&gt;
Tago currently has customers with needs for a Python SDK including Texas Instruments and Tesla. The sooner the SDK is developed, the sooner these companies can utilize them.  Python has a number of advantages over NodeJS, particularly around the communities.  The Python community has implemented a vast number of scientific and other niche libraries that could be useful in IoT sensors and measurement equipment.  &lt;br /&gt;
&amp;lt;!-- ==Problem Statement== --&amp;gt;&lt;br /&gt;
&amp;lt;!-- ==Design== --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
=== Services ===&lt;br /&gt;
As a part of services, Tago should provide some functions that can greatly help your application. When setting up a service, an analysis-token needs to be passed. For convenience, the context returns a property token that you can use to setup a service object.&lt;br /&gt;
&lt;br /&gt;
====sms====&lt;br /&gt;
The sms class should provide support to send a sms directly to tago admins or customers from analysis. SMS costs incurred depend on the country of operation.&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send SMS.&lt;br /&gt;
** Arguments&lt;br /&gt;
***  Phone number. (With the country code if not in the US)&lt;br /&gt;
***  Message to be sent&lt;br /&gt;
&lt;br /&gt;
====email====&lt;br /&gt;
The email class should provide support to send a email with analysis details or any other info.&lt;br /&gt;
'''Method'''&lt;br /&gt;
* send &lt;br /&gt;
** This method is used to send email.&lt;br /&gt;
** Arguments&lt;br /&gt;
***Email address&lt;br /&gt;
***Subject of the email&lt;br /&gt;
***Message to be sent&lt;br /&gt;
***E-mail address for the receiver to reply to&lt;br /&gt;
&lt;br /&gt;
===Third Party APIs (Extra) ===&lt;br /&gt;
Tago supports third-party API’s, like Google Maps and Weather service. All third-party services are free to use, with a free token that can obtained from the third party website.&lt;br /&gt;
&lt;br /&gt;
====geocoding====&lt;br /&gt;
This class is used to get a geolocation (latitude/longitude) for a valid address, or vice versa. To make use of this functionality, a google geocoding API key needs to be provided while creating a new object.&lt;br /&gt;
[https://developers.google.com/maps/documentation/geocoding/start Geocoding API provider] documentation. &lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*getGeolocation&lt;br /&gt;
** Convert the address to a valid geolocation, if it exists&lt;br /&gt;
** Arguments &lt;br /&gt;
*** address(string) A valid address&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*getAddress&lt;br /&gt;
** Convert a valid geolocation to an address, if it exists.&lt;br /&gt;
** Arguments&lt;br /&gt;
*** geolocation(string) A valid geolocation&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====currency====&lt;br /&gt;
This class checks several currencies in real-time, and the historical exchange rates for over 168 countries. &lt;br /&gt;
[https://currencylayer.com/ Currency API provider]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*convert&lt;br /&gt;
**Returns the current exchange rate of one currency to another one&lt;br /&gt;
** Arguments &lt;br /&gt;
*** from(string) convert from. See [https://currencylayer.com/currencies supported currencies] for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====distance====&lt;br /&gt;
This class helps to calculate the distance between two points. [https://developers.google.com/maps/documentation/distance-matrix/intro Google Distance API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
* measure&lt;br /&gt;
** Measure is a service that provides the travel distance and time for a matrix of origins and destinations.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** origins(array) An array of origins, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** destinations(array) An array of destinations, can be string location or [http://geojson.org/ geojson].&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’. See [https://developers.google.com/maps/faq#languagesupport language support] for more information. (optional)&lt;br /&gt;
*** mode(string) For the calculation of distances, you may specify the transportation mode to use. By default, distances are calculated for driving mode. See the [https://developers.google.com/maps/documentation/distance-matrix/intro#travel_modes travel modes] supported for more information&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
====weather====&lt;br /&gt;
Whenever you need to get weather conditions around the world, use weather service APIs. This class defines helper methods to achieve this and talk to tago server. [https://www.wunderground.com/weather/api/ Wunderground API]&lt;br /&gt;
&lt;br /&gt;
'''Methods'''&lt;br /&gt;
*current&lt;br /&gt;
** Get the current weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*forecast&lt;br /&gt;
** Returns a summary of the weather forecast for the next 10 days. This includes high and low temperatures, a string text forecast and other conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get the response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See `language support [https://www.wunderground.com/weather/api/d/docs?d=language-support language support]  for more information. (optional)&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*history&lt;br /&gt;
** Returns a summary of the weather conditions for the last 10 days. This includes high and low temperatures, a string text and other weather conditions.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** date(string) a past date.&lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set the language. Default is ‘EN’. See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
*alert&lt;br /&gt;
**Returns the short name description, expiration time and a long text description of a severe alert, if one has been issued for the searched location.&lt;br /&gt;
** Arguments &lt;br /&gt;
*** query(string) It can be address, zipcode or geojson.&lt;br /&gt;
*** full(boolean) Set to get response with full description. Default is false. (optional)&lt;br /&gt;
*** language(string) Set a language. Default is ‘EN’.  See [https://www.wunderground.com/weather/api/d/docs?d=language-support language support] for more information. (optional).&lt;br /&gt;
** Returns&lt;br /&gt;
*** (Promise)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- ==Test Plan== --&amp;gt;&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107817</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107817"/>
		<updated>2017-04-01T03:12:22Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project is an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called [https://github.com/kkushagra/expertiza/tree/exportdetail : ''''exportdetail'''']&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fills out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews are stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So that is why our assignment is to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
So the reason we implemented our feature a certain way is because there was already previous code for exporting an aggregate csv so we designed our feature in the same fashion. An export controller already existed so we added a new method in that for exporting details and modeled it based on the already existing export method. Assignment.rb already had the methods for setting columns/fields and getting aggregate export info so we added our implementations corresponding methods in that class as well. For the view, we simply added our code, that linked to the new controller method, in the same view where the other csv feature was implemented. No specific design pattern was used, but our feature was simply modeled after previously existing similar features.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    return csv unless detail_options.value?('true')&lt;br /&gt;
    &lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contains all answer objects for this assignment&lt;br /&gt;
     # Find all unique response types&lt;br /&gt;
    @uniq_response_type = ResponseMap.uniq.pluck(:type)&lt;br /&gt;
     # Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)   &lt;br /&gt;
     # create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @answers = generate_answer(@answers, @assignment)&lt;br /&gt;
    # Loop through each round and response type and construct a new row to be pushed in CSV&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        round_type = check_empty_rounds(@answers, round_num, res_type)&lt;br /&gt;
        unless round_type.nil?&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          csv &amp;lt;&amp;lt; csv_row(detail_options, answer)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method checks nil while populating a csv field headers to only add headers which the user selected from the checkboxes.&lt;br /&gt;
&lt;br /&gt;
  def self.handle_nil(csv_field)&lt;br /&gt;
    return ' ' if csv_field.nil?&lt;br /&gt;
    csv_field&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method generates a CSV row by looping through the answers.&lt;br /&gt;
&lt;br /&gt;
  # Generates a single row based on the detail_options selected &lt;br /&gt;
  def self.csv_row(detail_options, answer)&lt;br /&gt;
    tcsv = []&lt;br /&gt;
    @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
    ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
    @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
    @reviewee = Participant.find_by_id(ans.reviewee_id).user if @reviewee.nil?&lt;br /&gt;
    reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(@reviewee.id) if detail_options['team_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(@reviewee.name) if detail_options['team_name'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(reviewer.name) if detail_options['reviewer'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.question.txt) if detail_options['question'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.question.id) if detail_options['question_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.id) if detail_options['comment_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.comments) if detail_options['comments'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.answer) if detail_options['score'] == 'true'&lt;br /&gt;
    tcsv&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method generates a hash of answers, which is used to populate the CSV.&lt;br /&gt;
 &lt;br /&gt;
  # Populate answers will review information&lt;br /&gt;
  def self.generate_answer(answers, assignment)&lt;br /&gt;
    # get all response maps for this assignment&lt;br /&gt;
    @response_maps_for_assignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{assignment.id}&amp;quot;]) &lt;br /&gt;
    # for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @response_maps_for_assignment.each do |map|&lt;br /&gt;
      @response_for_this_map = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      # for this response, get the answer associated with it&lt;br /&gt;
      @response_for_this_map.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    answers&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method checks for number of answers in a particular round/response type and returns nil if there are none since the csv doesn't need to output any header for this round/response type. If this round has some answers then the round number header is generated along with the response type. If the round number is nil, then we simply make the header say 'Round Nill' instead.&lt;br /&gt;
&lt;br /&gt;
  # Checks if there are rounds with no reviews&lt;br /&gt;
  def self.check_empty_rounds(answers, round_num, res_type)&lt;br /&gt;
    unless answers[round_num][res_type].empty?&lt;br /&gt;
      round_type =&lt;br /&gt;
        if round_num.nil?&lt;br /&gt;
          &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
        else&lt;br /&gt;
          &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
        end&lt;br /&gt;
      return round_type&lt;br /&gt;
    end&lt;br /&gt;
    nil&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
Flow Chart&lt;br /&gt;
&lt;br /&gt;
When exporting a detailed CSV, an instructor can choose to include all, some, or none of the headers and the assignment may or may not include answers. The chart below details possible scenarios.&lt;br /&gt;
&lt;br /&gt;
[[File:OSSTestChart.PNG]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
'''The expected CSV file is read in as a text file to simplify string comparison with the generated CSV string.'''&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107816</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107816"/>
		<updated>2017-04-01T03:07:20Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project is an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called [https://github.com/kkushagra/expertiza/tree/exportdetail : ''''exportdetail'''']&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fills out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews are stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So that is why our assignment is to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
So the reason we implemented our feature a certain way is because there was already previous code for exporting an aggregate csv so we designed our feature in the same fashion. An export controller already existed so we added a new method in that for exporting details and modeled it based on the already existing export method. Assignment.rb already had the methods for setting columns/fields and getting aggregate export info so we added our implementations corresponding methods in that class as well. For the view, we simply added our code, that linked to the new controller method, in the same view where the other csv feature was implemented. No specific design pattern was used, but our feature was simply modeled after previously existing similar features.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    return csv unless detail_options.value?('true')&lt;br /&gt;
    &lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contains all answer objects for this assignment&lt;br /&gt;
     # Find all unique response types&lt;br /&gt;
    @uniq_response_type = ResponseMap.uniq.pluck(:type)&lt;br /&gt;
     # Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)   &lt;br /&gt;
     # create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @answers = generate_answer(@answers, @assignment)&lt;br /&gt;
    # Loop through each round and response type and construct a new row to be pushed in CSV&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        round_type = check_empty_rounds(@answers, round_num, res_type)&lt;br /&gt;
        unless round_type.nil?&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          csv &amp;lt;&amp;lt; csv_row(detail_options, answer)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method checks nil while populating a csv field headers to only add headers which the user selected from the checkboxes.&lt;br /&gt;
&lt;br /&gt;
  def self.handle_nil(csv_field)&lt;br /&gt;
    return ' ' if csv_field.nil?&lt;br /&gt;
    csv_field&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method generates a CSV row by looping through the answers.&lt;br /&gt;
&lt;br /&gt;
  # Generates a single row based on the detail_options selected &lt;br /&gt;
  def self.csv_row(detail_options, answer)&lt;br /&gt;
    tcsv = []&lt;br /&gt;
    @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
    ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
    @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
    @reviewee = Participant.find_by_id(ans.reviewee_id).user if @reviewee.nil?&lt;br /&gt;
    reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(@reviewee.id) if detail_options['team_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(@reviewee.name) if detail_options['team_name'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(reviewer.name) if detail_options['reviewer'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.question.txt) if detail_options['question'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.question.id) if detail_options['question_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.id) if detail_options['comment_id'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.comments) if detail_options['comments'] == 'true'&lt;br /&gt;
    tcsv &amp;lt;&amp;lt; handle_nil(answer.answer) if detail_options['score'] == 'true'&lt;br /&gt;
    tcsv&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method generates a hash of answers, which is used to populate the CSV.&lt;br /&gt;
 &lt;br /&gt;
  # Populate answers will review information&lt;br /&gt;
  def self.generate_answer(answers, assignment)&lt;br /&gt;
    # get all response maps for this assignment&lt;br /&gt;
    @response_maps_for_assignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{assignment.id}&amp;quot;]) &lt;br /&gt;
    # for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @response_maps_for_assignment.each do |map|&lt;br /&gt;
      @response_for_this_map = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      # for this response, get the answer associated with it&lt;br /&gt;
      @response_for_this_map.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    answers&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method checks for number of answers in a particular round/response type and returns nil if there are none since the csv doesn't need to output any header for this round/response type. If this round has some answers then the round number header is generated along with the response type. If the round number is nil, then we simply make the header say 'Round Nill' instead.&lt;br /&gt;
&lt;br /&gt;
  # Checks if there are rounds with no reviews&lt;br /&gt;
  def self.check_empty_rounds(answers, round_num, res_type)&lt;br /&gt;
    unless answers[round_num][res_type].empty?&lt;br /&gt;
      round_type =&lt;br /&gt;
        if round_num.nil?&lt;br /&gt;
          &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
        else&lt;br /&gt;
          &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
        end&lt;br /&gt;
      return round_type&lt;br /&gt;
    end&lt;br /&gt;
    nil&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
Flow Chart&lt;br /&gt;
&lt;br /&gt;
When exporting a detailed CSV, an instructor can choose to include all, some, or none of the headers and the assignment may or may not include answers. The chart below details possible scenarios.&lt;br /&gt;
&lt;br /&gt;
[[File:http://wiki.expertiza.ncsu.edu/index.php/File:OSSTestChart.PNG]] &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
'''The expected CSV file is read in as a text file to simplify string comparison with the generated CSV string.'''&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:OSSTestChart.PNG&amp;diff=107815</id>
		<title>File:OSSTestChart.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:OSSTestChart.PNG&amp;diff=107815"/>
		<updated>2017-04-01T03:06:10Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: 2017 Spring OSS E1729 Test Flow Chart.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;2017 Spring OSS E1729 Test Flow Chart.&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107262</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107262"/>
		<updated>2017-03-23T19:12:48Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
'''The expected CSV file is read in as a text file to simplify string comparison with the generated CSV string.'''&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107259</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107259"/>
		<updated>2017-03-23T18:39:43Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
'''The expected CSV file is read in as a text file to simplify string comparison with the generated CSV string.'''&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107258</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107258"/>
		<updated>2017-03-23T18:39:12Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
'''The expected CSV file is read in as a text file to simplify string comparison with generated CSV string.'''&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107257</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107257"/>
		<updated>2017-03-23T18:38:43Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
*The expected CSV file is read in as a text file to simplify string comparison with generated CSV string.&lt;br /&gt;
&lt;br /&gt;
The assignment details and all field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID field headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and all field headers should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107256</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107256"/>
		<updated>2017-03-23T18:33:42Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with some options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_some_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The assignment details and all non-ID headers should be displayed with one answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team Name / Author Name,Reviewer,Question / Dimension Name,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  team1,student1,Test question:,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with no data&amp;quot; do&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_data_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details and header should be displayed.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks csv with data and no options&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;false&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_no_options_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
Only the assignment details should be displayed along with no headers and no answer.&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107255</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107255"/>
		<updated>2017-03-23T18:04:24Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created:&lt;br /&gt;
&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
The following test case is run to check this scenario:&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;checks_if_csv has the correct data&amp;quot; do&lt;br /&gt;
    create(:answer, comments: &amp;quot;Test comment&amp;quot;)&lt;br /&gt;
    options = {&amp;quot;team_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;team_name&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;reviewer&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;question&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;question_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;comment_id&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;,&lt;br /&gt;
               &amp;quot;comments&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;, &amp;quot;score&amp;quot; =&amp;gt; &amp;quot;true&amp;quot;}&lt;br /&gt;
    expected_csv = File.read('spec/features/assignment_export_details/expected_details_csv.txt')&lt;br /&gt;
    generated_csv = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_headers(@assignment.id)&lt;br /&gt;
      csv &amp;lt;&amp;lt; Assignment.export_details_fields(options)&lt;br /&gt;
      Assignment.export_details(csv, @assignment.id, options)&lt;br /&gt;
    end&lt;br /&gt;
    expect(generated_csv).to eq(expected_csv)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
A CSV file with the following content is to be expected:&lt;br /&gt;
&lt;br /&gt;
  Assignment Name: final2,Assignment Instructor: instructor6&lt;br /&gt;
  Team ID / Author ID,Team Name / Author Name,Reviewer,Question / Dimension Name,Question ID / Dimension,Comment ID,Comments,Score&lt;br /&gt;
  Round 1 - ReviewResponseMap,---,---,---,---,---,---&lt;br /&gt;
  1,team1,student1,Test question:,1,1,Test comment,1&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107254</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107254"/>
		<updated>2017-03-23T17:56:38Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      ||&lt;br /&gt;
Before each test case is run, the following factories are created.&lt;br /&gt;
&lt;br /&gt;
 before(:each) do&lt;br /&gt;
    @assignment = create(:assignment)&lt;br /&gt;
    create(:assignment_team, name: &amp;quot;team1&amp;quot;)&lt;br /&gt;
    @student = create(:student, name: &amp;quot;student1&amp;quot;)&lt;br /&gt;
    create(:participant, user: @student)&lt;br /&gt;
    create(:questionnaire)&lt;br /&gt;
    create(:question)&lt;br /&gt;
    create(:review_response_map)&lt;br /&gt;
    create(:response)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107253</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107253"/>
		<updated>2017-03-23T17:54:00Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &lt;br /&gt;
Export to CSV with all field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     ||&lt;br /&gt;
Export to CSV with some field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     ||&lt;br /&gt;
Export to CSV with all field options enabled and data contains no answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     ||&lt;br /&gt;
Export to CSV with no field options enabled and data contains an answer&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107252</id>
		<title>CSC/ECE 517 Spring 2017/oss E1729</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2017/oss_E1729&amp;diff=107252"/>
		<updated>2017-03-23T17:34:19Z</updated>

		<summary type="html">&lt;p&gt;Aapatel8: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
This project was an addendum to the bigger Expertiza project. The Expertiza project is software to create reusable learning objects through peer review. It also supports team projects, and the submission of almost any document type, including URLs and wiki pages.&lt;br /&gt;
&lt;br /&gt;
The requirement for this OSS project was to ''''Export Scores In Detail''''&lt;br /&gt;
&lt;br /&gt;
==Deployment Link==&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000&lt;br /&gt;
&lt;br /&gt;
==Branch on Git==&lt;br /&gt;
The branch on which we developed this project on is called ''''exportdetail''''&lt;br /&gt;
&lt;br /&gt;
==Problem Statement==&lt;br /&gt;
Expertiza provides a the ability for an instructor to export scores for an assignment. Whenever a user fill out teammate reviews, peer reviews, feedback reviews, and etc. the scores for each question on those reviews is stored in the database. Currently however Expertiza only exports a csv with aggregated scores which are computed as weighted averages of the scores given in reviews. This is not the most helpful for visualizing the score data by question, individual team/user, reviewer. So it was our assignment to implement the ability to export a more detailed csv that contained all the scores for each question for each review and review type within a specific assignment.&lt;br /&gt;
&lt;br /&gt;
After talking with our project contact, Ferry, it was decided that the csv would be organized by round and within each round by response type. Part of the implementation was to also include the ability to choose the delimiter for the csv and specify which columns they wanted to include.&lt;br /&gt;
&lt;br /&gt;
==Design==&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
===Files Updated===&lt;br /&gt;
*app/controllers/export_file_controller.rb&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
*app/views/export_file/start.html.erb&lt;br /&gt;
&lt;br /&gt;
===Files Added===&lt;br /&gt;
*app/views/export_file/_export_details.html.erb&lt;br /&gt;
*spec/controllers/export_file_controller_spec.rb&lt;br /&gt;
*spec/features/assignment_export_details/expected_details_csv.txt&lt;br /&gt;
&lt;br /&gt;
===Code Snippets===&lt;br /&gt;
*export_file_controller_spec.rb&lt;br /&gt;
&lt;br /&gt;
This method is called when the 'Export Details' button is clicked and selects the delimiter and generates the csv with selected columns. It then passes the CSV into the assignment models method to populate it.&lt;br /&gt;
&lt;br /&gt;
 def exportdetails&lt;br /&gt;
     @delim_type = params[:delim_type2]&lt;br /&gt;
     if @delim_type == &amp;quot;comma&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;,&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;space&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot; &amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;tab&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = &amp;quot;\t&amp;quot;&lt;br /&gt;
     elsif @delim_type == &amp;quot;other&amp;quot;&lt;br /&gt;
       filename = params[:model] + params[:id] + &amp;quot;_Details.csv&amp;quot;&lt;br /&gt;
       delimiter = other_char2&lt;br /&gt;
     end&lt;br /&gt;
     allowed_models = ['Assignment']&lt;br /&gt;
     csv_data = CSV.generate(col_sep: delimiter) do |csv|&lt;br /&gt;
      if allowed_models.include? params[:model]&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_Headers(params[:id])&lt;br /&gt;
         csv &amp;lt;&amp;lt; Object.const_get(params[:model]).export_details_fields(params[:details])&lt;br /&gt;
         Object.const_get(params[:model]).export_details(csv, params[:id], params[:details])&lt;br /&gt;
      end&lt;br /&gt;
     end&lt;br /&gt;
   &lt;br /&gt;
     send_data csv_data,&lt;br /&gt;
               type: 'text/csv; charset=iso-8859-1; header=present',&lt;br /&gt;
         disposition: &amp;quot;attachment; filename=#{filename}&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*app/models/assignment.rb&lt;br /&gt;
&lt;br /&gt;
This method is called to populate it the csv and is where the majority of our implementation lies. It finds all the ResponseMaps associated with this assignment, then finds all the Responses associated with that each ResponseMap. Then for each response it saves the Answer objects associated with it into an array that is stored in a hash that is indexed by round and response type (teammate review/feedback review/etc).&lt;br /&gt;
&lt;br /&gt;
  def self.export_details(csv, parent_id, detail_options)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    @answers = {} # Contails all answer objects for this assignment&lt;br /&gt;
    #Find all unique response types&lt;br /&gt;
    @uniq_response_type =  ResponseMap.uniq.pluck(:type)&lt;br /&gt;
    #Find all unique round numbers&lt;br /&gt;
    @uniq_rounds = Response.uniq.pluck(:round)&lt;br /&gt;
    #create the nested hash that holds all the answers organized by round # and response type&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @answers[round_num] = {}&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        @answers[round_num][res_type] = []&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    #get all response maps for this assignment&lt;br /&gt;
    @responseMapsForAssignment = ResponseMap.find_by_sql([&amp;quot;SELECT * FROM response_maps WHERE reviewed_object_id = #{@assignment.id}&amp;quot;])&lt;br /&gt;
    #for each map, get the response &amp;amp; answer associated with it&lt;br /&gt;
    @responseMapsForAssignment.each do |map|&lt;br /&gt;
      @responseForThisMap = Response.find_by_sql([&amp;quot;SELECT * FROM responses WHERE map_id = #{map.id}&amp;quot;])&lt;br /&gt;
      #for this response, get the answer associated with it&lt;br /&gt;
      @responseForThisMap.each do |res_map|&lt;br /&gt;
        @answer = Answer.find_by_sql([&amp;quot;SELECT * FROM answers WHERE response_id = #{res_map.id}&amp;quot;])&lt;br /&gt;
        @answer.each do |ans|&lt;br /&gt;
          @answers[res_map.round][map.type].push(ans)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
    @uniq_rounds.each do |round_num|&lt;br /&gt;
      @uniq_response_type.each do |res_type|&lt;br /&gt;
        if @answers[round_num][res_type].size &amp;gt; 0&lt;br /&gt;
          if round_num.nil?&lt;br /&gt;
            round_type = &amp;quot;Round Nill - &amp;quot; + res_type&lt;br /&gt;
          else &lt;br /&gt;
            round_type = &amp;quot;Round &amp;quot; + round_num.to_s + &amp;quot; - &amp;quot; + res_type.to_s&lt;br /&gt;
          end&lt;br /&gt;
          csv &amp;lt;&amp;lt; [round_type, '---', '---', '---', '---', '---', '---']&lt;br /&gt;
        end&lt;br /&gt;
        @answers[round_num][res_type].each do |answer|&lt;br /&gt;
          row = []&lt;br /&gt;
          tcsv = []&lt;br /&gt;
          @response = Response.find_by_id(answer.response_id)&lt;br /&gt;
          ans = ResponseMap.find_by_id(@response.map_id)&lt;br /&gt;
          @reviewee = Team.find_by_id(ans.reviewee_id)&lt;br /&gt;
          if @reviewee.nil?&lt;br /&gt;
            @reviewee = Participant.find_by_id(ans.reviewee_id).user&lt;br /&gt;
          end&lt;br /&gt;
          reviewer = Participant.find_by_id(ans.reviewer_id).user&lt;br /&gt;
            if @reviewee.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.id &lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if @reviewee.nil? &lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['team_name'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; @reviewee.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if reviewer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['reviewer'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; reviewer.name&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.txt.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.txt&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.question.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['question_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.question.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.id.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comment_id'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.id&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.comments.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['comments'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.comments&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            if answer.answer.nil?&lt;br /&gt;
              tcsv &amp;lt;&amp;lt; ' '&lt;br /&gt;
            else&lt;br /&gt;
              if detail_options['score'] == 'true'&lt;br /&gt;
                tcsv &amp;lt;&amp;lt; answer.answer&lt;br /&gt;
              end&lt;br /&gt;
            end&lt;br /&gt;
            csv &amp;lt;&amp;lt; tcsv&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the columns in the csv.&lt;br /&gt;
&lt;br /&gt;
  # This method is used for export detailed contents. - Akshit, Kushagra, Vaibhav&lt;br /&gt;
  def self.export_details_fields(detail_options)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team ID / Author ID' if detail_options['team_id'] == 'true'       &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Team Name / Author Name' if detail_options['team_name'] == 'true' &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Reviewer' if detail_options['reviewer'] == 'true'    &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question / Dimension Name' if detail_options['question'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Question ID / Dimension' if detail_options['question_id'] == 'true'&lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comment ID' if detail_options['comment_id'] == 'true'   &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Comments' if detail_options['comments'] == 'true'      &lt;br /&gt;
    fields &amp;lt;&amp;lt; 'Score' if detail_options['score'] == 'true'  &lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
This method is called by the controller to set the headers in the csv (including Assignment Name and Instructor)&lt;br /&gt;
&lt;br /&gt;
  # This method is used to set the headers for the csv like Assignment Name and Assignment Instructor&lt;br /&gt;
  def self.export_Headers(parent_id)&lt;br /&gt;
    @assignment = Assignment.find(parent_id)&lt;br /&gt;
    fields = []&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Name: &amp;quot; + @assignment.name.to_s&lt;br /&gt;
    fields &amp;lt;&amp;lt; &amp;quot;Assignment Instructor: &amp;quot; + User.find(@assignment.instructor_id).name.to_s&lt;br /&gt;
    fields&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
==Test Plan==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Feature/Integration Test'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Rspec'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 1'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 2'''     || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 3'''     || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 4'''     || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Process Description'''      || &amp;lt; add content &amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| '''Test Type'''      || '''Functional Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Testing Tool'''      || '''Manual Testing'''&lt;br /&gt;
|-&lt;br /&gt;
| '''Scenario 5'''    || &lt;br /&gt;
&lt;br /&gt;
* Go to the deployment link (http://152.46.16.143:3000)&lt;br /&gt;
* Log in as an instructor (we used username: instructor6 and password: password)&lt;br /&gt;
* Click Manage -&amp;gt; Assignments&lt;br /&gt;
* Click on 'View Score' for an assignment (the magnifying glass and star icon)&lt;br /&gt;
* Let the page load&lt;br /&gt;
* Click 'Export Grade' at the bottom of the page&lt;br /&gt;
* The export grades page will load&lt;br /&gt;
* Select which columns you want the csv to output and what kind of delimiter&lt;br /&gt;
* Click 'Export Details' (CSV should take 1-2 minutes to generate, so please be patient)&lt;br /&gt;
&lt;br /&gt;
'''HINT WHEN TESTING:''' When you click the 'View Score' icon for an assignment, &amp;lt;br&amp;gt;&lt;br /&gt;
some assignments take FOREVER to load on the VCL, so please be patient. &amp;lt;br&amp;gt;&lt;br /&gt;
However here is a helpful link that will go straight to the export grades page for &amp;lt;br&amp;gt;&lt;br /&gt;
Assignment ID 754 Aka Wikipedia Contribution. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://152.46.16.143:3000/export_file/start?id=754&amp;amp;model=Assignment &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Aapatel8</name></author>
	</entry>
</feed>