diff --git a/dspot-web/README.md b/dspot-web/README.md new file mode 100644 index 000000000..59e97e521 --- /dev/null +++ b/dspot-web/README.md @@ -0,0 +1,45 @@ +# Introduction + +This is a prototype web interface of Dspot, inspired from [CommitGuru](http://commit.guru/). + +Prototype demo link: + +Author: Henry Luong + +## Running + +``` +cd dspot-web +npm install --no-optional +npm run-script start +``` + +## Screenshots + +### Front page + +The front page enables to submit a new repo. + +![Pic 1](https://github.com/Tailp/dspot/blob/web/dspot-web/screenshots/pic1.png) + +### All amplified repos page + +One can browse the list of successfully amplified projects. + +![Pic 3](https://github.com/Tailp/dspot/blob/web/dspot-web/screenshots/pic2.png) + +### Demo submission + +A repo has been submitted + +![Pic 4](https://github.com/Tailp/dspot/blob/web/dspot-web/screenshots/pic3.png) + +When done, there is one page per repo to display details + +![Pic 6](https://github.com/Tailp/dspot/blob/web/dspot-web/screenshots/pic4.png) + +## Implementation + +It is built by using MEAN (Mongodb,Express,AngularJS and Nodejs) stack, therefore it will be fetching data from database Mongodb and dynamically represent the data on the templated webpages with AngularJS for the Front-End part. Routing on the Back-End will be done by Nodejs and Expressjs to handle requests from the client and communication with the database. +MongoDB will be saving the state ("error","pending","old","success") of the request for REST. Mail will be sent using gmail when done running. + diff --git a/dspot-web/app.js b/dspot-web/app.js new file mode 100644 index 000000000..a01ceeb50 --- /dev/null +++ b/dspot-web/app.js @@ -0,0 +1,43 @@ +var createError = require('http-errors'); +var express = require('express'); +var path = require('path'); +var cookieParser = require('cookie-parser'); +var logger = require('morgan'); +var bodyParser = require("body-parser"); + +var indexRouter = require('./routes/index'); +var usersRouter = require('./routes/users'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'pug'); + +app.use(logger('dev')); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', indexRouter); +app.use('/users', usersRouter); +app.use("/public", express.static(__dirname + "/public")); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(createError(404)); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + +module.exports = app; \ No newline at end of file diff --git a/dspot-web/bin/www b/dspot-web/bin/www new file mode 100755 index 000000000..b09c4b4af --- /dev/null +++ b/dspot-web/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('server:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/dspot-web/controllers/data/data.json b/dspot-web/controllers/data/data.json new file mode 100644 index 000000000..e5c4e82a6 --- /dev/null +++ b/dspot-web/controllers/data/data.json @@ -0,0 +1,3 @@ +{ + "records": [{"repoName":"travisplay","data":[0, 20, 30]},{"repoName":"repairnator","data":[10, 0, 30]},{"repoName":"dspot","data":[10, 20, 30]}] +} \ No newline at end of file diff --git a/dspot-web/controllers/methods.js b/dspot-web/controllers/methods.js new file mode 100644 index 000000000..5a6c98bf2 --- /dev/null +++ b/dspot-web/controllers/methods.js @@ -0,0 +1,194 @@ +// Mongodb +const MONGODB_HOST = process.env.MONGODB_HOST || "mongodb://mongo:27017"; +const dbName = "Dspot" || process.env.MONGODB_NAME; +const colName = "AmpRecords" || process.env.MONGODB_COLNAME; +const MongoClient = require('mongodb').MongoClient; +// ActiveMQ +const stompit = require('stompit'); +const activemq_queuename = process.env.ACTIVEMQ_QUEUENAME || "Dpipeline";; + +// Others +const assert = require('assert'); +const async = require("async"); + +/** + * Extract the request url path and fetch the corresponding page and + * send it to the user + */ +function get_page(req, res, next) { + var path = '' + if (req.originalUrl == '/') { + path = 'pages/index.html' + } else { + path = 'pages' + req.originalUrl + '.html' + } + res.sendFile(path, { root: __dirname }); +} + +function fetchData(colName, query, limit, res) { + // Database Name + /* query = {}; + query["repoName"] = reqslug; */ + if (query == undefined) { + query = {}; + } + const dbName = "Dspot" //process.env.MONGODB_NAME; + MongoClient.connect(MONGODB_HOST, { useNewUrlParser: true }, function(err, client) { + assert.equal(null, err); + const db = client.db(dbName); + console.log("Connected to mongo"); + + if (limit != undefined) { + db.collection(colName).find(query,{projection:{_id: 0,"Email":0}}).sort({ Date: -1 }).limit(limit).toArray(function(err, result) { + if (err) { + res.json(err); + } else { + /*console.log(result)*/ + res.json(result); + } + }); + } else { + db.collection(colName).find(query,{projection:{_id: 0,"Email":0}}).sort({ Date: -1 }).toArray(function(err, result) { + if (err) { + console.log(err); + res.json(err); + } else { + /*console.log(result);*/ + res.json(result); + } + }); + } + client.close(); + }); +} + +/** + * Send message to ActiveMQ queue + * @param message is a string + */ +function sendMessageToActiveMQ(message) { + return Promise.resolve(stompit.connect({ host: process.env.ACTIVEMQ_HOSTNAME || 'activemq', port: 61613}, + (err, client) => { + var queueName = activemq_queuename; + const frame = client.send({ destination: queueName}); + frame.write(message); + frame.end(); + console.log("Message sended to " + queueName) + client.disconnect(); + })); +} + +/** + * This fetch the template page for any repository + * Data will be sent over later for the specific repo + */ +exports.get_reposTemplatePage = function(req, res, next) { + console.log("Getting repos template page"); + res.sendFile('pages/reposTemplate.html', { root: __dirname }); +} + +/** + * Fetch data, give slug and branch, return result for both + * Pitmutant- and JacocoSelector + */ +exports.get_repoInfoData = function(req, res, next) { + var query = {}; + query["RepoSlug"] = req.params.user + "/" + req.params.reponame; + query["RepoBranch"] = req.params.branchname; + + fetchData('AmpRecords', query, undefined, res); +} + +/** + * For the start page fetch data for displaying 3 most + * recent scanned repos. + */ +exports.get_ReposData = function(req, res, next) { + console.log("getting most 3 recent repos data"); + /* res.sendFile( "." + req.originalUrl,{root: __dirname }); */ + /* Check if the requested data is which selector otherwise take 3 most*/ + if (req.params.state == "recent") { + fetchData('AmpRecords', {"State": "recent"}, 3, res); + } else if (req.params.state == "All") { /*Only take recent and pending state*/ + fetchData('AmpRecords',{$or:[{"State":{$eq:"recent"}},{"State":{$eq:"pending"}}]},undefined,res); + } +} + +/** + * THis should be removed later. UniqueRecords is removed. + */ +exports.get_reposInfoData = function(req, res, next) { + console.log("getting all unique data"); + fetchData('uniqueRecords', {}, undefined, res); +} + +/** + * When someone submit at the home page this function + * will check if there is already a unhandled request + * previously by checking if there is a pending state + * document in the database. If not it will submit a + * new doc with pending state with the submitted information + */ +function post_submitRepo(req, res, next) { + const url = require('url'); + + if (!validateEmail(req.body.email)) { + return Promise.resolve(res.status(400).send("Invalid email")); + } + + let repoSlug = url.parse(req.body.repo.url).pathname.substring(1); + /*let repoBranch = req.body.repo.branch;*/ + let repoBranch = "master" /*Always master - will be removed in the future*/ + let selector = req.body.dspot.selector; + /*Constructing query*/ + var query = {}; + query["RepoSlug"] = repoSlug; + query["RepoBranch"] = repoBranch; + query["State"] = "pending"; + query["Email"] = req.body.email; + + + var ampOptions = {}; + ampOptions['test-criterion'] = selector; + query["AmpOptions"] = ampOptions; + + MongoClient.connect(MONGODB_HOST, { useNewUrlParser: true }, function(err, client) { + assert.equal(null, err); + const db = client.db(dbName); + console.log("Connected to mongo"); + + // This fetch the secret associated with the slug and check it + db.collection(colName).findOne(query, async function(err, result) { + if (err) { + console.log(err); + } else { + if (result == null) { + console.log("Proceed initializing a pending document"); + + var datetime = new Date().toISOString().split('.')[0]+"Z"; + + query["Date"] = datetime; + await db.collection(colName).insertOne(query, function(err, res) { + if (err) throw err; + console.log("1 document inserted"); + }); + sendMessageToActiveMQ(repoSlug + "," + repoBranch + "," + selector) + res.status(200).send("Success!\n We'll comeback to you over email. The Dspot team. \n\n (you can now safely close this window)"); + } else { + /*console.log(result);*/ + res.status(400).send("Already existed a similar pending request"); + } + client.close(); + } + }); + }); +} + +function validateEmail(email) { + var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +} + + +exports.get_page = get_page; +exports.post_submitRepo = post_submitRepo; \ No newline at end of file diff --git a/dspot-web/controllers/pages/about.html b/dspot-web/controllers/pages/about.html new file mode 100644 index 000000000..8f5d52315 --- /dev/null +++ b/dspot-web/controllers/pages/about.html @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + Document + + + + + + + + + + + + + +
+

