Commit f04520e7 authored by Mathieu Rodic's avatar Mathieu Rodic

[FEATURE] Charts are now working with AngularJS on `/tests/mvc`

(some features are still to be added)
parent 14034b42
...@@ -223,7 +223,7 @@ class NodesChildrenMetatadata(APIView): ...@@ -223,7 +223,7 @@ class NodesChildrenMetatadata(APIView):
}) })
return JsonHttpResponse({ return JsonHttpResponse({
'collection': collection, 'data': collection,
}) })
...@@ -329,9 +329,9 @@ class NodesChildrenQueries(APIView): ...@@ -329,9 +329,9 @@ class NodesChildrenQueries(APIView):
raise APIException('Unrecognized "type": "%s" in the query\'s "retrieve" parameter. Possible values are: "%s".' % (retrieve['type'], '", "'.join(retrieve_types), ), 400) raise APIException('Unrecognized "type": "%s" in the query\'s "retrieve" parameter. Possible values are: "%s".' % (retrieve['type'], '", "'.join(retrieve_types), ), 400)
if retrieve['type'] == 'fields': if retrieve['type'] == 'fields':
fields_names = ['id'] + list(set(retrieve['list']) - {'id'}) fields_names = ['id'] + retrieve['list'] if 'id' not in retrieve['list'] else retrieve['list']
elif retrieve['type'] == 'aggregates': elif retrieve['type'] == 'aggregates':
fields_names = list(set(retrieve['list'])) fields_names = list(retrieve['list'])
fields_list = [] fields_list = []
for field_name in fields_names: for field_name in fields_names:
...@@ -410,6 +410,9 @@ class NodesChildrenQueries(APIView): ...@@ -410,6 +410,9 @@ class NodesChildrenQueries(APIView):
.join(metadata_alias, metadata_alias.node_id == Node.id) .join(metadata_alias, metadata_alias.node_id == Node.id)
.filter(metadata_alias.metadata_id == metadata.id) .filter(metadata_alias.metadata_id == metadata.id)
) )
# adjust date
if metadata.type == 'datetime':
value = value + '2000-01-01T00:00:00Z'[len(value):]
# filter query # filter query
query = query.filter(operator( query = query.filter(operator(
getattr(metadata_alias, 'value_' + metadata.type), getattr(metadata_alias, 'value_' + metadata.type),
...@@ -493,22 +496,19 @@ class NodesChildrenQueries(APIView): ...@@ -493,22 +496,19 @@ class NodesChildrenQueries(APIView):
class NodesList(APIView): class NodesList(APIView):
def get(self, request): def get(self, request):
query = Node query = (Node
.query(Node.id, Node.name, NodeType.name.label('type'))
.join(NodeType)
)
if 'type' in request.GET: if 'type' in request.GET:
query = query.filter(type__name=request.GET['type']) query = query.filter(NodeType.name == request.GET['type'])
if 'parent' in request.GET: if 'parent' in request.GET:
query = query.filter(parent_id=int(request.GET['parent'])) query = query.filter(Node.parent_id == int(request.GET['parent']))
collection = [] return JsonHttpResponse({'data': [
for child in query.all(): node._asdict()
type_name = child.type.name for node in query.all()
collection.append({ ]})
'id': child.id,
'text': child.name,
'type': type_name,
'children': type_name is not 'Document',
})
return JsonHttpResponse(collection)
class Nodes(APIView): class Nodes(APIView):
...@@ -520,7 +520,7 @@ class Nodes(APIView): ...@@ -520,7 +520,7 @@ class Nodes(APIView):
return JsonHttpResponse({ return JsonHttpResponse({
'id': node.id, 'id': node.id,
'name': node.name, 'name': node.name,
'type': '', # 'type': node.type__name,
'metadata': dict(node.metadata), 'metadata': dict(node.metadata),
}) })
...@@ -570,7 +570,7 @@ class CorpusController: ...@@ -570,7 +570,7 @@ class CorpusController:
format = request.GET.get('format', 'json') format = request.GET.get('format', 'json')
if format == 'json': if format == 'json':
return JsonHttpResponse({ return JsonHttpResponse({
"collection": [{ "data": [{
'terms': row[0], 'terms': row[0],
'occurrences': row[1] 'occurrences': row[1]
} for row in query.all()], } for row in query.all()],
...@@ -583,132 +583,3 @@ class CorpusController: ...@@ -583,132 +583,3 @@ class CorpusController:
raise ValidationError('Unrecognized "format=%s", should be "csv" or "json"' % (format, )) raise ValidationError('Unrecognized "format=%s", should be "csv" or "json"' % (format, ))
@classmethod
def data(cls, request, corpus_id):
# parameters retrieval and validation
corpus = cls.get(corpus_id)
# query building: initialization
columns = []
conditions = []
group = []
order = []
having = []
join_ngrams = False
# query building: parameters
parameters = request.GET.getlist('parameters[]')
for parameter in parameters:
c = len(columns)
parameter_array = parameter.split('.')
if len(parameter_array) != 2:
raise ValidationError('Unrecognized "parameter[]=%s"' % (parameter, ))
origin = parameter_array[0]
key = parameter_array[1]
if origin == "metadata":
columns.append("%s.metadata->'%s' AS c%d" % (Node._meta.db_table, key, c, ))
conditions.append("%s.metadata ? '%s'" % (Node._meta.db_table, key, ))
# conditions.append("c%d IS NOT NULL" % (c, ))
group.append("c%d" % (c, ))
order.append("c%d" % (c, ))
else:
raise ValidationError('Unrecognized type "%s" in "parameter[]=%s"' % (origin, parameter, ))
# query building: mesured value
mesured = request.GET.get('mesured', '')
c = len(columns)
if mesured == "documents.count":
columns.append("COUNT(%s.id) AS c%d " % (Node._meta.db_table, c, ))
elif mesured == "ngrams.count":
columns.append("COUNT(%s.id) AS c%d " % (Ngram._meta.db_table, c, ))
join_ngrams = True
else:
raise ValidationError('The "mesured" parameter should take one of the following values: "documents.count", "ngrams.count"')
# query building: filters
for filter in request.GET.getlist('filters[]', ''):
splitFilter = filter.split('.')
origin = splitFilter[0]
# 127.0.0.1:8000/api/corpus/13410/data
# ?mesured=ngrams.count
# &parameters[]=metadata.publication_date
# &format=json
# &filters[]=ngrams.in.bee,bees
# &filters[]=metadata.language_fullname.eq.English
# &filters[]=metadata.publication_date.gt.1950-01-01
# &filters[]=metadata.publication_date.lt.2000-01-01
# &filters[]=metadata.title.contains.e
if origin == 'ngrams':
if splitFilter[1] == 'in':
ngrams = '.'.join(splitFilter[2:]).split(',')
map(str.strip, ngrams)
map(lambda ngram: ngram.replace("'", "''"), ngrams)
conditions.append(
"%s.terms IN ('%s')" % (Ngram._meta.db_table, "', '".join(ngrams), )
)
join_ngrams = True
elif origin == 'metadata':
key = splitFilter[1].replace("'", "''")
operator = splitFilter[2]
value = '.'.join(splitFilter[3:]).replace("'", "''")
condition = "%s.metadata->'%s' " % (Node._meta.db_table, key, )
if operator == 'contains':
condition += "LIKE '%%%s%%'" % (value, )
else:
condition += {
'eq': '=',
'lt': '<=',
'gt': '>=',
}[operator]
condition += " '%s'" % (value, )
conditions.append(condition)
else:
raise ValidationError('Unrecognized "filter[]=%s"' % (filter, ))
# query building: initializing SQL
sql_0 = ''
sql_1 = '\nSELECT '
sql_2 = '\nFROM %s' % (Node._meta.db_table, )
sql_3 = '\nWHERE (%s.parent_id = %d)' % (Node._meta.db_table, corpus.id, )
# sql_0 = _sql_cte
# sql_1 = '\nSELECT '
# sql_2 = '\nFROM %s\nINNER JOIN cte ON cte."id" = %s.id' % (Node._meta.db_table, Node._meta.db_table, )
# sql_3 = '\nWHERE ((NOT cte.id = \'%d\') AND (\'%d\' = ANY(cte."path")))' % (corpus.id, corpus.id, )
# query building: assembling SQL
sql_1 += ", ".join(columns)
sql_2 += "\nINNER JOIN %s ON %s.id = %s.type_id" % (NodeType._meta.db_table, NodeType._meta.db_table, Node._meta.db_table, )
if join_ngrams:
sql_2 += "\nINNER JOIN %s ON %s.node_id = %s.id" % (Node_Ngram._meta.db_table, Node_Ngram._meta.db_table, Node._meta.db_table, )
sql_2 += "\nINNER JOIN %s ON %s.id = %s.ngram_id" % (Ngram._meta.db_table, Ngram._meta.db_table, Node_Ngram._meta.db_table, )
sql_3 += "\nAND %s.name = 'Document'" % (NodeType._meta.db_table, )
if conditions:
sql_3 += "\nAND (%s)" % (" AND ".join(conditions), )
if group:
sql_3 += "\nGROUP BY %s" % (", ".join(group), )
if order:
sql_3 += "\nORDER BY %s" % (", ".join(order), )
sql = sql_0 + sql_1 + sql_2 + sql_3
# query execution
# return DebugHttpResponse(sql)
cursor = connection.cursor()
cursor.execute(sql)
# response building
format = request.GET.get('format', 'json')
keys = parameters + [mesured]
rows = cursor.fetchall()
if format == 'json':
dimensions = []
for key in keys:
suffix = key.split('_')[-1]
dimensions.append({
'key': key,
'type': 'datetime' if suffix == 'date' else 'numeric'
})
return JsonHttpResponse({
"collection": [
{key: value for key, value in zip(keys, row)}
for row in rows
],
"list": [row for row in rows],
"dimensions" : dimensions
})
elif format == 'csv':
return CsvHttpResponse([keys] + [row for row in rows])
else:
raise ValidationError('Unrecognized "format=%s", should be "csv" or "json"' % (format, ))
...@@ -44,10 +44,10 @@ urlpatterns = patterns('', ...@@ -44,10 +44,10 @@ urlpatterns = patterns('',
url(r'^api/nodes$', gargantext_web.api.NodesList.as_view()), url(r'^api/nodes$', gargantext_web.api.NodesList.as_view()),
url(r'^api/nodes/(\d+)/ngrams$', gargantext_web.api.CorpusController.ngrams), url(r'^api/nodes/(\d+)/ngrams$', gargantext_web.api.CorpusController.ngrams),
url(r'^api/nodes/(\d+)/data$', gargantext_web.api.CorpusController.data),
url(r'^graph-it$', views.graph_it), url(r'^graph-it$', views.graph_it),
url(r'^ngrams$', views.ngrams), url(r'^ngrams$', views.ngrams),
url(r'^tests/mvc$', views.tests_mvc),
) )
......
/*
line-chart - v1.1.4 - 04 December 2014
https://github.com/n3-charts/line-chart
Copyright (c) 2014 n3-charts
*/
var directive,m,mod,old_m,__indexOf=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};old_m=angular.module("n3-charts.linechart",["n3charts.utils"]),m=angular.module("n3-line-chart",["n3charts.utils"]),directive=function(a,b){return old_m.directive(a,b),m.directive(a,b)},directive("linechart",["n3utils","$window","$timeout",function(a,b,c){var d;return d=function(d,e,f){var g,h,i,j,k,l;return l=a,g=l.getDefaultMargins(),e[0].style["font-size"]=0,d.updateDimensions=function(a){var b,c,d,g,h;return d=e[0].parentElement,h=l.getPixelCssProp(d,"padding-top"),b=l.getPixelCssProp(d,"padding-bottom"),c=l.getPixelCssProp(d,"padding-left"),g=l.getPixelCssProp(d,"padding-right"),a.width=+(f.width||d.offsetWidth||900)-c-g,a.height=+(f.height||d.offsetHeight||500)-h-b},d.redraw=function(){return d.updateDimensions(g),d.update(g)},i=!1,h={onSeriesVisibilityChange:function(a){var b,c,e;return e=a.series,b=a.index,c=a.newVisibility,d.options.series[b].visible=c,d.$apply()}},d.update=function(a){var b,c,g,i,j,k,m;return k=l.sanitizeOptions(d.options,f.mode),i=angular.extend(h,l.getTooltipHandlers(k)),g=l.getDataPerSeries(d.data,k),j="thumbnail"===f.mode,l.clean(e[0]),m=l.bootstrap(e[0],a),b=l.createAxes(m,a,k.axes).andAddThemIf(j),g.length&&l.setScalesDomain(b,d.data,k.series,m,k),j?l.adjustMarginsForThumbnail(a,b):l.adjustMargins(m,a,k,d.data),l.createContent(m,i),g.length&&(c=l.getBestColumnWidth(a,g,k),l.drawArea(m,b,g,k,i).drawColumns(m,b,g,c,k,i).drawLines(m,b,g,k,i),k.drawDots&&l.drawDots(m,b,g,k,i)),k.drawLegend&&l.drawLegend(m,k.series,a,i),"scrubber"===k.tooltip.mode?l.createGlass(m,a,i,b,g,k,c):"none"!==k.tooltip.mode?l.addTooltips(m,a,k.axes):void 0},j=void 0,k=function(){return null!=j&&c.cancel(j),j=c(d.redraw,1)},b.addEventListener("resize",k),d.$watch("data",d.redraw,!0),d.$watch("options",d.redraw,!0)},{replace:!0,restrict:"E",scope:{data:"=",options:"="},template:"<div></div>",link:d}}]),mod=angular.module("n3charts.utils",[]),mod.factory("n3utils",["$window","$log","$rootScope",function(a,b){return{addPatterns:function(a,b){var c;return c=a.select("defs").selectAll("pattern").data(b.filter(function(a){return a.striped})).enter().append("pattern").attr({id:function(a){return a.type+"Pattern_"+a.index},patternUnits:"userSpaceOnUse",x:0,y:0,width:60,height:60}).append("g").style({fill:function(a){return a.color},"fill-opacity":.3}),c.append("rect").style("fill-opacity",.3).attr("width",60).attr("height",60),c.append("path").attr("d","M 10 0 l10 0 l -20 20 l 0 -10 z"),c.append("path").attr("d","M40 0 l10 0 l-50 50 l0 -10 z"),c.append("path").attr("d","M60 10 l0 10 l-40 40 l-10 0 z"),c.append("path").attr("d","M60 40 l0 10 l-10 10 l -10 0 z")},drawArea:function(a,b,c,d){var e,f;return e=c.filter(function(a){return"area"===a.type}),this.addPatterns(a,e),f={y:this.createLeftAreaDrawer(b,d.lineMode,d.tension),y2:this.createRightAreaDrawer(b,d.lineMode,d.tension)},a.select(".content").selectAll(".areaGroup").data(e).enter().append("g").attr("class",function(a){return"areaGroup series_"+a.index}).append("path").attr("class","area").style("fill",function(a){return a.striped!==!0?a.color:"url(#areaPattern_"+a.index+")"}).style("opacity",function(a){return a.striped?"1":"0.3"}).attr("d",function(a){return f[a.axis](a.values)}),this},createLeftAreaDrawer:function(a,b,c){return d3.svg.area().x(function(b){return a.xScale(b.x)}).y0(function(b){return a.yScale(b.y0)}).y1(function(b){return a.yScale(b.y0+b.y)}).interpolate(b).tension(c)},createRightAreaDrawer:function(a,b,c){return d3.svg.area().x(function(b){return a.xScale(b.x)}).y0(function(b){return a.y2Scale(b.y0)}).y1(function(b){return a.y2Scale(b.y0+b.y)}).interpolate(b).tension(c)},getPseudoColumns:function(a,b){var c,d;return a=a.filter(function(a){return"column"===a.type}),d={},c=[],a.forEach(function(a){var e,f,g;return f=!1,b.stacks.forEach(function(b,e){var g;return null!=a.id&&(g=a.id,__indexOf.call(b.series,g)>=0)?(d[a.id]=e,__indexOf.call(c,e)<0&&c.push(e),f=!0):void 0}),f===!1?(e=d[a.id]=g=c.length,c.push(e)):void 0}),{pseudoColumns:d,keys:c}},getBestColumnWidth:function(a,b,c){var d,e,f,g,h,i;return b&&0!==b.length?0===b.filter(function(a){return"column"===a.type}).length?10:(i=this.getPseudoColumns(b,c),g=i.pseudoColumns,e=i.keys,f=b[0].values.length+2,h=e.length,d=a.width-a.left-a.right,parseInt(Math.max((d-(f-1)*c.columnsHGap)/(f*h),5))):10},getColumnAxis:function(a,b,c){var d,e,f,g;return g=this.getPseudoColumns(a,c),e=g.pseudoColumns,d=g.keys,f=d3.scale.ordinal().domain(d).rangeBands([0,d.length*b],0),function(a){var c;return null==e[a.id]?0:(c=e[a.id],f(c)-d.length*b/2)}},drawColumns:function(a,b,c,d,e,f){var g,h;return c=c.filter(function(a){return"column"===a.type}),h=this.getColumnAxis(c,d,e),c.forEach(function(a){return a.xOffset=h(a)+.5*d}),g=a.select(".content").selectAll(".columnGroup").data(c).enter().append("g").attr("class",function(a){return"columnGroup series_"+a.index}).style("stroke",function(a){return a.color}).style("fill",function(a){return a.color}).style("fill-opacity",.8).attr("transform",function(a){return"translate("+h(a)+",0)"}).on("mouseover",function(c){var d;return d=d3.select(d3.event.target),"function"==typeof f.onMouseOver?f.onMouseOver(a,{series:c,x:d.attr("x"),y:b[c.axis+"Scale"](d.datum().y0+d.datum().y),datum:d.datum()}):void 0}).on("mouseout",function(){return d3.select(d3.event.target).attr("r",2),"function"==typeof f.onMouseOut?f.onMouseOut(a):void 0}),g.selectAll("rect").data(function(a){return a.values}).enter().append("rect").style({"stroke-opacity":function(a){return 0===a.y?"0":"1"},"stroke-width":"1px","fill-opacity":function(a){return 0===a.y?0:.7}}).attr({width:d,x:function(a){return b.xScale(a.x)},height:function(a){return 0===a.y?b[a.axis+"Scale"].range()[0]:Math.abs(b[a.axis+"Scale"](a.y0+a.y)-b[a.axis+"Scale"](a.y0))},y:function(a){return 0===a.y?0:b[a.axis+"Scale"](Math.max(0,a.y0+a.y))}}),this},drawDots:function(a,b,c,d,e){var f;return f=a.select(".content").selectAll(".dotGroup").data(c.filter(function(a){var b;return("line"===(b=a.type)||"area"===b)&&a.drawDots})).enter().append("g"),f.attr({"class":function(a){return"dotGroup series_"+a.index},fill:function(a){return a.color}}).selectAll(".dot").data(function(a){return a.values}).enter().append("circle").attr({"class":"dot",r:function(a){return a.dotSize},cx:function(a){return b.xScale(a.x)},cy:function(a){return b[a.axis+"Scale"](a.y+a.y0)}}).style({stroke:"white","stroke-width":"2px"}),"none"!==d.tooltip.mode&&f.on("mouseover",function(b){var c;return c=d3.select(d3.event.target),c.attr("r",function(a){return a.dotSize+2}),"function"==typeof e.onMouseOver?e.onMouseOver(a,{series:b,x:c.attr("cx"),y:c.attr("cy"),datum:c.datum()}):void 0}).on("mouseout",function(){return d3.select(d3.event.target).attr("r",function(a){return a.dotSize}),"function"==typeof e.onMouseOut?e.onMouseOut(a):void 0}),this},computeLegendLayout:function(a,b,c){var d,e,f,g,h,i,j,k,l,m;for(i=10,l=this,h=this.getLegendItemsWidths(a,"y"),g=[0],e=1;e<h.length;)g.push(h[e-1]+g[e-1]+i),e++;if(k=this.getLegendItemsWidths(a,"y2"),!(k.length>0))return[g];for(m=c.width-c.right-c.left,d=0,j=[],f=k.length-1;f>=0;)j.push(m-d-k[f]),d+=k[f]+i,f--;return j.reverse(),[g,j]},getLegendItemsWidths:function(a,b){var c,d,e,f,g;if(f=this,c=function(a){return f.getTextBBox(a).width},e=a.selectAll(".legendItem."+b),!(e.length>0))return[];for(g=[],d=0;d<e[0].length;)g.push(c(e[0][d])),d++;return g},drawLegend:function(a,b,c,d){var e,f,g,h,i,j,k,l;return k=this,i=a.append("g").attr("class","legend"),e=16,a.select("defs").append("svg:clipPath").attr("id","legend-clip").append("circle").attr("r",e/2),f=i.selectAll(".legendItem").data(b),g=f.enter().append("g").attr({"class":function(a,b){return"legendItem series_"+b+" "+a.axis},opacity:function(b,c){return b.visible===!1?(k.toggleSeries(a,c),"0.2"):"1"}}),f.on("click",function(a,b){return"function"==typeof d.onSeriesVisibilityChange?d.onSeriesVisibilityChange({series:a,index:b,newVisibility:!(a.visible!==!1)}):void 0}),f.append("circle").attr({fill:function(a){return a.color},stroke:function(a){return a.color},"stroke-width":"2px",r:e/2}),f.append("path").attr({"clip-path":"url(#legend-clip)","fill-opacity":function(a){var b;return"area"===(b=a.type)||"column"===b?"1":"0"},fill:"white",stroke:"white","stroke-width":"2px",d:function(a){return k.getLegendItemPath(a,e,e)}}),f.append("circle").attr({"fill-opacity":0,stroke:function(a){return a.color},"stroke-width":"2px",r:e/2}),f.append("text").attr({"class":function(a,b){return"legendText series_"+b},"font-family":"Courier","font-size":10,transform:"translate(13, 4)","text-rendering":"geometric-precision"}).text(function(a){return a.label||a.y}),l=this.computeLegendLayout(a,b,c),h=l[0],j=l[1],g.attr({transform:function(a){return"y"===a.axis?"translate("+h.shift()+","+(c.height-40)+")":"translate("+j.shift()+","+(c.height-40)+")"}}),this},getLegendItemPath:function(a,b,c){var d,e;return"column"===a.type?(e="M"+-b/3+" "+-c/8+" l0 "+c+" ",e+="M0 "+-c/3+" l0 "+c+" ",e+="M"+b/3+" "+-c/10+" l0 "+c+" "):(d="M-"+b/2+" 0"+c/3+" l"+b/3+" -"+c/3+" l"+b/3+" "+c/3+" l"+b/3+" -"+2*c/3,"area"===a.type,d)},toggleSeries:function(a,b){var c;return c=!1,a.select(".content").selectAll(".series_"+b).style("display",function(){return"none"===d3.select(this).style("display")?(c=!0,"initial"):(c=!1,"none")}),c},drawLines:function(a,b,c,d,e){var f,g,h;return f={y:this.createLeftLineDrawer(b,d.lineMode,d.tension),y2:this.createRightLineDrawer(b,d.lineMode,d.tension)},h=a.select(".content").selectAll(".lineGroup").data(c.filter(function(a){var b;return"line"===(b=a.type)||"area"===b})).enter().append("g"),h.style("stroke",function(a){return a.color}).attr("class",function(a){return"lineGroup series_"+a.index}).append("path").attr({"class":"line",d:function(a){return f[a.axis](a.values)}}).style({fill:"none","stroke-width":function(a){return a.thickness},"stroke-dasharray":function(a){return"dashed"===a.lineMode?"10,3":void 0}}),d.tooltip.interpolate&&(g=function(c){var d,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A;r=d3.select(d3.event.target);try{q=d3.mouse(this)}catch(B){f=B,q=[0,0]}for(s=r.datum().values,g=z=0,A=s.length;A>z;g=++z)d=s[g],t=b.xScale(d.x),w=b.yScale(d.y),("undefined"==typeof m||null===m||m>t)&&(m=t,n=d.x),("undefined"==typeof i||null===i||t>i)&&(i=t,j=d.x),("undefined"==typeof o||null===o||o>w)&&(o=w),("undefined"==typeof k||null===k||w>k)&&(k=w),("undefined"==typeof p||null===p||d.y<p)&&(p=d.y),("undefined"==typeof l||null===l||d.y>l)&&(l=d.y);return u=(q[0]-m)/(i-m),x=(q[1]-o)/(k-o),v=Math.round(u*(j-n)+n),y=Math.round((1-x)*(l-p)+p),h={x:v,y:y},"function"==typeof e.onMouseOver?e.onMouseOver(a,{series:c,x:q[0],y:q[1],datum:h}):void 0},h.on("mousemove",g).on("mouseout",function(){return"function"==typeof e.onMouseOut?e.onMouseOut(a):void 0})),this},createLeftLineDrawer:function(a,b,c){return d3.svg.line().x(function(b){return a.xScale(b.x)}).y(function(b){return a.yScale(b.y+b.y0)}).interpolate(b).tension(c)},createRightLineDrawer:function(a,b,c){return d3.svg.line().x(function(b){return a.xScale(b.x)}).y(function(b){return a.y2Scale(b.y+b.y0)}).interpolate(b).tension(c)},getPixelCssProp:function(b,c){var d;return d=a.getComputedStyle(b,null).getPropertyValue(c),+d.replace(/px$/,"")},getDefaultMargins:function(){return{top:20,right:50,bottom:60,left:50}},clean:function(a){return d3.select(a).on("keydown",null).on("keyup",null).select("svg").remove()},bootstrap:function(a,b){var c,d,e;return d3.select(a).classed("chart",!0),e=b.width,c=b.height,d=d3.select(a).append("svg").attr({width:e,height:c}).append("g").attr("transform","translate("+b.left+","+b.top+")"),d.append("defs").attr("class","patterns"),d},createContent:function(a){return a.append("g").attr("class","content")},createGlass:function(a,b,c,d,e,f,g){var h,i,j,k;return j=a.append("g").attr({"class":"glass-container",opacity:0}),k=j.selectAll(".scrubberItem").data(e).enter().append("g").attr("class",function(a,b){return"scrubberItem series_"+b}),h=k.append("g").attr({"class":function(){return"rightTT"}}),h.append("path").attr({"class":function(a,b){return"scrubberPath series_"+b},y:"-7px",fill:function(a){return a.color}}),this.styleTooltip(h.append("text").style("text-anchor","start").attr({"class":function(a,b){return"scrubberText series_"+b},height:"14px",transform:"translate(7, 3)","text-rendering":"geometric-precision"})).text(function(a){return a.label||a.y}),i=k.append("g").attr({"class":function(){return"leftTT"}}),i.append("path").attr({"class":function(a,b){return"scrubberPath series_"+b},y:"-7px",fill:function(a){return a.color}}),this.styleTooltip(i.append("text").style("text-anchor","end").attr({"class":function(a,b){return"scrubberText series_"+b},height:"14px",transform:"translate(-13, 3)","text-rendering":"geometric-precision"})).text(function(a){return a.label||a.y}),k.append("circle").attr({"class":function(a,b){return"scrubberDot series_"+b},fill:"white",stroke:function(a){return a.color},"stroke-width":"2px",r:4}),j.append("rect").attr({"class":"glass",width:b.width-b.left-b.right,height:b.height-b.top-b.bottom}).style("fill","white").style("fill-opacity",1e-6).on("mouseover",function(){return c.onChartHover(a,d3.select(d3.event.target),d,e,f,g)})},getDataPerSeries:function(a,b){var c,d,e,f;return e=b.series,c=b.axes,e&&e.length&&a&&a.length?(f=e.map(function(c,d){var e;return e={index:d,name:c.y,values:[],color:c.color,axis:c.axis||"y",xOffset:0,type:c.type,thickness:c.thickness,drawDots:c.drawDots!==!1},null!=c.dotSize&&(e.dotSize=c.dotSize),c.striped===!0&&(e.striped=!0),null!=c.lineMode&&(e.lineMode=c.lineMode),c.id&&(e.id=c.id),a.filter(function(a){return null!=a[c.y]}).forEach(function(a){var d;return d={x:a[b.axes.x.key],y:a[c.y],y0:0,axis:c.axis||"y"},null!=c.dotSize&&(d.dotSize=c.dotSize),e.values.push(d)}),e}),null==b.stacks||0===b.stacks.length?f:(d=d3.layout.stack().values(function(a){return a.values}),b.stacks.forEach(function(a){var b;if(a.series.length>0)return b=f.filter(function(b){var c;return null!=b.id&&(c=b.id,__indexOf.call(a.series,c)>=0)}),d(b)}),f)):[]},resetMargins:function(a){var b;return b=this.getDefaultMargins(),a.left=b.left,a.right=b.right,a.top=b.top,a.bottom=b.bottom},adjustMargins:function(a,b,c,d){var e,f,g,h,i;return this.resetMargins(b),d&&d.length&&c.series.length&&(b.left=this.getWidestTickWidth(a,"y"),b.right=this.getWidestTickWidth(a,"y2"),0===b.right&&(b.right=20),"scrubber"!==c.tooltip.mode&&(i=c.series,e=i.filter(function(a){return"y2"!==a.axis}),f=this.getWidestOrdinate(d,e,c),b.left=this.estimateSideTooltipWidth(a,f).width+20,g=i.filter(function(a){return"y2"===a.axis}),g.length))?(h=this.getWidestOrdinate(d,g,c),b.right=this.estimateSideTooltipWidth(a,h).width+20):void 0},adjustMarginsForThumbnail:function(a){return a.top=1,a.bottom=2,a.left=0,a.right=1},estimateSideTooltipWidth:function(a,b){var c,d;return d=a.append("text"),d.text(""+b),this.styleTooltip(d),c=this.getTextBBox(d[0][0]),d.remove(),c},getTextBBox:function(a){return a.getBBox()},getWidestTickWidth:function(a,b){var c,d,e,f;return d=0,c=this.getTextBBox,e=a.select("."+b+".axis").selectAll(".tick"),null!=(f=e[0])&&f.map(function(a){return d=Math.max(d,c(a).width)}),d},getWidestOrdinate:function(a,b,c){var d;return d="",a.forEach(function(a){return b.forEach(function(b){var e,f;return e=a[b.y],null!=b.axis&&(null!=(f=c.axes[b.axis])?f.labelFunction:void 0)&&(e=c.axes[b.axis].labelFunction(e)),null!=e&&(""+e).length>(""+d).length?d=e:void 0})}),d},getDefaultOptions:function(){return{tooltip:{mode:"scrubber"},lineMode:"linear",tension:.7,axes:{x:{type:"linear",key:"x"},y:{type:"linear"}},series:[],drawLegend:!0,drawDots:!0,stacks:[],columnsHGap:5}},sanitizeOptions:function(a,b){return null==a?this.getDefaultOptions():("thumbnail"===b&&(a.drawLegend=!1,a.drawDots=!1,a.tooltip={mode:"none",interpolate:!1}),a.series=this.sanitizeSeriesOptions(a.series),a.stacks=this.sanitizeSeriesStacks(a.stacks,a.series),a.axes=this.sanitizeAxes(a.axes,this.haveSecondYAxis(a.series)),a.lineMode||(a.lineMode="linear"),a.tension=/^\d+(\.\d+)?$/.test(a.tension)?a.tension:.7,this.sanitizeTooltip(a),a.drawLegend=a.drawLegend!==!1,a.drawDots=a.drawDots!==!1,angular.isNumber(a.columnsHGap)||(a.columnsHGap=5),a)},sanitizeSeriesStacks:function(a,c){var d;return null==a?[]:(d={},c.forEach(function(a){return d[a.id]=a}),a.forEach(function(a){return a.series.forEach(function(c){var e;if(e=d[c],null!=e){if(e.axis!==a.axis)return b.warn("Series "+c+" is not on the same axis as its stack")}else if(!e)return b.warn("Unknown series found in stack : "+c)})}),a)},sanitizeTooltip:function(a){var b;if(!a.tooltip)return void(a.tooltip={mode:"scrubber"});if("none"!==(b=a.tooltip.mode)&&"axes"!==b&&"scrubber"!==b&&(a.tooltip.mode="scrubber"),"scrubber"===a.tooltip.mode?delete a.tooltip.interpolate:a.tooltip.interpolate=!!a.tooltip.interpolate,"scrubber"===a.tooltip.mode&&a.tooltip.interpolate)throw new Error("Interpolation is not supported for scrubber tooltip mode.")},sanitizeSeriesOptions:function(a){var b,c;return null==a?[]:(b=d3.scale.category10(),c={},a.forEach(function(a){if(null!=c[a.id])throw new Error("Twice the same ID ("+a.id+") ? Really ?");return null!=a.id?c[a.id]=a:void 0}),a.forEach(function(a,d){var e,f,g,h,i;if(a.axis="y2"!==(null!=(f=a.axis)?f.toLowerCase():void 0)?"y":"y2",a.color||(a.color=b(d)),a.type="line"===(g=a.type)||"area"===g||"column"===g?a.type:"line","column"===a.type?(delete a.thickness,delete a.lineMode,delete a.drawDots,delete a.dotSize):/^\d+px$/.test(a.thickness)||(a.thickness="1px"),("line"===(h=a.type)||"area"===h)&&("dashed"!==(i=a.lineMode)&&delete a.lineMode,a.drawDots!==!1&&null==a.dotSize&&(a.dotSize=2)),null==a.id){for(e=0;null!=c["series_"+e];)e++;a.id="series_"+e,c[a.id]=a}return a.drawDots===!1?delete a.dotSize:void 0}),a)},sanitizeAxes:function(a,b){var c;return null==a&&(a={}),a.x=this.sanitizeAxisOptions(a.x),(c=a.x).key||(c.key="x"),a.y=this.sanitizeAxisOptions(a.y),b&&(a.y2=this.sanitizeAxisOptions(a.y2)),a},sanitizeExtrema:function(a){var b,c;return c=this.getSanitizedNumber(a.min),null!=c?a.min=c:delete a.min,b=this.getSanitizedNumber(a.max),null!=b?a.max=b:delete a.max},getSanitizedNumber:function(a){var c;return null==a?void 0:(c=parseInt(a,10),isNaN(c)?void b.warn("Invalid extremum value : "+a+", deleting it."):c)},sanitizeAxisOptions:function(a){return null==a?{type:"linear"}:(a.type||(a.type="linear"),this.sanitizeExtrema(a),a)},createAxes:function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n;return d=null!=c.y2,h=b.width,e=b.height,h=h-b.left-b.right,e=e-b.top-b.bottom,i=void 0,i="date"===c.x.type?d3.time.scale().rangeRound([0,h]):d3.scale.linear().rangeRound([0,h]),k=void 0,k="log"===c.y.type?d3.scale.log().clamp(!0).rangeRound([e,0]):d3.scale.linear().rangeRound([e,0]),l=void 0,l=d&&"log"===c.y2.type?d3.scale.log().clamp(!0).rangeRound([e,0]):d3.scale.linear().rangeRound([e,0]),k.clamp(!0),l.clamp(!0),j=this.createAxis(i,"x",c),n=this.createAxis(k,"y",c),m=this.createAxis(l,"y2",c),f=function(a){return a.style({font:"10px Courier","shape-rendering":"crispEdges"}),a.selectAll("path").style({fill:"none",stroke:"#000"})},g=this,{xScale:i,yScale:k,y2Scale:l,xAxis:j,yAxis:n,y2Axis:m,andAddThemIf:function(b){return b||(f(a.append("g").attr("class","x axis").attr("transform","translate(0,"+e+")").call(j)),f(a.append("g").attr("class","y axis").call(n)),d&&f(a.append("g").attr("class","y2 axis").attr("transform","translate("+h+", 0)").call(m))),{xScale:i,yScale:k,y2Scale:l,xAxis:j,yAxis:n,y2Axis:m}}}},createAxis:function(a,b,c){var d,e,f;return f={x:"bottom",y:"left",y2:"right"},e=c[b],d=d3.svg.axis().scale(a).orient(f[b]).tickFormat(null!=e?e.labelFunction:void 0),null==e?d:(angular.isNumber(e.ticks)&&d.ticks(e.ticks),angular.isArray(e.ticks)&&d.tickValues(e.ticks),d)},setScalesDomain:function(a,b,c,d,e){var f,g;return this.setXScale(a.xScale,b,c,e.axes),g=this.getVerticalDomain(e,b,c,"y"),f=this.getVerticalDomain(e,b,c,"y2"),a.yScale.domain(g).nice(),a.y2Scale.domain(f).nice(),d.selectAll(".x.axis").call(a.xAxis),d.selectAll(".y.axis").call(a.yAxis),d.selectAll(".y2.axis").call(a.y2Axis)},getVerticalDomain:function(a,b,c,d){var e,f;return(f=a.axes[d])?null!=f.ticks&&angular.isArray(f.ticks)?[f.ticks[0],f.ticks[f.ticks.length-1]]:(e=this.yExtent(c.filter(function(a){return a.axis===d&&a.visible!==!1}),b,a.stacks.filter(function(a){return a.axis===d})),"log"===f.type&&(e[0]=0===e[0]?.001:e[0]),null!=f.min&&(e[0]=f.min),null!=f.max&&(e[1]=f.max),e):[]},yExtent:function(a,b,c){var d,e,f;return f=Number.POSITIVE_INFINITY,e=Number.NEGATIVE_INFINITY,d=[],c.forEach(function(b){return d.push(b.series.map(function(b){return a.filter(function(a){return a.id===b})[0]}))}),a.forEach(function(a){var b;return b=!1,c.forEach(function(c){var d;return d=a.id,__indexOf.call(c.series,d)>=0?b=!0:void 0}),b?void 0:d.push([a])}),d.forEach(function(a){return f=Math.min(f,d3.min(b,function(b){return a.reduce(function(a,c){return Math.min(a,b[c.y])},Number.POSITIVE_INFINITY)})),e=Math.max(e,d3.max(b,function(b){return a.reduce(function(a,c){return a+b[c.y]},0)}))}),f===e?f>0?[0,2*f]:[2*f,0]:[f,e]},setXScale:function(a,b,c,d){var e,f;return e=this.xExtent(b,d.x.key),c.filter(function(a){return"column"===a.type}).length&&this.adjustXDomainForColumns(e,b,d.x.key),f=d.x,null!=f.min&&(e[0]=f.min),null!=f.max&&(e[1]=f.max),a.domain(e)},xExtent:function(a,b){var c,d,e;return e=d3.extent(a,function(a){return a[b]}),c=e[0],d=e[1],c===d?c>0?[0,2*c]:[2*c,0]:[c,d]},adjustXDomainForColumns:function(a,b,c){var d;return d=this.getAverageStep(b,c),angular.isDate(a[0])?(a[0]=new Date(a[0].getTime()-d),a[1]=new Date(a[1].getTime()+d)):(a[0]=a[0]-d,a[1]=a[1]+d)},getAverageStep:function(a,b){var c,d,e;if(!(a.length>1))return 0;for(e=0,d=a.length-1,c=0;d>c;)e+=a[c+1][b]-a[c][b],c++;return e/d},haveSecondYAxis:function(a){return!a.every(function(a){return"y2"!==a.axis})},showScrubber:function(a,b,c,d,e,f){var g;return g=this,b.on("mousemove",function(){return a.selectAll(".glass-container").attr("opacity",1),g.updateScrubber(a,d3.mouse(this),c,d,e,f)}),b.on("mouseout",function(){return b.on("mousemove",null),a.selectAll(".glass-container").attr("opacity",0)})},getClosestPoint:function(a,b){var c,d,e;for(d=0,e=a.length-1,c=Math.round((e-d)/2);;)if(b<a[c].x?(e=c,c-=Math.ceil((e-d)/2)):(d=c,c+=Math.floor((e-d)/2)),c===d||c===e){c=Math.abs(b-a[d].x)<Math.abs(b-a[e].x)?d:e;break}return a[c]},updateScrubber:function(a,b,c,d,e,f){var g,h,i,j,k,l;return k=b[0],l=b[1],g=function(a){return a.transition().duration(50)},i=this,h=[],d.forEach(function(b,d){var f,j,l,m,n,o,p,q,r;return f=a.select(".scrubberItem.series_"+d),e.series[d].visible===!1?void f.attr("opacity",0):(f.attr("opacity",1),r=i.getClosestPoint(b.values,c.xScale.invert(k)),q=r.x+" : "+r.y,e.tooltip.formatter&&(q=e.tooltip.formatter(r.x,r.y,e.series[d])),n=f.select(".rightTT"),m=n.select("text"),m.text(q),l=f.select(".leftTT"),j=l.select("text"),j.text(q),p={right:i.getTextBBox(m[0][0]).width+5,left:i.getTextBBox(j[0][0]).width+5},o="y2"===b.axis?"right":"left",k=c.xScale(r.x),"left"===o?k+i.getTextBBox(j[0][0]).x-10<0&&(o="right"):"right"===o&&k+p.right>i.getTextBBox(a.select(".glass")[0][0]).width&&(o="left"),"left"===o?(g(n).attr("opacity",0),g(l).attr("opacity",1)):(g(n).attr("opacity",1),g(l).attr("opacity",0)),h[d]={index:d,x:k,y:c[r.axis+"Scale"](r.y+r.y0),side:o,sizes:p})}),h=this.preventOverlapping(h),j=Math.max(15,100/f),d.forEach(function(b,c){var d,f,k,l;if(e.series[c].visible!==!1)return f=h[c],d=a.select(".scrubberItem.series_"+c),k=d.select("."+f.side+"TT"),l="left"===f.side?b.xOffset:-b.xOffset,k.select("text").attr("transform",function(){return"left"===f.side?"translate("+(-3-j-l)+", "+(f.labelOffset+3)+")":"translate("+(4+j+l)+", "+(f.labelOffset+3)+")"}),k.select("path").attr("d",i.getScrubberPath(f.sizes[f.side]+1,f.labelOffset,f.side,j+l)),g(d).attr({transform:"translate("+(h[c].x+b.xOffset)+", "+h[c].y+")"})})},getScrubberPath:function(a,b,c,d){var e,f,g,h;return e=18,f=d,a=a,g="left"===c?1:-1,h=1,0!==b&&(h=Math.abs(b)/b),b||(b=0),["m0 0","l"+g+" 0","l0 "+(b+h),"l"+-g*(f+1)+" 0","l0 "+(-e/2-h),"l"+-g*a+" 0","l0 "+e,"l"+g*a+" 0","l0 "+(-e/2-h),"l"+g*(f-1)+" 0","l0 "+(-b+h),"l1 0","z"].join("")},preventOverlapping:function(a){var b,c,d,e;return d=18,b={},a.forEach(function(a){var c;return b[c=a.x]||(b[c]={left:[],right:[]}),b[a.x][a.side].push(a)}),c=function(a){var c,e,f,g,h,i,j,k,l;f=[];for(j in b)if(i=b[j],0!==i[a].length){for(g={};i[a].length>0;){h=i[a].pop(),c=!1;for(k in g)e=g[k],+k-d<=(l=h.y)&&+k+d>=l&&(e.push(h),c=!0);c||(g[h.y]=[h])}f.push(g)}return f},e=function(a){var b,c,d,e,f,g,h;f=20;for(b in a){g=a[b];for(h in g)d=g[h],c=d.length,1!==c?(d=d.sort(function(a,b){return a.y-b.y}),e=c%2===0?-(f/2)*(c/2):-(c-1)/2*f,d.forEach(function(a,b){return a.labelOffset=e+f*b})):d[0].labelOffset=0}},e(c("left")),e(c("right")),a},getTooltipHandlers:function(a){return"scrubber"===a.tooltip.mode?{onChartHover:angular.bind(this,this.showScrubber)}:{onMouseOver:angular.bind(this,this.onMouseOver),onMouseOut:angular.bind(this,this.onMouseOut)}},styleTooltip:function(a){return a.attr({"font-family":"monospace","font-size":10,fill:"white","text-rendering":"geometric-precision"})},addTooltips:function(a,b,c){var d,e,f,g,h,i,j,k;return h=b.width,e=b.height,h=h-b.left-b.right,e=e-b.top-b.bottom,g=24,d=18,f=5,i=a.append("g").attr({id:"xTooltip","class":"xTooltip",opacity:0}),i.append("path").attr("transform","translate(0,"+(e+1)+")"),this.styleTooltip(i.append("text").style("text-anchor","middle").attr({width:g,height:d,transform:"translate(0,"+(e+19)+")"})),k=a.append("g").attr({id:"yTooltip","class":"yTooltip",opacity:0}),k.append("path"),this.styleTooltip(k.append("text").attr({width:d,height:g})),null!=c.y2?(j=a.append("g").attr({id:"y2Tooltip","class":"y2Tooltip",opacity:0,transform:"translate("+h+",0)"}),j.append("path"),this.styleTooltip(j.append("text").attr({width:d,height:g}))):void 0},onMouseOver:function(a,b){return this.updateXTooltip(a,b),"y2"===b.series.axis?this.updateY2Tooltip(a,b):this.updateYTooltip(a,b)},onMouseOut:function(a){return this.hideTooltips(a)},updateXTooltip:function(a,b){var c,d,e,f,g,h;return g=b.x,c=b.datum,e=b.series,h=a.select("#xTooltip"),h.transition().attr({opacity:1,transform:"translate("+g+",0)"}),f=c.x,d=h.select("text"),d.text(f),h.select("path").attr("fill",e.color).attr("d",this.getXTooltipPath(d[0][0]))},getXTooltipPath:function(a){var b,c,d;return d=Math.max(this.getTextBBox(a).width,15),b=18,c=5,"m-"+d/2+" "+c+" l0 "+b+" l"+d+" 0 l0 "+-b+"l"+(-d/2+c)+" 0 l"+-c+" -"+b/4+" l"+-c+" "+b/4+" l"+(-d/2+c)+" 0z"},updateYTooltip:function(a,b){var c,d,e,f,g,h;return g=b.y,c=b.datum,e=b.series,h=a.select("#yTooltip"),h.transition().attr({opacity:1,transform:"translate(0, "+g+")"}),d=h.select("text"),d.text(c.y),f=this.getTextBBox(d[0][0]).width+5,d.attr({transform:"translate("+(-f-2)+",3)",width:f}),h.select("path").attr("fill",e.color).attr("d",this.getYTooltipPath(f))},updateY2Tooltip:function(a,b){var c,d,e,f,g,h;return g=b.y,c=b.datum,e=b.series,h=a.select("#y2Tooltip"),h.transition().attr("opacity",1),d=h.select("text"),d.text(c.y),f=this.getTextBBox(d[0][0]).width+5,d.attr({transform:"translate(7, "+(parseFloat(g)+3)+")",w:f}),h.select("path").attr({fill:e.color,d:this.getY2TooltipPath(f),transform:"translate(0, "+g+")"})},getYTooltipPath:function(a){var b,c;return b=18,c=5,"m0 0l"+-c+" "+-c+" l0 "+(-b/2+c)+" l"+-a+" 0 l0 "+b+" l"+a+" 0 l0 "+(-b/2+c)+"l"+-c+" "+c+"z"},getY2TooltipPath:function(a){var b,c;return b=18,c=5,"m0 0l"+c+" "+c+" l0 "+(b/2-c)+" l"+a+" 0 l0 "+-b+" l"+-a+" 0 l0 "+(b/2-c)+" l"+-c+" "+c+"z"},hideTooltips:function(a){return a.select("#xTooltip").transition().attr("opacity",0),a.select("#yTooltip").transition().attr("opacity",0),a.select("#y2Tooltip").transition().attr("opacity",0)}}}]);
\ No newline at end of file
...@@ -27,27 +27,36 @@ var operators = { ...@@ -27,27 +27,36 @@ var operators = {
{'label': 'is after', 'key': '>'} {'label': 'is after', 'key': '>'}
], ],
}; };
var strDate = function(date) {
return date.getUTCFullYear() + '-' +
('00' + (date.getUTCMonth() + 1)).slice(-2) + '-' +
('00' + date.getUTCDate()).slice(-2) + 'T' +
('00' + date.getUTCHours()).slice(-2) + ':' +
('00' + date.getUTCMinutes()).slice(-2) + ':' +
('00' + date.getUTCSeconds()).slice(-2) + 'Z';
}
var groupings = { var groupings = {
datetime: { datetime: {
century: { century: {
truncate: function(x) {return x.substr(0, 2)}, truncate: function(x) {return x.substr(0, 2) + '00-01-01T00:00:00Z';},
next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+100); return x;}, next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+100); return strDate(x);},
}, },
decade: { decade: {
truncate: function(x) {return x.substr(0, 3)}, truncate: function(x) {return x.substr(0, 3) + '0-01-01T00:00:00Z';},
next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+10); return x;}, next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+10); return strDate(x);},
}, },
year: { year: {
truncate: function(x) {return x.substr(0, 4)}, truncate: function(x) {return x.substr(0, 4) + '-01-01T00:00:00Z';},
next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+1); return x;}, next: function(x) {x = new Date(x); x.setFullYear(x.getFullYear()+1); return strDate(x);},
}, },
month: { month: {
truncate: function(x) {return x.substr(0, 7)}, truncate: function(x) {return x.substr(0, 7) + '-01T00:00:00Z';},
next: function(x) {x = new Date(x); x.setMonth(x.getMonth()+1); return x;}, next: function(x) {x = new Date(x); x.setMonth(x.getMonth()+1); return strDate(x);},
}, },
day: { day: {
truncate: function(x) {return x.substr(0, 10)}, truncate: function(x) {return x.substr(0, 10) + 'T00:00:00Z';},
next: function(x) {x = new Date(x); x.setDate(x.getDate()+1); return x;}, next: function(x) {x = new Date(x); x.setDate(x.getDate()+1); return strDate(x);},
}, },
}, },
numeric: { numeric: {
...@@ -65,6 +74,30 @@ var gargantext = angular.module('Gargantext', ['n3-charts.linechart']); ...@@ -65,6 +74,30 @@ var gargantext = angular.module('Gargantext', ['n3-charts.linechart']);
// tuning the application's scope // tuning the application's scope
angular.module('Gargantext').run(['$rootScope', function($rootScope){ angular.module('Gargantext').run(['$rootScope', function($rootScope){
$rootScope.Math = Math; $rootScope.Math = Math;
$rootScope.getColor = function(i, n){
var h = .5 + (i / n) % 1;
var s = .8;
var v = .8;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
var r, g, b;
switch (i % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
r = Math.round(255 * r);
g = Math.round(255 * g);
b = Math.round(255 * b);
var color = 'rgb(' + r + ',' + g + ',' + b + ')';
return color;
};
$rootScope.range = function(min, max, step){ $rootScope.range = function(min, max, step){
if (max == undefined){ if (max == undefined){
max = min; max = min;
...@@ -94,22 +127,6 @@ gargantext.controller("QueryController", function($scope, $http) { ...@@ -94,22 +127,6 @@ gargantext.controller("QueryController", function($scope, $http) {
$scope.corpora = response.data; $scope.corpora = response.data;
$scope.$apply(); $scope.$apply();
}); });
// update entities depending on the selected corpus
$scope.updateEntities = function() {
return true;
var url = '/api/nodes/' + $scope.corpusId + '/children/metadata';
$scope.entities = undefined;
$scope.filters = [];
$http.get(url).success(function(response){
$scope.entities = {
metadata: response.data,
ngrams: [
{key:'terms', type:'string'},
{key:'terms count', type:'integer'}
]
};
});
};
// filtering informations retrieval // filtering informations retrieval
$scope.operators = operators; $scope.operators = operators;
// add a filter // add a filter
...@@ -243,82 +260,163 @@ gargantext.controller("DatasetController", function($scope, $http) { ...@@ -243,82 +260,163 @@ gargantext.controller("DatasetController", function($scope, $http) {
gargantext.controller("GraphController", function($scope, $http, $element) { gargantext.controller("GraphController", function($scope, $http, $element) {
// initialization // initialization
$scope.datasets = [{}]; $scope.datasets = [{color: '#FFF'}];
$scope.resultsList = [];
$scope.queries = {}; $scope.queries = {};
$scope.graph = { $scope.graph = {
data: [ data: [],
{x: 0, value: 4, otherValue: 14},
{x: 1, value: 8, otherValue: 1},
{x: 2, value: 15, otherValue: 11},
{x: 3, value: 16, otherValue: 147},
{x: 4, value: 23, otherValue: 87},
{x: 5, value: 42, otherValue: 45}
],
options: { options: {
axes: { axes: {
x: {key: 'x', labelFunction: function(value) {return value;}, type: 'linear', min: 0, max: 10, ticks: 2}, x: {key: 'x', type: 'date'},
y: {type: 'linear', min: 0, max: 1, ticks: 5}, y: {type: 'log'},
y2: {type: 'linear', min: 0, max: 1, ticks: [1, 2, 3, 4]} // x: {key: 'x', labelFunction: function(value) {return value;}, type: 'linear', min: 0, max: 10, ticks: 2},
// y: {type: 'linear', min: 0, max: 1, ticks: 5},
// y2: {type: 'linear'}
// y2: {type: 'linear', min: 0, max: 1, ticks: [1, 2, 3, 4]}
}, },
series: [ // series: [
{y: 'value', color: 'steelblue', thickness: '2px', type: 'area', striped: true, label: 'Pouet'}, // {y: 'y0'},
{y: 'otherValue', axis: 'y2', color: 'lightsteelblue', visible: false, drawDots: true, dotSize: 2} // // {y: 'y1'},
], // // {y: 'y0', color: 'steelblue', thickness: '2px', type: 'area', striped: true, label: 'Pouet'},
// // {y: 'y1', axis: 'y2', color: 'orange', visible: true, drawDots: true, dotSize: 2}
// ],
lineMode: 'linear', lineMode: 'linear',
tension: 0.7, tension: 0.7,
tooltip: {mode: 'scrubber', formatter: function(x, y, series) {return 'pouet';}}, tooltip: {mode: 'scrubber', formatter: function(x, y, series) {return x + ' → ' + y;}},
drawLegend: true, drawLegend: false,
drawDots: true, drawDots: true,
columnsHGap: 5 columnsHGap: 5
} }
}; };
// add a dataset // add a dataset
$scope.addDataset = function() { $scope.addDataset = function() {
$scope.datasets.push({}); $scope.datasets.push({color: '#FFF'});
};
// show results on the graph
$scope.showResults = function(keys) {
// Format specifications
var xKey = keys[0];
var yKey = keys[1];
var grouping = groupings.datetime.year;
var convert = function(x) {return new Date(x);};
// Find extrema for X
var xMin, xMax;
angular.forEach($scope.resultsList, function(results){
if (results.length == 0) {
return false;
}
var xMinTmp = results[0][xKey];
var xMaxTmp = results[results.length - 1][xKey];
if (xMin === undefined || xMinTmp < xMin) {
xMin = xMinTmp;
}
if (xMax === undefined || xMaxTmp < xMax) {
xMax = xMaxTmp;
}
});
// Create the dataObject for interpolation
var dataObject = {};
xMin = grouping.truncate(xMin);
xMax = grouping.truncate(xMax);
for (var x=xMin; x<=xMax; x=grouping.next(x)) {
var row = [];
angular.forEach($scope.resultsList, function(results){
row.push(0);
});
dataObject[x] = row;
}
// Fill the dataObject with results
angular.forEach($scope.resultsList, function(results, resultsIndex){
angular.forEach(results, function(result, r){
var x = grouping.truncate(result[xKey]);
var y = result[yKey];
dataObject[x][resultsIndex] += parseFloat(y);
});
});
// Convert this object back to a sorted array
var linearData = [];
for (var x in dataObject) {
var row = {x: convert(x)};
var yList = dataObject[x];
for (var i=0; i<yList.length; i++) {
row['y' + i] = yList[i];
}
linearData.push(row);
}
// Finally, update the graph
var series = [];
for (var i=0, n=$scope.resultsList.length; i<n; i++) {
series.push({
y: 'y'+i,
axis: 'y',
color: $scope.getColor(i, n)
});
}
$scope.graph.options.lineMode = 'bundle';
$scope.graph.options.tension = .7;
$scope.graph.options.series = series;
$scope.graph.data = linearData;
}; };
// perform a query on the server // perform a query on the server
$scope.query = function() { $scope.query = function() {
// reinitialize graph // reinitialize data
$scope.resultsList = new Array($scope.datasets.length);
$scope.indexById = {};
$scope.graph.data = []; $scope.graph.data = [];
// add all the server request to the queue // add all the server request to the queue
for (var datasetId in $scope.queries) { var index = 0;
var query = $scope.queries[datasetId]; angular.forEach($scope.queries, function(query, datasetId) {
$scope.indexById[datasetId] = index++;
var data = { var data = {
filters: query.filters, filters: query.filters,
sort: ['metadata.publication_date.day'], sort: ['metadata.publication_date.day'],
retrieve: { retrieve: {
type: 'aggregates', type: 'aggregates',
list: ['count', 'metadata.publication_date.day'] list: ['metadata.publication_date.day', 'count']
} }
}; };
$http.post(query.url, data, {cache: true}).success(function(response) { $http.post(query.url, data, {cache: true}).success(function(response) {
// values initialization var index = $scope.indexById[datasetId];
var dataset = []; $scope.resultsList[index] = response.results;
var keyX = response.retrieve[0]; for (var i=0, n=$scope.resultsList.length; i<n; i++) {
var keyY = response.retrieve[1]; if ($scope.resultsList[i] == undefined) {
// data interpolation & transformation return;
var xPrev = undefined; }
var rows = response.data;
for (var i=0, n=rows.length; i<n; i++) {
var row = rows[i];
var x = row[keyX];
var y = new Date(row[keyY]);
dataset.push([x, y]);
} }
// add data to the graph $scope.showResults(response.retrieve);
// $scope.graph.data.push(dataset);
$scope.graph.data = dataset;
}).error(function(response) { }).error(function(response) {
console.error(response); console.error('An error occurred while retrieving the query response');
}); });
} });
dbg = $scope;
}; };
// update the queries // update the queries (catches the vent thrown by children dataset controllers)
$scope.$on('updateDataset', function(e, data) { $scope.$on('updateDataset', function(e, data) {
$scope.queries[data.datasetId] = { $scope.queries[data.datasetId] = {
url: data.url, url: data.url,
filters: data.filters filters: data.filters
}; };
}); });
}); });
\ No newline at end of file
// For debugging only!
setTimeout(function(){
var corpusId = $('div.corpus select option').last().val();
// first dataset
$('div.corpus select').val(corpusId).change();
setTimeout(function(){
// second dataset
$('button.add').first().click();
var d = $('li.dataset').last();
d.find('select').change();
// second dataset's filter
d.find('div.filters button').last().click();
d.find('select').last().val('metadata').change();
d.find('select').last().val('abstract').change();
d.find('select').last().val('contains').change();
d.find('input').last().val('dea').change();
// refresh
$('button.refresh').first().click();
}, 500);
}, 500);
\ No newline at end of file
...@@ -222,7 +222,10 @@ ...@@ -222,7 +222,10 @@
<li class="dataset" ng-controller="DatasetController" ng-repeat="dataset in datasets"> <li class="dataset" ng-controller="DatasetController" ng-repeat="dataset in datasets">
<div class="corpus"> <div class="corpus">
<button ng-click="removeDataset($index)" title="remove this dataset">X</button> <button ng-click="removeDataset($index)" title="remove this dataset">X</button>
Documents count in the corpus <select ng-model="mesured" style="background-color:{{ getColor($index, datasets.length) }}">
<option ng-repeat="(key, value) in {'Documents count': 'count', 'Ngrams count': 'ngrams.count'}" value="{{ value }}">{{ key }}</option>
</select>
in the corpus
<select ng-model="corpusId" ng-change="updateEntities()"> <select ng-model="corpusId" ng-change="updateEntities()">
<option ng-repeat="corpus in corpora" value="{{corpus.id}}">{{corpus.name}}</option> <option ng-repeat="corpus in corpora" value="{{corpus.id}}">{{corpus.name}}</option>
</select> </select>
...@@ -256,12 +259,11 @@ ...@@ -256,12 +259,11 @@
</li> </li>
</ul> </ul>
<button ng-click="query()">Refresh results</button> <button class="refresh" ng-click="query()">Refresh results</button>
<button ng-click="addDataset()">Add a dataset...</button> <button class="add" ng-click="addDataset()">Add a dataset...</button>
<hr/> <hr/>
<div class="graph"> <div class="graph">
<linechart data="graph.data" options="graph.options" mode="" width="" height=""></linechart> <linechart data="graph.data" options="graph.options"></linechart>
<!-- <ng-dygraphs ng-if="graph.data.length" data="graph.data" options="graph.options" legend="graph.legend"></ng-dygraphs> -->
</div> </div>
</div> </div>
...@@ -291,7 +293,7 @@ ...@@ -291,7 +293,7 @@
{% endverbatim %} {% endverbatim %}
<script type="text/javascript" src="{% static "js/angular.min.js" %}"></script> <script type="text/javascript" src="{% static "js/angular.min.js" %}"></script>
<script type="text/javascript" src="{% static "js/d3/d3.v2.min.js" %}"></script> <script type="text/javascript" src="{% static "js/d3/d3.v2.min.js" %}"></script>
<script type="text/javascript" src="https://raw.githubusercontent.com/n3-charts/line-chart/master/build/line-chart.min.js"></script> <script type="text/javascript" src="{% static "js/d3/n3.line-chart.min.js" %}"></script>
<!-- <script type="text/javascript" src="{% static "js/d3/angular-charts.js" %}"></script> --> <!-- <script type="text/javascript" src="{% static "js/d3/angular-charts.js" %}"></script> -->
<script type="text/javascript" src="{% static "js/gargantext.angular.js" %}"></script> <script type="text/javascript" src="{% static "js/gargantext.angular.js" %}"></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment