diff --git a/README.md b/README.md
index 434d9285..69d54c9d 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ Clone the repository.
$> git clone https://github.com/SOCR/SOCRAT.git
Now, lets install all the dependencies. Go into the root folder,
+
$> cd SOCRAT
$> npm install
$> bower install
@@ -72,7 +73,7 @@ Copyright and License
**The LGPL v3.0 License**
-Copyright (c) 2013-2015 Statistics Online Computational Resource [(SOCR)](http://www.StatisticsResource.org)
+Copyright (c) 2013-2016 Statistics Online Computational Resource [(SOCR)](http://www.StatisticsResource.org)
All SOCR programs, materials, tools and resources are developed by and freely disseminated to the entire community.
Users may revise, extend, redistribute, modify under the terms of the Lesser GNU General Public License
diff --git a/app/app.coffee b/app/app.coffee
index 3fca2c7e..cefba07b 100644
--- a/app/app.coffee
+++ b/app/app.coffee
@@ -240,10 +240,9 @@ App.run([
core.register 'charts', charts
core.start 'charts'
+
#core.register 'importer', importer
#core.start 'importer'
- core.register 'charts', charts
- core.start 'charts'
# add module to the list of Tools to appear in Tools tab dropdown
tools = [
diff --git a/app/partials/analysis-nav.jade b/app/partials/analysis-nav.jade
index 4fd690bb..1d4b89bf 100644
--- a/app/partials/analysis-nav.jade
+++ b/app/partials/analysis-nav.jade
@@ -5,7 +5,7 @@ ul.nav.navbar-nav
li(ng-class="getClass('/wrangleData')")
a(ng-href='#/wrangleData') Wrangle Data
li.dropdown
- a(href="#", data-toggle="dropdown", role="button", aria-haspopup="true", aria-expanded="false").dropdown-toggle
+ a(href="", data-toggle="dropdown", role="button", aria-haspopup="true", aria-expanded="false").dropdown-toggle
| Tools
span.caret
ul.dropdown-menu
diff --git a/app/partials/analysis/charts/sidebar.jade b/app/partials/analysis/charts/sidebar.jade
index 99368294..8b336ad8 100644
--- a/app/partials/analysis/charts/sidebar.jade
+++ b/app/partials/analysis/charts/sidebar.jade
@@ -4,6 +4,9 @@ div(ng-controller="sideChartsCtrl")
h4 Variables
div
p {{graphSelect.message}}
+ //div(ng-show=categorical).form-inline
+ input(ng-model="selectorCat" type="checkbox" id="categorical")
+ p X is a categorical variable
div.form-inline
div.form-horizontal(ng-show="graphSelect.x")
label(for = "x") Add X
@@ -11,6 +14,10 @@ div(ng-controller="sideChartsCtrl")
div.form-horizontal(ng-show="graphSelect.y")
label(for = "y") Add Y
select#y.form-control(ng-change="changeVar(selector2,headers,'y');" ng-model="selector2" ng-options="v as v.value for v in headers")
+ div(ng-show = "labelVar")
+ input(ng-model="labelCheck" type="checkbox" id = "label")
+ p Check if Y variable is the labels column
+
div.form-horizontal(ng-show="graphSelect.z")
label(for = "z") Add Radius
select#z.form-control(ng-change="changeVar(selector3,headers,'z');" ng-model="selector3" ng-options="v as v.value for v in headers")
diff --git a/app/partials/analysis/getData/main.jade b/app/partials/analysis/getData/main.jade
index b9920fe1..0606413b 100644
--- a/app/partials/analysis/getData/main.jade
+++ b/app/partials/analysis/getData/main.jade
@@ -4,7 +4,7 @@ div(ng-controller='getDataMainCtrl')
p.lead To perform any analysis, you need data to begin with. Choose one of the available methods
|to get the data into the web-app.
- form.socrData(collapse="showState.socrData")
+ form.socrData(uib-collapse="showState.socrData")
legend SOCR Datasets
div.form-group
select.form-control(
@@ -12,7 +12,7 @@ div(ng-controller='getDataMainCtrl')
ng-options="item as item.name for item in socrDatasets track by item.id")
button.btn.btn-primary(ng-click="getSocrData()") Load
- div.worldBank(collapse="showState.worldBank")
+ div.worldBank(uib-collapse="showState.worldBank")
h3 WorldBank Data
span Size:
input(type="text",class="input-mini", ng-model="size")
@@ -21,8 +21,8 @@ div(ng-controller='getDataMainCtrl')
option(value='2.4_OOSC.RATE') Out of School Children rate
button.btn.btn-primary(ng-click="getWB()") Load
- div.generate(collapse="showState.generate")
- ul.nav.nav-pills.nav-stacked
+ div.generate(uib-collapse="showState.generate")
+ ul.nav.nav-pills.nav-stacked.disabled
li
a Binomial Coin Toss
li
@@ -34,10 +34,14 @@ div(ng-controller='getDataMainCtrl')
li
a The Beta Distribution Experiment
- br
- handsontable(purpose="json")
-
-
-
-
+ form.jsonUrl(uib-collapse="showState.jsonParse")
+ legend Enter URL to parse JSON file
+ div.form-group
+ label(for="getDataJsonUrlInput").sr-only Enter URL
+ div.btn-group.btn-group-justified(role="group")
+ input#getDataJsonUrlInput(type='text', placeholder='URL to JSON ...', ng-model="jsonUrl").form-control
+ span#getDataJsonUrlClear(ng-click="jsonUrl=''").glyphicon.glyphicon-remove-circle
+ button.btn.btn-primary(ng-click="getJsonByUrl()") Parse
+ br
+ handsontable(purpose="json")
\ No newline at end of file
diff --git a/app/partials/analysis/getData/sidebar.jade b/app/partials/analysis/getData/sidebar.jade
index 5cdc84d5..de05de60 100644
--- a/app/partials/analysis/getData/sidebar.jade
+++ b/app/partials/analysis/getData/sidebar.jade
@@ -2,29 +2,35 @@ div(ng-controller="getDataSidebarCtrl")
p. Data input
div.list-group
a#getDataGrid.list-group-item(
- href="#"
+ href=""
ng-click="show('grid')"
ng-class="{active:selected == 'getDataGrid'}")
| Copy-paste to spreadsheet
a#getDataSocrData.list-group-item(
- href="#"
+ href=""
ng-click="show('socrData')"
ng-class="{active:selected == 'getDataSocrData'}")
| Load SOCR dataset
a#getDataWorldBank.list-group-item(
- href="#"
+ href=""
ng-click="show('worldBank')"
ng-class="{active:selected == 'getDataWorldBank'}")
| Load data from WorldBank
- a#getDataGenerate.list-group-item.disabled(
- href="#"
+ a#getDataGenerate.list-group-item(
+ href=""
ng-click="show('generate')"
ng-class="{active:selected == 'getDataGenerate'}")
| Use data generator
- a.list-group-item.disabled(href="#", ng-click="getJson()", ng-class="{active:selected == 'getDataJson'}")
- | Parse JSON fileParse SOCR Data page
- a.list-group-item.disabled(href="#", ng-click="save()", ng-class="{active:selected == 'getDataSocrPage'}")
- | Parse SOCR Data page
+ a#getDataJson.list-group-item(
+ href=""
+ ng-click="show('jsonParse')"
+ ng-class="{active:selected == 'getDataJson'}")
+ | Parse JSON file by URL
+ a#getDataSocrPage.list-group-item.disabled(
+ href=""
+ ng-click="save()"
+ ng-class="{active:selected == 'getDataSocrPage'}")
+ | Parse SOCR Data page by URL
//
accordion(close-others="true")
diff --git a/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade b/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade
index d85813b4..3ef32d46 100644
--- a/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade
+++ b/app/partials/analysis/tools/machineLearning/kMeans/sidebar.jade
@@ -1,53 +1,55 @@
div(ng-controller="kMeansSidebarCtrl")
form
legend Parameters
- div.checkbox
- label
- input(type="checkbox", ng-model="wholedataseton")
- | Cluster whole dataset (visualize 2D projection)
- div.form-group
- label X column
- //- show data header column names
- select(ng-model="xCol", ng-options="col for col in cols").form-control
- div.form-group
- label Y column
- //- show data header column names
- select(ng-model="yCol", ng-options="col for col in cols").form-control
- div.form-group
- fieldset(ng-disabled="!labelson")
- label Labels column
+ fieldset(ng-disabled="kmeanson")
+ div.checkbox
+ label
+ input(type="checkbox", ng-model="wholedataseton")
+ | Cluster whole dataset (visualize 2D projection)
+ div.form-group
+ label X column
//- show data header column names
- select(ng-model="labelCol", ng-options="col for col in cols").form-control
- div.checkbox
- label
- input(type="checkbox", ng-model="labelson")
- | Labels present
- div.form-group
- label k
- select(ng-model="k", ng-options="k for k in ks").form-control
- div.checkbox(ng-hide="!labelson")
- label
- input(
- type="checkbox"
- ng-model="accuracyon"
- ng-disabled="k !== numUniqueLabels.num || labelCol !== numUniqueLabels.labelCol")
- | Compute accuracy
- div.form-group
- label Distance
- select(ng-model="dist", ng-options="dist for dist in distances").form-control
- div.form-group
- label Initialization
- select(ng-model="initMethod", ng-options="init for init in inits").form-control
- button.btn.btn-info(
- type="submit"
- aria-label="Get k value"
- ng-click="detectKValue()"
- ng-disabled="!labelson")
- | Detect k
- button.btn.btn-primary(
- type="submit"
- aria-label="Run k-Means"
- ng-click="run()"
- ng-disabled="kmeanson")
- | Run
- span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running")
+ select(ng-model="xCol", ng-options="col for col in cols", ng-change="updateDataPoints()").form-control
+ div.form-group
+ label Y column
+ //- show data header column names
+ select(ng-model="yCol", ng-options="col for col in cols", ng-change="updateDataPoints()").form-control
+ div.form-group
+ fieldset(ng-disabled="!labelson")
+ label Labels column
+ //- show data header column names
+ select(ng-model="labelCol", ng-options="col for col in cols").form-control
+ div.checkbox
+ label
+ input(type="checkbox", ng-model="labelson")
+ | Labels present
+ div.form-group
+ fieldset.form-inline
+ label k
+ select(ng-model="k", ng-options="k for k in ks").form-control
+ button.btn.btn-info(
+ type="submit"
+ aria-label="Get k value"
+ ng-click="detectKValue()"
+ ng-disabled="!labelson")
+ | Detect k
+ div.checkbox(ng-hide="!labelson")
+ label
+ input(
+ type="checkbox"
+ ng-model="accuracyon"
+ ng-disabled="k !== numUniqueLabels.num || labelCol !== numUniqueLabels.labelCol")
+ | Compute accuracy
+ div.form-group
+ label Distance
+ select(ng-model="dist", ng-options="dist for dist in distances").form-control
+ div.form-group
+ label Initialization
+ select(ng-model="initMethod", ng-options="init for init in inits").form-control
+ button.btn.btn-primary(
+ type="submit"
+ aria-label="Run k-Means"
+ ng-click="run()"
+ ng-disabled="kmeanson")
+ | Run
+ span.glyphicon.glyphicon-refresh(aria-hidden="true", ng-class="running")
diff --git a/app/scripts/analysis/charts/charts.coffee b/app/scripts/analysis/charts/charts.coffee
index 4641ecf3..9784c7a0 100644
--- a/app/scripts/analysis/charts/charts.coffee
+++ b/app/scripts/analysis/charts/charts.coffee
@@ -83,9 +83,9 @@ charts = angular.module('app_analysis_charts', [])
name: 'Bar Graph'
value: 0
x: true
- y: false
+ y: true
z: false
- message: "Choose a numerical variable for x and a categorical variable for y."
+ message: "Use option x to choose a numerical or categorical variable, or choose one categorical variable and one numerical variable."
,
name: 'Scatter Plot'
value: 1
@@ -114,6 +114,13 @@ charts = angular.module('app_analysis_charts', [])
y: false
z: false
message: "Choose one variable to put into a pie chart."
+ ,
+ name: 'Stream Graph'
+ value: 5
+ x: true
+ y: true
+ z: false
+ message: "Choose two numerical variables"
]
$scope.graphSelect = {}
@@ -130,7 +137,7 @@ charts = angular.module('app_analysis_charts', [])
for i in [1...len] by 1
tmp =
- x: parseFloat _chartData[$scope.graphInfo.x][i].value
+ x: _chartData[$scope.graphInfo.x][i].value
obj.push tmp
else if $scope.graphInfo.y isnt "" and $scope.graphInfo.z is ""
@@ -138,8 +145,8 @@ charts = angular.module('app_analysis_charts', [])
for i in [1...len] by 1
tmp =
- x: parseFloat _chartData[$scope.graphInfo.x][i].value
- y: parseFloat _chartData[$scope.graphInfo.y][i].value
+ x: _chartData[$scope.graphInfo.x][i].value
+ y: _chartData[$scope.graphInfo.y][i].value
obj.push tmp
else
@@ -147,9 +154,9 @@ charts = angular.module('app_analysis_charts', [])
for i in [1...len] by 1
tmp =
- x: parseFloat _chartData[$scope.graphInfo.x][i].value
- y: parseFloat _chartData[$scope.graphInfo.y][i].value
- z: parseFloat _chartData[$scope.graphInfo.z][i].value
+ x: _chartData[$scope.graphInfo.x][i].value
+ y: _chartData[$scope.graphInfo.y][i].value
+ z: _chartData[$scope.graphInfo.z][i].value
obj.push tmp
return obj
@@ -163,8 +170,12 @@ charts = angular.module('app_analysis_charts', [])
$rootScope.$broadcast 'charts:graphDiv', results
+ $scope.labelVar = false
+ $scope.labelCheck = null
$scope.changeName = () ->
$scope.graphInfo.graph = $scope.graphSelect.name
+
+
$scope.createGraph()
$scope.changeVar = (selector,headers, ind) ->
@@ -172,6 +183,7 @@ charts = angular.module('app_analysis_charts', [])
if selector.value is h.value then $scope.graphInfo[ind] = parseFloat h.key
$scope.createGraph()
+
sb = ctrlMngr.getSb()
token = sb.subscribe
@@ -211,34 +223,10 @@ charts = angular.module('app_analysis_charts', [])
format: _format
])
-#.factory 'stackedBar', [
-# () ->
-# _drawStack = (width, height, ) ->
-# x = d3.scale.ordinal()
-# .rangeRoundBands([0, width], .1)
-#
-# y = d3.scale.linear()
-# .rangeRound([height, 0])
-#
-# color = d3.scale.ordinal()
-# .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"])
-#
-# xAxis = d3.svg.axis()
-# .scale(x)
-# .orient("bottom")
-#
-# yAxis = d3.svg.axis()
-# .scale(y)
-# .orient("left")
-# .tickFormat(d3.format(".2s"))
-#
-#
-# drawStack:_drawStack
-#]
-
-.factory 'scatterplot', [
+
+.factory 'scatterPlot', [
() ->
- _drawScatterplot = (data,ranges,width,height,_graph,container,gdata) ->
+ _drawScatterPlot = (data,ranges,width,height,_graph,container,gdata) ->
x = d3.scale.linear().domain([ranges.xMin,ranges.xMax]).range([ 0, width ])
y = d3.scale.linear().domain([ranges.yMin,ranges.yMax]).range([ height, 0 ])
@@ -254,8 +242,8 @@ charts = angular.module('app_analysis_charts', [])
yMap = (d)-> y yValue(d)
# set up fill color
- cValue = (d)-> d.y
- color = d3.scale.category10()
+ #cValue = (d)-> d.y
+ #color = d3.scale.category10()
# x axis
_graph.append("g")
@@ -265,7 +253,6 @@ charts = angular.module('app_analysis_charts', [])
.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
-# .style('text-anchor', 'end')
.text gdata.xLab.value
# y axis
@@ -278,14 +265,12 @@ charts = angular.module('app_analysis_charts', [])
.attr('y', -50 )
.attr('x', -(height / 2))
.attr("dy", ".71em")
-# .style("text-anchor", "end")
.text gdata.yLab.value
# add the tooltip area to the webpage
tooltip = container
.append('div')
.attr('class', 'tooltip')
- # .style('opacity', 0)
# draw dots
_graph.selectAll('.dot')
@@ -295,7 +280,8 @@ charts = angular.module('app_analysis_charts', [])
.attr('r', 5)
.attr('cx', xMap)
.attr('cy', yMap)
- .style('fill', (d)->color cValue(d))
+ .style('fill', 'DodgerBlue')
+ .attr('opacity', '0.5')
.on('mouseover', (d)->
tooltip.transition().duration(200).style('opacity', .9)
tooltip.html('
(' + xValue(d)+ ',' + yValue(d) + ')
')
@@ -303,30 +289,26 @@ charts = angular.module('app_analysis_charts', [])
.on('mouseout', (d)->
tooltip. transition().duration(500).style('opacity', 0))
-
-
- drawScatterplot: _drawScatterplot
-
+ drawScatterPlot: _drawScatterPlot
]
.factory 'histogram',[
() ->
- _drawHist = (_graph,data,container,gdata,width,height) ->
+ _drawHist = (_graph,data,container,gdata,width,height,ranges) ->
container.append('input').attr('id', 'slider').attr('type','range').attr('min', '1').attr('max','10').attr('step', '1').attr('value','5')
bins = null
dataHist = null
arr = data.map (d) -> parseFloat d.x
- x = d3.scale.linear().domain([0,d3.max arr]).range([0,width])
-
+ x = d3.scale.linear().domain([ranges.xMin, ranges.xMax]).range([0,width])
plotHist = (bins) ->
$('#slidertext').remove()
container.append('text').attr('id', 'slidertext').text('Bin Slider: '+bins).attr('position','relative').attr('left', '50px')
dataHist = d3.layout.histogram().bins(bins)(arr)
- y = d3.scale.linear().domain([0, d3.max dataHist.map (i) -> i.length]).range([0, height])
+ y = d3.scale.linear().domain([0,d3.max dataHist.map (i) -> i.length]).range([height,0])
yAxis = d3.svg.axis().scale(y).orient("left")
xAxis = d3.svg.axis().scale(x).orient("bottom")
@@ -343,7 +325,6 @@ charts = angular.module('app_analysis_charts', [])
.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
-# .style('text-anchor', 'end')
.text gdata.xLab.value
# y axis
@@ -356,7 +337,6 @@ charts = angular.module('app_analysis_charts', [])
.attr('y', -50 )
.attr('x', -(height / 2))
.attr("dy", ".71em")
-# .style("text-anchor", "end")
.text "Count"
bar = _graph.selectAll('.bar')
@@ -365,19 +345,21 @@ charts = angular.module('app_analysis_charts', [])
bar.enter()
.append("g")
+ rect_width = width/bins
bar.append('rect')
-# .style('fill', 'steelblue')
- .attr('x', (d,i) -> x d.x)
+ .attr('x', (d) -> x d.x)
.attr('y', (d) -> height - y d.y)
- .attr('width', (d) -> x d.dx)
+ .attr('width', rect_width)
.attr('height', (d) -> y d.y)
+ .attr("stroke","white")
+ .attr("stroke-width",1)
.on('mouseover', () -> d3.select(this).transition().style('fill', 'orange'))
.on('mouseout', () -> d3.select(this).transition().style('fill', 'steelblue'))
bar.append('text')
- .attr('x', (d,i) -> x d.x)
+ .attr('x', (d) -> x d.x)
.attr('y', (d) -> height - y d.y)
- .attr('dx', (d) -> .5*x d.dx)
+ .attr('dx', (d) -> .5*rect_width)
.attr('dy', '20px')
.attr('fill', '#fff')
.attr('text-anchor', 'middle')
@@ -396,73 +378,70 @@ charts = angular.module('app_analysis_charts', [])
.factory 'pie', [
() ->
- pieData = null
+ valueSum = 0
makePieData = (data) ->
- pieMax = d3.max(data, (d)->parseFloat d.x)
- pieMin = d3.min(data, (d)->parseFloat d.x)
- maxPiePieces = 7 # set magic constant to variable
- rangeInt = Math.ceil((pieMax - pieMin)/maxPiePieces)
- console.log rangeInt
- piePieces = new Array(maxPiePieces - 1) # create array with numbers of pie pieces
- for i in [0..maxPiePieces-1] by 1
- piePieces[i] = []
-
- for el in data
- index = Math.floor((el.x - pieMin)/rangeInt)
- piePieces[index].push(el.x) # assign each el.x to a piePiece
- console.log "piePieces[" + index + "]=" + piePieces[index]
-
- obj = {}
- for i in [0..maxPiePieces-1] by 1
- obj[i] = piePieces[i].length
-
- pieData = d3.entries obj
- for d in pieData
- d.key = (d.key*rangeInt)+pieMin+"-"+((d.key-1)*rangeInt)+pieMin
- return pieData
+ valueSum = 0
+ counts = {}
+ if(!isNaN(data[0].x)) # data is number
+ pieMax = d3.max(data, (d)-> parseFloat d.x)
+ pieMin = d3.min(data, (d)-> parseFloat d.x)
+ maxPiePieces = 7 # set magic constant to variable
+ rangeInt = Math.ceil((pieMax - pieMin) / maxPiePieces)
+ counts = {}
+ for val in data
+ index = Math.floor((val.x - pieMin) / rangeInt)
+ groupName = index + "-" + (index + rangeInt)
+ #console.log groupName
+ counts[groupName] = counts[groupName] || 0
+ counts[groupName]++
+ valueSum++
+ else # data is string
+ for i in [0..data.length-1] by 1
+ currentVar = data[i].x
+ counts[currentVar] = counts[currentVar] || 0
+ counts[currentVar]++
+ valueSum++
+
+ obj = d3.entries counts
+ return obj
_drawPie = (data,width,height,_graph) ->
- makePieData(data)
radius = Math.min(width, height) / 2
-
arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(0)
- labelArc = d3.svg.arc()
- .outerRadius(radius-10)
- .innerRadius(radius-10)
-
- color = d3.scale.ordinal().range(["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"])
-
+ #color = d3.scale.ordinal().range(["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"])
+ color = d3.scale.category20c()
arcOver = d3.svg.arc()
.outerRadius(radius + 10)
- # .innerRadius(0+10)
pie = d3.layout.pie()
-#.value (d) -> d.count
- .value (d) -> parseFloat d.value
- type = (d) ->
- d.y = +d.y
- return d
+ .value((d)-> d.value)
+ .sort(null)
+
+ formatted_data = makePieData(data)
+ #console.log formatted_data
+ #console.log pie(formatted_data)
arcs = _graph.selectAll(".arc")
- .data(pie(pieData))
+ .data(pie(formatted_data))
.enter()
.append('g')
.attr("class", "arc")
arcs.append('path')
.attr('d', arc)
- .attr('fill', (d) -> color(d.data.key))
- .on('mouseenter', (d) -> d3.select(this).attr("stroke","white").transition().attr("d", arcOver).attr("stroke-width",5))
+ .attr('fill', (d) -> color(d.data.value))
+ .on('mouseenter', (d) -> d3.select(this).attr("stroke","white") .transition().attr("d", arcOver).attr("stroke-width",3))
.on('mouseleave', (d) -> d3.select(this).transition().attr('d', arc).attr("stroke", "none"))
arcs.append('text')
.attr('id','tooltip')
.attr('transform', (d) -> 'translate('+arc.centroid(d)+')')
.attr('text-anchor', 'middle')
- .text (d) -> d.data.key
+ .text (d) -> d.data.key + ': ' + parseFloat(100*d.data.value/valueSum).toFixed(2) + '%'
+
drawPie: _drawPie
]
@@ -474,16 +453,26 @@ charts = angular.module('app_analysis_charts', [])
xAxis = d3.svg.axis().scale(x).orient('bottom')
yAxis = d3.svg.axis().scale(y).orient('left')
- r = d3.scale.linear()
- .domain([d3.min(data, (d)-> parseFloat d.z), d3.max(data, (d)-> parseFloat d.z)])
- .range([3,15])
-
- rValue = (d)->parseFloat d.z
+ zIsNumber = !isNaN(data[0].z)
+
+ r = 0
+ rValue = 0
+ if(zIsNumber)
+ r = d3.scale.linear()
+ .domain([d3.min(data, (d)-> parseFloat d.z), d3.max(data, (d)-> parseFloat d.z)])
+ .range([3,15])
+ rValue = (d) -> parseFloat d.z
+ else
+ r = d3.scale.linear()
+ .domain([5, 5])
+ .range([3,15])
+ rValue = (d) -> d.z
tooltip = container
.append('div')
.attr('class', 'tooltip')
- # .style('opacity', 0)
+
+ color = d3.scale.category10()
# x axis
_graph.append("g")
@@ -493,7 +482,6 @@ charts = angular.module('app_analysis_charts', [])
.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
-# .style('text-anchor', 'end')
.text gdata.xLab.value
# y axis
@@ -506,21 +494,31 @@ charts = angular.module('app_analysis_charts', [])
.attr('y', -50 )
.attr('x', -(height / 2))
.attr("dy", ".71em")
-# .style("text-anchor", "end")
.text gdata.yLab.value
-
# create circle
_graph.selectAll('.circle')
.data(data)
.enter().append('circle')
- .attr('fill', 'yellow')
+ .attr('fill',
+ if(zIsNumber)
+ 'yellow'
+ else
+ (d) -> color(d.z))
.attr('opacity', '0.7')
- .attr('stroke', 'orange')
+ .attr('stroke',
+ if(zIsNumber)
+ 'orange'
+ else
+ (d) -> color(d.z))
.attr('stroke-width', '2px')
.attr('cx', (d) -> x d.x)
.attr('cy', (d) -> y d.y)
- .attr('r', (d) -> r d.z)
+ .attr('r', (d) ->
+ if(zIsNumber) # if d.z is number, use d.z as radius
+ r d.z
+ else # else, set radius to be 8
+ 8)
.on('mouseover', (d) ->
tooltip.transition().duration(200).style('opacity', .9)
tooltip.html(''+gdata.zLab.value+': '+ rValue(d)+'
')
@@ -532,57 +530,296 @@ charts = angular.module('app_analysis_charts', [])
.factory 'bar', [
() ->
+ _setAxisPar = (x,y,xAxis,yAxis,type, width, height) ->
+ ord = d3.scale.ordinal()
+ lin = d3.scale.linear()
+
+ switch type
+ when "xCat" or "xCatAndyNum"
+ x = ord.rangeRoundBands([0, width], .1)
+ y = lin.range([ height, 0 ])
+ when "xNum" or "xNumAndyCat"
+ x = lin.range([ 0, width ])
+ y = ord.rangeRoundBands([height, 0], .1)
+ when "xNumAndyNum"
+ x = lin.range([ 0, width ])
+ y = lin.range([ height, 0 ])
+ else
+ alert "Two categorical variables"
+
+
+ xAxis = d3.svg.axis().scale(x).orient('bottom')
+ yAxis = d3.svg.axis().scale(y).orient('left')
+
_drawBar = (width,height,data,_graph,gdata) ->
x = d3.scale.linear().range([ 0, width ])
y = d3.scale.linear().range([ height, 0 ])
+
+
xAxis = d3.svg.axis().scale(x).orient('bottom')
yAxis = d3.svg.axis().scale(y).orient('left')
x.domain([d3.min(data, (d)->parseFloat d.x), d3.max(data, (d)->parseFloat d.x)])
y.domain([d3.min(data, (d)->parseFloat d.y), d3.max(data, (d)->parseFloat d.y)])
+ #without y
+ if !data[0].y
+ #Works
+ if isNaN data[0].x
+ counts = {}
+ for i in [0..data.length-1] by 1
+ currentVar = data[i].x
+ counts[currentVar] = counts[currentVar] || 0
+ counts[currentVar]++
+ counts = d3.entries counts
+# console.log counts
+ x = d3.scale.ordinal().rangeRoundBands([0, width], .1)
+ xAxis = d3.svg.axis().scale(x).orient('bottom')
+ x.domain(counts.map (d) -> d.key)
+ y.domain([d3.min(counts, (d)-> parseFloat d.value), d3.max(counts, (d)-> parseFloat d.value)])
+
+ _graph.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call xAxis
+ .append('text')
+ .attr('class', 'label')
+ .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
+ .text gdata.xLab.value
+
+ _graph.append('g')
+ .attr('class', 'y axis')
+ .call yAxis
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('y', -50 )
+ .attr('x', -(height / 2))
+ .attr('dy', '1em')
+ .text "Count"
+
+ # create bar elements
+ _graph.selectAll('rect')
+ .data(counts)
+ .enter().append('rect')
+ .attr('class', 'bar')
+ .attr('x',(d)-> x d.key )
+ .attr('width', x.rangeBand())
+ .attr('y', (d)-> y d.value )
+ .attr('height', (d)-> Math.abs(height - y d.value))
+ .attr('fill', 'steelblue')
+
+
+ else #data is numerical and only x. height is rect width, width is x of d.x,
+ #y becomes the categorical
+ y = d3.scale.ordinal().rangeRoundBands([height, 0], .1)
+ yAxis = d3.svg.axis().scale(y).orient('left')
+
+ y.domain((d) -> d.x)
+
+ _graph.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call xAxis
+ .append('text')
+ .attr('class', 'label')
+ .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
+ .text gdata.xLab.value
+
+ _graph.append('g')
+ .attr('class', 'y axis')
+ .call yAxis
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('y', -50 )
+ .attr('x', -(height / 2))
+ .attr('dy', '1em')
+ .text "Null"
+
+ rectWidth = height/data.length
+ # create bar elements
+ _graph.selectAll('rect')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', 'bar')
+ #.attr('x',(d)-> x d.x )
+ .attr('width', (d)-> x d.x)
+ .attr('y', (d,i)-> i*rectWidth)
+ .attr('height', rectWidth)
+ .attr('fill', 'steelblue')
+
+
+
+ #with y
+ else
+ #y is categorical
+ if isNaN data[0].y
+
+ y = d3.scale.ordinal().rangeRoundBands([0, height], .1)
+ y.domain(data.map (d) -> d.y)
+ yAxis = d3.svg.axis().scale(y).orient('left')
+
+ _graph.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call xAxis
+ .append('text')
+ .attr('class', 'label')
+ .attr('x', width-80)
+ .attr('y', 30)
+ .text gdata.xLab.value
+
+ _graph.append('g')
+ .attr('class', 'y axis')
+ .call yAxis
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr("x", -80)
+ .attr("y", -40)
+ .attr('dy', '1em')
+ .text gdata.yLab.value
+
+ _graph.selectAll('rect')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', 'bar')
+ #.attr('x',(d)-> x d.x )
+ .attr('width', (d) -> Math.abs(x d.x))
+ .attr('y', (d)-> y d.y )
+ .attr('height', y.rangeBand())
+ .attr('fill', 'steelblue')
+
+
+ else if !isNaN data[0].y
+ if isNaN data[0].x
+ console.log "xCat and yNum"
+ x = d3.scale.ordinal().rangeRoundBands([0, width], .1)
+ x.domain(data.map (d) -> d.x)
+ xAxis = d3.svg.axis().scale(x).orient('bottom')
+ #y.domain([d3.min(data, (d)-> parseFloat d.y), d3.max(data, (d)-> parseFloat d.y)])
+
+ _graph.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call xAxis
+ .append('text')
+ .attr('class', 'label')
+ .attr('transform', 'translate(' + (width / 2) + ',' + 40 + ')')
+ .text gdata.xLab.value
+
+ _graph.append('g')
+ .attr('class', 'y axis')
+ .call yAxis
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr('y', -50 )
+ .attr('x', -(height / 2))
+ .attr('dy', '1em')
+ .text "Count"
+
+ # create bar elements
+ _graph.selectAll('rect')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', 'bar')
+ .attr('x',(d)-> x d.x )
+ .attr('width', x.rangeBand())
+ .attr('y', (d)-> y d.y )
+ .attr('height', (d)-> Math.abs(height - y d.y))
+ .attr('fill', 'steelblue')
+ else
+
+ #else if !isNaN data[0].y and !isNaN data[0].x
+ rectWidth = width / data.length
+
+ _graph.append('g')
+ .attr('class', 'x axis')
+ .attr('transform', 'translate(0,' + height + ')')
+ .call xAxis
+ .append('text')
+ .attr('class', 'label')
+ .attr('x', width-80)
+ .attr('y', 30)
+ .text gdata.xLab.value
+
+ _graph.append('g')
+ .attr('class', 'y axis')
+ .call yAxis
+ .append('text')
+ .attr('transform', 'rotate(-90)')
+ .attr("x", -80)
+ .attr("y", -40)
+ .attr('dy', '1em')
+ .text gdata.yLab.value
+
+
+ # create bar elements
+ _graph.selectAll('rect')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', 'bar')
+ .attr('x',(d)-> x d.x )
+ .attr('width', rectWidth)
+ .attr('y', (d)-> y d.y )
+ .attr('height', (d)-> Math.abs(height - y d.y) )
+ .attr('fill', 'steelblue')
- _graph.append('g')
- .attr('class', 'x axis')
- .attr('transform', 'translate(0,' + height + ')')
- .call xAxis
- .append('text')
- .attr('class', 'label')
- .attr('x', width-80)
- .attr('y', 30)
- .text gdata.xLab.value
-
- _graph.append('g')
- .attr('class', 'y axis')
- .call yAxis
- .append('text')
- .attr('transform', 'rotate(-90)')
- .attr("x", -80)
- .attr("y", -40)
- .attr('dy', '1em')
- .text gdata.yLab.value
- rectWidth = width / data.length
- # create bar elements
- _graph.selectAll('rect')
- .data(data)
- .enter().append('rect')
- .attr('class', 'bar')
- .attr('x',(d)-> x d.x )
- .attr('width', rectWidth)
- .attr('y', (d)-> y d.y )
- .attr('height', (d)-> Math.abs(height - y d.y) )
- .attr('fill', 'steelblue')
drawBar: _drawBar
]
+.factory 'streamGraph', [
+ () ->
+
+ _randomSample = (data, n) ->
+ #take an array of objects, and the desired number of random ones. return array of objects
+ for d in data
+ d.x = +d.x
+ d.y = +d.y
+
+ random = []
+ for i in [0...n-1] by 1
+ random.push(data[Math.floor(Math.random() * data.length)])
+ return random
+
+ _streamGraph = (data,ranges,width,height,_graph) ->
+ n = 20
+ m = 100
+ stack = d3.layout.stack().offset('wiggle')
+ layers = stack(d3.range(n).map () -> _randomSample(data,m))
+ console.log layers
+ x = d3.scale.linear()
+ .domain([0, m - 1])
+ .range([0, width]);
+
+ y = d3.scale.linear()
+ .domain([0, d3.max(layers, (layer)-> d3.max(layer, (d) -> return d.y0 + d.y))])
+ .range([height, 0])
+
+ color = d3.scale.linear()
+ .range(["#aad", "#556"])
+
+ area = d3.svg.area()
+ .x((d) -> x d.x)
+ .y0((d) -> y d.y0)
+ .y1((d) -> y(d.y0 + d.y))
+
+ _graph.selectAll("path")
+ .data(layers)
+ .enter().append("path")
+ .attr("d", area)
+ .style("fill", () -> color(Math.random()))
+
+ streamGraph: _streamGraph
+]
+
.directive 'd3Charts', [
- 'scatterplot',
+ 'scatterPlot',
'histogram',
'pie',
'bubble',
- 'bar'
- (scatterplot,histogram,pie,bubble,bar) ->
+ 'bar',
+ 'streamGraph'
+ (scatterPlot,histogram,pie,bubble,bar,streamGraph) ->
restrict: 'E'
template: ""
link: (scope, elem, attr) ->
@@ -600,10 +837,14 @@ charts = angular.module('app_analysis_charts', [])
if newChartData
gdata = newChartData
data = newChartData.data
+# _label = null
+
+ console.log data
#id = '#'+ newInfo.name
container = d3.select(elem.find('div')[0])
container.selectAll('*').remove()
+ console.log "test"
svg = container.append('svg').attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom)
#svg.select("#remove").remove()
_graph = svg.append('g').attr("transform", "translate(" + margin.left + "," + margin.top + ")")
@@ -615,17 +856,21 @@ charts = angular.module('app_analysis_charts', [])
xMax: d3.max data, (d) -> parseFloat d.x
yMax: d3.max data, (d) -> parseFloat d.y
+# $scope.on 'Charts: labels y', (events, data) ->
+# _label = data
+
switch gdata.name
when 'Bar Graph'
bar.drawBar(width,height,data,_graph,gdata)
when 'Bubble Chart'
bubble.drawBubble(ranges,width,height,_graph,data,gdata,container)
when 'Histogram'
- histogram.drawHist(_graph,data,container,gdata,width,height)
+ histogram.drawHist(_graph,data,container,gdata,width,height,ranges)
when 'Pie Chart'
_graph = svg.append('g').attr("transform", "translate(300,250)").attr("id", "remove")
pie.drawPie(data,width,height,_graph)
when 'Scatter Plot'
- scatterplot.drawScatterplot(data,ranges,width,height,_graph,container,gdata)
+ scatterPlot.drawScatterPlot(data,ranges,width,height,_graph,container,gdata)
+ when 'Stream Graph'
+ streamGraph.streamGraph(data,ranges,width,height,_graph)
]
-
diff --git a/app/scripts/analysis/getData/getData.coffee b/app/scripts/analysis/getData/getData.coffee
index 41173ae7..5de01be0 100644
--- a/app/scripts/analysis/getData/getData.coffee
+++ b/app/scripts/analysis/getData/getData.coffee
@@ -106,10 +106,11 @@ getData = angular.module('app_analysis_getData', [
console.log 'handsontable data updated to db'
_setData = (data) ->
- console.log '%c inputCache set called for the project'+$stateParams.projectId+':'+$stateParams.forkId, 'color:steelblue'
+ console.log '%c inputCache set called for the project' + $stateParams.projectId + ':' + $stateParams.forkId,
+ 'color:steelblue'
# TODO: fix checking existance of parameters to default table name #SOCR-140
- if data? or $stateParams.projectId? or $stateParams.forkId?
+ if data? or $stateParams.projectId? or $stateParams.forkId?
_data = data unless data is 'edit'
# clear any previous db update broadcast messages
@@ -239,7 +240,7 @@ getData = angular.module('app_analysis_getData', [
# get the sandbox made for this module
# sb = getDataSb.getSb()
# console.log 'sandbox created'
- $scope.jsonUrl = 'url..'
+ $scope.jsonUrl = ''
flag = true
$scope.selected = null
@@ -265,12 +266,17 @@ getData = angular.module('app_analysis_getData', [
$scope.selected = 'getDataWorldBank'
$scope.$emit 'change in showStates', 'worldBank'
- when 'generate'
+ when 'generate'
$scope.selected = 'getDataGenerate'
$scope.$emit 'change in showStates', 'generate'
+ when 'jsonParse'
+ $scope.selected = 'getDataJson'
+ $scope.$emit 'change in showStates', 'jsonParse'
+
# getJson
$scope.getJson = ->
+ console.log 123
console.log $scope.jsonUrl
if $scope.jsonUrl is ''
@@ -359,6 +365,7 @@ getData = angular.module('app_analysis_getData', [
d3.text url,
(dataResults) ->
if dataResults?.length > 0
+ # parse to unnamed array
dataResults = d3.csv.parseRows dataResults
_data =
columnHeader: dataResults.shift()
@@ -376,12 +383,32 @@ getData = angular.module('app_analysis_getData', [
else
console.log 'rejected:' + msg
+ $scope.getJsonByUrl = ->
+ d3.json $scope.jsonUrl,
+ (dataResults) ->
+ if dataResults?.length > 0
+ _data =
+ columnHeader: dataResults.shift()
+ data: [null, dataResults]
+ # purpose is helps in pin pointing which
+ # handsontable directive to update.
+ purpose: 'json'
+ console.log 'resolved'
+ # pass a message to update the handsontable div
+ # data is the formatted data which plugs into the
+ # handontable.
+ $scope.$emit 'update handsontable', _data
+ # switch the accordion from getJson to grid
+ # $scope.$emit("change in showStates","grid")
+ else
+ console.log 'rejected:' + msg
+
try
- _showState = new showState(['grid', 'socrData', 'worldBank', 'generate'], $scope)
+ _showState = new showState(['grid', 'socrData', 'worldBank', 'generate', 'jsonParse'], $scope)
catch e
console.log e.message
- # Adding Listeners
+ # adding listeners
$scope.$on 'update showStates', (obj, data) ->
_showState.set data
@@ -389,7 +416,7 @@ getData = angular.module('app_analysis_getData', [
console.log 'get data main div loaded'
])
-# Helps sidebar accordion to keep in sync with the main div.
+# Helps sidebar accordion to keep in sync with the main div
.factory('showState', ->
(obj, scope) ->
if arguments.length is 0
@@ -410,8 +437,6 @@ getData = angular.module('app_analysis_getData', [
scope.showState[index] = false
else
scope.showState[i] = true
- #console.log "final state"
- #console.log scope.showState
)
# ###
diff --git a/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee b/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee
index 3cc2682c..c840ab5b 100644
--- a/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee
+++ b/app/scripts/analysis/tools/machineLearning/kMeans/kmeans.coffee
@@ -87,6 +87,9 @@ kMeans = angular.module('app_analysis_kMeans', [])
# safe enforce $scope.$digest to activate directive watchers
$timeout updateChartData
+ $scope.$on 'kmeans:updateDataPoints', (event, dataPoints) ->
+ _update dataPoints
+
_finish = (results=null) ->
msgManager.broadcast 'kmeans:done', results
showResults results
@@ -117,9 +120,14 @@ kMeans = angular.module('app_analysis_kMeans', [])
wholedataseton: true
accuracyon: false
+ updateDataPoints = (data) ->
+ xCol = data.header.indexOf $scope.xCol
+ yCol = data.header.indexOf $scope.yCol
+ data = ([row[xCol], row[yCol]] for row in data.data)
+ msgManager.broadcast 'kmeans:updateDataPoints', data
+
# set initial values for sidebar controls
initSidebarControls = (initControlValues) ->
-
params = kmeans.getParameters()
$scope.ks = [params.minK..params.maxK]
$scope.distances = params.distances
@@ -149,6 +157,10 @@ kMeans = angular.module('app_analysis_kMeans', [])
$scope.kmeanson = off
if $scope.labelson
$scope.numUniqueLabels = detectKValue data
+ $scope.updateDataPoints = () ->
+ updateDataPoints data
+ $timeout ->
+ updateDataPoints data
setDetectedKValue = (detectedK) ->
if detectedK.num <= 10
@@ -191,7 +203,7 @@ kMeans = angular.module('app_analysis_kMeans', [])
data = ([row[xCol], row[yCol]] for row in data.data)
# re-check if possible to compute accuracy
- if $scope.labelson and $scope.k is $scope.numUniqueLabels and $scope.accuracyon
+ if $scope.labelson and $scope.k is $scope.numUniqueLabels.num and $scope.accuracyon
acc = on
obj =
@@ -219,6 +231,7 @@ kMeans = angular.module('app_analysis_kMeans', [])
msgScope: ['kMeans']
listener: (msg, data) ->
updateSidebarControls(data)
+ updateDataPoints(data)
$scope.detectKValue = ->
detectedK = detectKValue data
setDetectedKValue detectedK
@@ -558,7 +571,7 @@ kMeans = angular.module('app_analysis_kMeans', [])
distanceType = distanceType.toLowerCase()
initMethod = initMethod.toLowerCase()
- _updateGraph data
+# _updateGraph data
if initMethod is 'forgy'
centroids = _initCentroids data, k
diff --git a/app/styles/custom.less b/app/styles/custom.less
index c18e50d2..ad9284b4 100644
--- a/app/styles/custom.less
+++ b/app/styles/custom.less
@@ -72,7 +72,7 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
to { transform: scale( 1 ) rotate( 360deg ); }
}
-#nav-clear-tool-search {
+.clear-search-input {
position: absolute;
right: 15px;
top: 0;
@@ -84,6 +84,23 @@ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
color: #ccc;
}
-#nav-tool-search {
+.margin-search-input {
margin: 2px 10px;
}
+
+#nav-clear-tool-search {
+ .clear-search-input
+}
+
+#nav-tool-search {
+ .margin-search-input
+}
+
+#getDataJsonUrlClear {
+ .clear-search-input;
+ right: 5px;
+}
+
+#getDataJsonUrlInput {
+ padding-right: 20px;
+}
diff --git a/bower.json b/bower.json
index 4bdf59ec..73270917 100644
--- a/bower.json
+++ b/bower.json
@@ -1,34 +1,34 @@
{
"author": "Alexandr Kalinin, Selvam Palanimalai",
"name": "SOCRAT",
- "version": "v0.1.5-alpha.4",
+ "version": "v0.1.5-alpha.5",
"description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization",
"homepage": "https://github.com/SOCR/SOCRAT",
"dependencies": {
- "angular": "^1.3.0",
+ "angular": "^1.5.0",
"jquery-layout": "~1.4.3",
"jquery-highlight": "~3.3.0",
"jquery-hoverintent": "https://github.com/briancherne/jquery-hoverIntent.git#~1.8.1",
- "datatables": "DataTables#~1.10.9",
- "d3": "~3.5.6",
- "angular-animate": "^1.3.0",
- "angular-cookies": "^1.3.0",
- "angular-messages": "^1.3.0",
- "angular-resource": "^1.3.0",
- "angular-route": "^1.3.0",
- "angular-sanitize": "^1.3.0",
- "angular-touch": "^1.3.0",
- "angular-ui": "~0.4.0",
+ "datatables": "DataTables#~1.10.11",
+ "d3": "~3.5.15",
+ "angular-animate": "^1.5.0",
+ "angular-cookies": "^1.5.0",
+ "angular-messages": "^1.5.0",
+ "angular-resource": "^1.5.0",
+ "angular-route": "^1.5.0",
+ "angular-sanitize": "^1.5.0",
+ "angular-touch": "^1.5.0",
+ "angular-ui": "^0.4.0",
"angular-ui-router": "~0.2.15",
- "ngHandsontable": "angular-ui-handsontable#~0.7.0",
+ "ngHandsontable": "angular-ui-handsontable#~0.8.0",
"wrangler": "https://github.com/alxndrkalinin/wrangler.git",
- "bootstrap": "^3.2.0",
- "angular-bootstrap": "~0.14.2",
- "jstat": "~1.4.6",
+ "bootstrap": "^3.3.6",
+ "angular-bootstrap": "~1.1.2",
+ "jstat": "~1.5.2",
"jsfeat": "~0.0.8"
},
"devDependencies": {
- "angular-mocks": "^1.3.0"
+ "angular-mocks": "^1.5.0"
},
"appPath": "app",
"moduleName": "expApp",
diff --git a/package.json b/package.json
index c213bc39..a4f4e60f 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,13 @@
{
"author": "Alexandr Kalinin, Selvam Palanimalai",
"name": "SOCRAT",
- "version": "v0.1.5-alpha.4",
+ "version": "v0.1.5-alpha.5",
"description": "Flexible HTML5/JS toolkit for interactive data analysis and visuzalization",
"homepage": "https://github.com/SOCR/SOCRAT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/SOCR/SOCRAT.git"
+ },
"devDependencies": {
"coffee-script": "*",
"grunt": "^0.4.5",
@@ -50,5 +54,5 @@
"scripts": {
"test": "grunt test"
},
- "license": "MIT"
+ "license": "LGPL v3.0"
}