About

+
+ +
+
+
+ DSpot is a tool that generates missing assertions in JUnit tests. DSpot takes as input a Java project with an existing test suite. DSpot generates new test cases from existing ones and write them on disk. + DSpot supports Java projects built with Maven and Gradle + +
+
+
+ + + \ No newline at end of file diff --git a/dspot-web/controllers/pages/index.html b/dspot-web/controllers/pages/index.html new file mode 100644 index 000000000..042b8ac43 --- /dev/null +++ b/dspot-web/controllers/pages/index.html @@ -0,0 +1,248 @@ + + + + + + + + + + + + STAMP-DSpot Demo + + + + + + + + + + + +
+
+

Amplify your JUnit tests

+
+ DSpot is a tool that generates missing assertions in JUnit tests. DSpot takes as input a Java project with an existing test suite. DSpot generates new test cases from existing ones and write them on disk. + DSpot supports Java projects built with Maven and Gradle + +
+
+
+ +
+
+
+
+
+
+

Analyze a Repository

+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+ +
+
+
+ +
+
+ +
+
+ +
+ {{response}} +
+ + +
+
+
+
+
+

Important!

+
+

▪ If a config file 'dspot.properties' is present at the repository root, it is taken into account Documentation

+

▪ Auto-config is provided only for single module maven project structure (non-multimodule)

+
+
+
+
+ +
+ + + + \ No newline at end of file diff --git a/dspot-web/controllers/pages/repos.html b/dspot-web/controllers/pages/repos.html new file mode 100644 index 000000000..54cd7b056 --- /dev/null +++ b/dspot-web/controllers/pages/repos.html @@ -0,0 +1,219 @@ + + + + + + + + + + + + Document + + + + + + + + + + + + + +
+

Repositories

+
+
+ +
+ + + + diff --git a/dspot-web/controllers/pages/reposTemplate.html b/dspot-web/controllers/pages/reposTemplate.html new file mode 100644 index 000000000..3603ea338 --- /dev/null +++ b/dspot-web/controllers/pages/reposTemplate.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + Document + + + + + + + + + + +
+
+
+
+

Data over a period

+ +
+ + {{addLineGraph("pitMutantLineGraph",pitmutantLineData,datePitMutant,'totalNewMutantkilled / totalOrignalKilledMutants (%)',"#007bff")}} +
+
+ + {{addLineGraph("jacocoCovLineGraph",jacocoLineData,dateJacoco,'(totalAmpCoverage - totalInitialCoverage) / totalInitialCoverage (%)',"#007bff")}} +
+
+

Most recent amplication details

+
+
+

Overall details


+ + {{addDonutGraph("repoPieGraph",mostRecentData,mostRecentSelector,donutLabel)}} +
+
+

Data over all tests in


+ + {{addBarGraph("repoBarGraph",datasets,barLabels)}} +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/dspot-web/dspot-web.yaml b/dspot-web/dspot-web.yaml new file mode 100644 index 000000000..60eb6d09c --- /dev/null +++ b/dspot-web/dspot-web.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dspot-web + labels: + app: dspot-web +spec: + replicas: 1 + selector: + matchLabels: + app: dspot-web + template: + metadata: + labels: + app: dspot-web + spec: + containers: + - name: dspot-web + image: tailp/dspot-web + imagePullPolicy: Always + ports: + - containerPort: 3000 + env: + - name: MONGODB_HOST + value: mongodb://mongo:27017 # default if service of Mongodb deployed on K8s is called mongo with port 27017. + - name: ACTIVEMQ_HOSTNAME + value: activemq +--- +apiVersion: v1 +kind: Service +metadata: + name: dspot-web +spec: + ports: + - port: 3000 + targetPort: 3000 + selector: + app: dspot-web + type: LoadBalancer \ No newline at end of file diff --git a/dspot-web/package.json b/dspot-web/package.json new file mode 100644 index 000000000..59b70d662 --- /dev/null +++ b/dspot-web/package.json @@ -0,0 +1,26 @@ +{ + "name": "myapp", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "nodemon ./bin/www;" + }, + "dependencies": { + "async": "^3.1.0", + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "mongodb": "^3.3.0-beta2", + "morgan": "~1.9.1", + "pug": "2.0.0-beta11", + "stompit": "latest", + "express": "latest", + "path": "latest", + "body-parser": "latest", + "assert": "latest" + }, + "devDependencies": { + "nodemon": "^1.19.1" + } +} diff --git a/dspot-web/public/javascripts/allReposInfo.js b/dspot-web/public/javascripts/allReposInfo.js new file mode 100644 index 000000000..ddc6517f0 --- /dev/null +++ b/dspot-web/public/javascripts/allReposInfo.js @@ -0,0 +1,200 @@ +var colors = ["#007bff", "#28a745", "#e53935", "#c3e6cb", "#dc3545", "#6c757d"]; +var allReposApp = angular.module("allReposApp", []); +/*Directives*/ +allReposApp.directive("addDonutGraphForPitMutant", function() { + return { + template: "{{addDonutGraph(x.RepoSlug + x.RepoBranch + 'PitMutantScore' + dateDiff(x.Date),[x.AmpResult.TotalResult.totalOrignalKilledMutants,x.AmpResult.TotalResult.totalNewMutantkilled],['OriginalKills', 'NewKills'],['#007bff', '#28a745'])}}" + }; +}); + +allReposApp.directive("addDonutGraphForJacocoCov", function() { + return { + template: "{{addDonutGraph(x.RepoSlug + x.RepoBranch + 'JacocoCov' + dateDiff(x.Date),[x.AmpResult.TotalResult.totalAmpCoverage],['totalInitialCoverage', 'totalAmpCoverage'],['#6c757d', '#c3e6cb'])}}" + }; +}); + + +/*Controller*/ +allReposApp.controller("allReposCtr", function($scope, $http) { + /*variables */ + var statesToInclude = ['recent', 'pending']; + /*Http requests*/ + $http.get("/data/All").then(function(res) { + $scope.allReposData = res.data; + $scope.dataToShow = res.data; + $scope.InputSlug = ''; + $scope.filteredDataToShow = []; + + var recentReposDataVar = []; + var pendingReposDataVar = []; + /*Separate data - put this into some kind of function later*/ + for (index in res.data) { + if (res.data[index].State == "recent") { + recentReposDataVar.push(res.data[index]); + } else { + pendingReposDataVar.push(res.data[index]); + } + } + $scope.recentReposData = recentReposDataVar; + $scope.pendingReposData = pendingReposDataVar; + + $scope.$watchGroup(["InputSlug","dataToShow"], function() { + var filteredData = []; + for ( index in $scope.dataToShow) { + if($scope.dataToShow[index].RepoSlug.includes($scope.InputSlug)) { + filteredData.push($scope.dataToShow[index]); + } + } + $scope.filteredDataToShow = filteredData; + }); + }).then(function() { + /*Pagination*/ + $scope.curPage = 0; + $scope.itemsPerPage = 10; + $scope.maxSize = 5; + $scope.numOfPages = function() { + return Math.ceil($scope.filteredDataToShow.length / $scope.itemsPerPage); + }; + + $scope.$watchGroup(['curPage + numPerPage','filteredDataToShow'], function() { + var begin = $scope.curPage * $scope.itemsPerPage, + end = begin + $scope.itemsPerPage; + $scope.slicedData = $scope.filteredDataToShow.slice(begin, end); + }); + }) + +$scope.addDonutGraph = function(elemId, donutdata, label, colors) { + new Chart(document.getElementById(elemId), { + type: "doughnut", + data: { + datasets: [{ + data: donutdata, + backgroundColor: colors + }], + + // These labels appear in the legend and in the tooltips when hovering different arcs + labels: label + }, + responsive: true, + maintainAspectRatio: true, + options: { + legend: { + display: false + }, + tooltips: { + enabled: false, + displayColors: false, + borderWidth: 10 + } + } + }); +}; + + +/*Helpers*/ +$scope.dateDiff = function(givenDate) { + var newDate = Math.floor((new Date() - new Date(givenDate))/1000); + if (newDate < 60) { + return newDate.toString() + " seconds ago"; + } else if (Math.floor(newDate/60) < 60) { + return Math.floor(newDate/60).toString() + " minutes ago"; + } else if (Math.floor(newDate/3600) < 24) { + return Math.floor(newDate/3600).toString() + " hrs ago"; + } else { + return Math.floor(newDate/86400).toString() + " days ago"; + }; +} + + +$scope.switchRecentState = function() { + var index = $.inArray("recent", statesToInclude); + if (index > -1) { + statesToInclude.splice(index, 1); + /*Recent state is with the view array, switch to only pending if pending is with include states*/ + if ($.inArray("pending", statesToInclude) > -1) { + $scope.dataToShow = $scope.pendingReposData; + } else { + $scope.dataToShow = []; + } + } else { + /*If it's empty then only include recent data*/ + statesToInclude.push("recent"); + if ($.inArray("pending", statesToInclude) > -1) { + /*If pending should also be included*/ + $scope.dataToShow = $scope.allReposData; + } else { + /*Otherwise only show recent*/ + $scope.dataToShow = $scope.recentReposData; + } + } +} + +$scope.switchPendingState = function() { + var index = $.inArray("pending", statesToInclude); + if (index > -1) { + /*Pending state is with the view array, after switching only show recent*/ + statesToInclude.splice(index, 1); + if ($.inArray("recent", statesToInclude) > -1) { + $scope.dataToShow = $scope.recentReposData; + } else { + $scope.dataToShow = []; + } + } else { + /*If it's empty then only include pending data*/ + statesToInclude.push("pending"); + if ($.inArray("recent", statesToInclude) > -1) { + /*If recent should also be included*/ + $scope.dataToShow = $scope.allReposData; + } else { + /*Otherwise only show pending*/ + $scope.dataToShow = $scope.pendingReposData; + } + } +} + +$scope.resetFilter = function() { + firstFilteredData = []; +} + +}); + +/*Filters*/ +allReposApp.filter('cut', function() { + return function(value, wordwise, max, tail) { + if (!value) return ''; + + max = parseInt(max, 10); + if (!max) return value; + if (value.length <= max) return value; + + value = value.substr(0, max); + if (wordwise) { + var lastspace = value.lastIndexOf(' '); + if (lastspace !== -1) { + //Also remove . and , so its gives a cleaner result. + if (value.charAt(lastspace - 1) === '.' || value.charAt(lastspace - 1) === ',') { + lastspace = lastspace - 1; + } + value = value.substr(0, lastspace); + } + } + + return value + (tail || ' …'); + }; +}); + +allReposApp.filter('range', function() { + return function(input, total) { + if(total > 0) { + total = parseInt(total); + + for (var i=0; i 0) { + /*Get most recent total data for pie graph*/ + if (res.data[0].AmpOptions['test-criterion'] == "JacocoCoverageSelector") { + $scope.mostRecentData = [res.data[0].AmpResult.TotalResult.totalAmpCoverage, res.data[0].AmpResult.TotalResult.totalInitialCoverage]; + $scope.mostRecentSelector = "JacocoCoverageSelector"; + $scope.donutLabel = ["totalAmpCoverage", "totalInitialCoverage"] + } else { + $scope.mostRecentData = [res.data[0].AmpResult.TotalResult.totalNewMutantkilled, res.data[0].AmpResult.TotalResult.totalOrignalKilledMutants]; + $scope.mostRecentSelector = "PitmutantScoreSelector"; + $scope.donutLabel = ["totalNewMutantkilled", "totalOrignalKilledMutants"] + } + + /*Get line data*/ + for (i in res.data) { + /*JacococCoverage*/ + if (res.data[i].AmpOptions['test-criterion'] == "JacocoCoverageSelector") { + var val1 = parseInt(res.data[i].AmpResult.TotalResult.totalAmpCoverage); + var val2 = parseInt(res.data[i].AmpResult.TotalResult.totalInitialCoverage); + jacocoLineData.unshift(Math.round(val1 / val2) - 1); + dateJacoco.unshift(res.data[i].Date); + } else { + /*PitmutantScore*/ + var val1 = parseInt(res.data[i].AmpResult.TotalResult.totalNewMutantkilled); + var val2 = parseInt(res.data[i].AmpResult.TotalResult.totalOrignalKilledMutants); + pitmutantLineData.unshift(Math.round(val1 / val2)); + datePitMutant.unshift(res.data[i].Date); + } + } + delete res.data[0].AmpResult["TotalResult"]; + /*[{ + data: [1, 2], + label: 'Dataset-1', + borderColor: "#007bff", + backgroundColor: "#007bff", + fill: true + }]*/ + var datasets = []; + var barLabels = []; + /*Get bar labels for bar graph*/ + if (res.data[0].AmpOptions['test-criterion'] == "JacocoCoverageSelector") { + barLabels = ["initialCoverage","amplifiedCoverage","totalCoverage"]; + } else { + barLabels = ["originalKilledMutants","NewMutantKilled"]; + } + /*Get most recent all test data for bar graph*/ + for( key in res.data[0].AmpResult) { + var dataset = {}; + var data = []; + var stats = res.data[0].AmpResult[key]; + var label = key.split("/D/").pop(); + var borderColor = $scope.generateRandomColor(); + var backgroundColor = borderColor; + + if (res.data[0].AmpOptions['test-criterion'] == "JacocoCoverageSelector") { + data.push(stats.initialCoverage); + data.push(stats.totalAmpCoverage); + data.push(stats.totalCoverage); + } else { + data.push(stats.originalKilledMutants); + data.push(stats.NewMutantKilled) + } + + dataset["data"] = data; + dataset["label"] = label; + dataset["borderColor"] = borderColor; + dataset["backgroundColor"] = backgroundColor; + dataset["fill"] = true; + datasets.push(dataset); + } + + /*console.log(datasets);*/ + /*Line data*/ + $scope.pitmutantLineData = pitmutantLineData; + $scope.jacocoLineData = jacocoLineData; + $scope.dateJacoco = dateJacoco; + $scope.datePitMutant = datePitMutant; + + /*Bar data*/ + $scope.datasets = datasets; + $scope.barLabels = barLabels; + } else { + /*Default value so that the page does not look weird*/ + $scope.recentRepoData = res.data; + $scope.mostRecentData = []; + $scope.mostRecentSelector = "Unknown"; + $scope.pitmutantLineData = []; + $scope.jacocoLineData = []; + $scope.date = ""; + $scope.dataset = []; + } + }); + $scope.showPitMutant = true; + $scope.switchTab = function() { + $scope.showPitMutant = !$scope.showPitMutant; + } + + $scope.generateRandomColor = function() { + return '#'+(Math.random()*0xFFFFFF<<0).toString(16); + } + + /*Graphs*/ + $scope.addDonutGraph = function(elemId, donutdata, title, label) { + new Chart(document.getElementById(elemId), { + type: "doughnut", + data: { + datasets: [{ + data: donutdata, + backgroundColor: [ + "#007bff", + "#28a745", + "#e53935", + "#c3e6cb", + "#dc3545", + "#6c757d" + ] + }], + // These labels appear in the legend and in the tooltips when hovering different arcs + labels: label + }, + responsive: true, + maintainAspectRatio: true, + options: { + legend: { + position: 'bottom' + }, + title: { + display: true, + text: title + } + } + }); + }; + + $scope.addLineGraph = function(elemId, lineData, label, nameLabel, color) { + new Chart(document.getElementById(elemId), { + type: "line", + data: { + labels: label, + datasets: [{ + data: lineData, + label: nameLabel, + borderColor: color, + backgroundColor: color, + fill: true + }] + }, + responsive: true, + maintainAspectRatio: true, + options: { + scales: { + xAxes: [{ + scaleLabel: { + display: true, + labelString: 'Date' + }, + ticks: { + autoSkip: true, + maxTicksLimit: 10 + } + }] + }, + title: { + display: true + }, + layout: { + padding: 0, + margin: 0 + } + } + }); + } + + $scope.addBarGraph = function(elemId,datasets,labels) { + console.log(datasets); + console.log(labels); + new Chart(document.getElementById(elemId), { + type: "bar", + data: { + labels: labels, + datasets: datasets + }, + responsive: true, + maintainAspectRatio: true, + options: { + scales: { + xAxes: [{ + stacked: true + }], + yAxes: [{ + stacked: true + }] + } + } + }); + } +}); \ No newline at end of file diff --git a/dspot-web/public/stylesheets/style.css b/dspot-web/public/stylesheets/style.css new file mode 100644 index 000000000..9453385b9 --- /dev/null +++ b/dspot-web/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/dspot-web/routes/index.js b/dspot-web/routes/index.js new file mode 100644 index 000000000..dc2dc361a --- /dev/null +++ b/dspot-web/routes/index.js @@ -0,0 +1,15 @@ +var express = require('express'); +var router = express.Router(); + +let methods = require('../controllers/methods') +/* GET home page. */ +router.get('/', methods.get_page); +router.get('/repos',methods.get_page); +router.get('/about',methods.get_page); +router.get('/data/:state',methods.get_ReposData); +router.get('/reposInfo',methods.get_reposInfoData); +router.get('/repo/*',methods.get_reposTemplatePage); +router.get('/repodata/:user/:reponame/:branchname',methods.get_repoInfoData) /*Still using ?*/ +router.post('/reposubmit',methods.post_submitRepo) + +module.exports = router; diff --git a/dspot-web/routes/users.js b/dspot-web/routes/users.js new file mode 100644 index 000000000..623e4302b --- /dev/null +++ b/dspot-web/routes/users.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +router.get('/', function(req, res, next) { + res.send('respond with a resource'); +}); + +module.exports = router; diff --git a/dspot-web/screenshots/pic1.png b/dspot-web/screenshots/pic1.png new file mode 100644 index 000000000..28a11bcb7 Binary files /dev/null and b/dspot-web/screenshots/pic1.png differ diff --git a/dspot-web/screenshots/pic2.png b/dspot-web/screenshots/pic2.png new file mode 100644 index 000000000..5f4efacfe Binary files /dev/null and b/dspot-web/screenshots/pic2.png differ diff --git a/dspot-web/screenshots/pic3.png b/dspot-web/screenshots/pic3.png new file mode 100644 index 000000000..b97119126 Binary files /dev/null and b/dspot-web/screenshots/pic3.png differ diff --git a/dspot-web/screenshots/pic4.png b/dspot-web/screenshots/pic4.png new file mode 100644 index 000000000..b336260c6 Binary files /dev/null and b/dspot-web/screenshots/pic4.png differ diff --git a/dspot-web/views/error.pug b/dspot-web/views/error.pug new file mode 100644 index 000000000..51ec12c6a --- /dev/null +++ b/dspot-web/views/error.pug @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/dspot-web/views/index.pug b/dspot-web/views/index.pug new file mode 100644 index 000000000..3d63b9a04 --- /dev/null +++ b/dspot-web/views/index.pug @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/dspot-web/views/layout.pug b/dspot-web/views/layout.pug new file mode 100644 index 000000000..15af079bf --- /dev/null +++ b/dspot-web/views/layout.pug @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content