@@ -22,124 +22,223 @@ Template.chartsLayout.rendered = function () {
2222 'request_path'
2323 ]
2424 } ;
25-
26- drawChart ( input ) ;
25+ // Drawing the chart
26+ drawChart ( input )
2727} ;
2828
2929Template . chartsLayout . created = function ( ) {
3030
31+ // function that sets chart data to be available in template
3132 drawChart = function ( input ) {
3233
3334 Meteor . call ( "getChartData" , input , function ( err , data ) {
35+
36+ // error checking
3437 if ( err ) {
3538
3639 // removing loading state once loaded
3740 $ ( '#loadingState' ) . html ( "Loaded" ) ;
3841 alert ( "Data is not found!" ) ;
3942
4043 } else {
44+ // Parse the returned data for DC
45+ var parsedData = parseData ( data ) ;
4146
42- var items = data . hits . hits ;
47+ // Render the charts using parsed data
48+ renderCharts ( parsedData ) ;
4349
44- var index = new crossfilter ( items ) ;
50+ }
51+ } ) ;
52+ } ;
4553
46- var dateFormat = d3 . time . format . iso ;
47- items . forEach ( function ( d ) {
54+ // function that parses chart data
55+ parseData = function ( data ) {
56+ // Get chart data from within data object
57+ var items = data . hits . hits ;
4858
49- var timeStamp = moment ( d . fields . request_at [ 0 ] ) ;
50- timeStamp = timeStamp . format ( ) ;
51- d . fields . ymd = dateFormat . parse ( timeStamp ) ;
52- d . fields . itemsCount = + data . hits . total ;
59+ // Create CrossFilter index using chart data
60+ var index = new crossfilter ( items ) ;
5361
62+ // Create ISO date format
63+ var dateFormat = d3 . time . format . iso ;
5464
55- } ) ;
65+ // Parse each item adding timestamp as YMD and count the items
66+ items . forEach ( function ( d ) {
67+ // Parse timestamp to Moment .jsobject
68+ var timeStamp = moment ( d . fields . request_at [ 0 ] ) ;
5669
57- var timeStampDimension = index . dimension ( function ( d ) { return d . fields . ymd ; } ) ;
58- var countryDimension = index . dimension ( function ( d ) { return d . fields . request_ip_country } ) ;
70+ // Format the timestamp using Moment.js format method
71+ timeStamp = timeStamp . format ( ) ;
5972
60- var timeStampGroup = timeStampDimension . group ( ) ;
61- var countryGroup = countryDimension . group ( ) ;
62- var all = index . groupAll ( ) ;
73+ // Add YMD field to item
74+ d . fields . ymd = dateFormat . parse ( timeStamp ) ;
6375
64- dc . dataCount ( "#row-selection" )
65- . dimension ( index )
66- . group ( all ) ;
76+ // Add item count from total hits
77+ d . fields . itemsCount = + data . hits . total ;
78+ } ) ;
6779
68- var totalCountries = countryGroup . reduceSum ( function ( d ) {
69- return d . fields . itemsCount ;
70- } ) ;
80+ // Create timestamp dimension from YMD field
81+ var timeStampDimension = index . dimension ( function ( d ) { return d . fields . ymd ; } ) ;
7182
72- var minDate = d3 . min ( items , function ( d ) { return d . fields . ymd ; } ) ;
73- var maxDate = d3 . max ( items , function ( d ) { return d . fields . ymd ; } ) ;
74-
75- var timeScale = d3 . time . scale ( ) . domain ( [ minDate , maxDate ] ) ;
76- var countryScale = d3 . scale . ordinal ( ) . domain ( countryDimension ) ;
77-
78- var chart = dc . lineChart ( "#line-chart" ) ;
79- var countryChart = dc . barChart ( "#bar-chart" ) ;
80- var dataTable = dc . dataTable ( "#data-table" ) ;
81- var overview = dc . barChart ( "#overview-chart" ) ;
82-
83- chart
84- . width ( 1140 )
85- . height ( 480 )
86- . transitionDuration ( 1500 )
87- . elasticY ( true )
88- . x ( timeScale )
89- . dimension ( timeStampDimension )
90- . group ( timeStampGroup )
91- . mouseZoomable ( true )
92- . rangeChart ( overview )
93- . renderArea ( true )
94- . dotRadius ( 3 )
95- . brushOn ( false )
96- . renderHorizontalGridLines ( true )
97- . renderVerticalGridLines ( true ) ;
98-
99- overview
100- . width ( 1140 )
101- . height ( 40 )
102- . margins ( { top : 0 , right : 50 , bottom : 20 , left : 40 } )
103- . dimension ( timeStampDimension )
104- . group ( timeStampGroup )
105- . centerBar ( true )
106- . gap ( 1 )
107- . x ( d3 . time . scale ( ) . domain ( [ new Date ( 2015 , 1 , 1 ) , new Date ( ) ] ) )
108- . alwaysUseRounding ( true )
109- . yAxis ( ) . ticks ( 0 ) ;
110-
111- countryChart
112- . width ( 1140 )
113- . height ( 250 )
114- . dimension ( countryDimension )
115- . group ( totalCountries )
116- . x ( countryScale )
117- . xUnits ( dc . units . ordinal )
118- . renderHorizontalGridLines ( true )
119- . renderVerticalGridLines ( true ) ;
120-
121- dataTable . width ( 960 ) . height ( 800 )
122- . dimension ( timeStampDimension )
123- . group ( function ( d ) { return "Logs"
124- } )
125- . size ( 100 ) // number of rows to return
126- . columns ( [
127- function ( d ) { return d . fields . ymd ; } ,
128- function ( d ) { return d . fields . request_ip_country ; } ,
129- function ( d ) { return d . fields . request_ip ; } ,
130- function ( d ) { return d . fields . response_time ; } ,
131- function ( d ) { return d . fields . request_path ; }
132- ] )
133- . sortBy ( function ( d ) { return - d . fields . ymd ; } )
134- . order ( d3 . ascending ) ;
83+ // Create country dimension from Request IP Country field
84+ var countryDimension = index . dimension ( function ( d ) { return d . fields . request_ip_country } ) ;
13585
136- // removing loading state once loaded
137- $ ( '#loadingState' ) . html ( "Loaded! Took <b>" + data . took + "</b>ms" ) ;
86+ // Group entries by timestamp dimension
87+ var timeStampGroup = timeStampDimension . group ( ) ;
13888
139- dc . renderAll ( ) ;
89+ // Group entries by country
90+ var countryGroup = countryDimension . group ( ) ;
14091
141- }
92+ // Create index by all dimensions
93+ var all = index . groupAll ( ) ;
94+
95+ // Providing information about current data selection
96+ dc . dataCount ( "#row-selection" )
97+ . dimension ( index )
98+ . group ( all ) ;
99+
100+ // total amount of countries within filtering
101+ var totalCountries = countryGroup . reduceSum ( function ( d ) {
102+ return d . fields . itemsCount ;
142103 } ) ;
104+
105+ // set up a range of dates for charts
106+ var minDate = d3 . min ( items , function ( d ) { return d . fields . ymd ; } ) ;
107+ var maxDate = d3 . max ( items , function ( d ) { return d . fields . ymd ; } ) ;
108+
109+ // Create time scale from min and max dates
110+ var timeScale = d3 . time . scale ( ) . domain ( [ minDate , maxDate ] ) ;
111+
112+ // Create country scale from country dimension
113+ var countryScale = d3 . scale . ordinal ( ) . domain ( countryDimension ) ;
114+
115+
116+ // Return object with all key values created above
117+ return {
118+ timeStampDimension : timeStampDimension ,
119+ countryDimension : countryDimension ,
120+ timeStampGroup : timeStampGroup ,
121+ countryGroup : countryGroup ,
122+ totalCountries : totalCountries ,
123+ timeScale : timeScale ,
124+ countryScale : countryScale ,
125+ took : data . took
126+ } ;
143127 }
144128
129+ renderCharts = function ( parsedData ) {
130+
131+ var timeStampDimension = parsedData . timeStampDimension ;
132+ var countryDimension = parsedData . countryDimension ;
133+ var timeStampGroup = parsedData . timeStampGroup ;
134+ var countryGroup = parsedData . countryGroup ;
135+ var totalCountries = parsedData . totalCountries ;
136+ var timeScale = parsedData . timeScale ;
137+ var countryScale = parsedData . countryScale ;
138+ var took = parsedData . took ;
139+
140+ var chart = dc . lineChart ( "#line-chart" ) ;
141+ var countryChart = dc . barChart ( "#bar-chart" ) ;
142+
143+ chart
144+ . width ( 1140 )
145+ . height ( 480 )
146+ . elasticX ( true )
147+ . x ( timeScale )
148+ . dimension ( timeStampDimension )
149+ . group ( timeStampGroup )
150+ . renderArea ( true )
151+ . dotRadius ( 3 )
152+ . renderHorizontalGridLines ( true )
153+ . renderVerticalGridLines ( true ) ;
154+
155+ countryChart
156+ . width ( 1140 )
157+ . height ( 250 )
158+ . dimension ( countryDimension )
159+ . group ( totalCountries )
160+ . x ( countryScale )
161+ . xUnits ( dc . units . ordinal )
162+ . renderHorizontalGridLines ( true )
163+ . renderVerticalGridLines ( true ) ;
164+
165+
166+ // Creates Dynatable
167+ var dynatable = $ ( '#dc-data-table' ) . dynatable ( {
168+ features : {
169+ pushState : false
170+ } ,
171+ dataset : {
172+ records : setUpDataSet ( ) ,
173+ perPageDefault : 10 ,
174+ perPageOptions : [ 10 , 20 , 50 , 100 ]
175+ }
176+ } ) . data ( 'dynatable' ) ;
177+
178+ // Listens to filtering event and refreshes the table on a change
179+ function RefreshTable ( ) {
180+ dc . events . trigger ( function ( ) {
181+ dynatable . settings . dataset . originalRecords = setUpDataSet ( ) ;
182+ dynatable . process ( ) ;
183+ } ) ;
184+ } ;
185+
186+ // Add each chart to the DC Chart Registry
187+ for ( var i = 0 ; i < dc . chartRegistry . list ( ) . length ; i ++ ) {
188+ var chartI = dc . chartRegistry . list ( ) [ i ] ;
189+ chartI . on ( "filtered" , RefreshTable ) ;
190+ }
191+
192+ // Parse data into array for chart
193+ function setUpDataSet ( ) {
194+ var dataSet = [ ] ;
195+ timeStampDimension . top ( Infinity ) . forEach ( function ( e ) {
196+
197+ var country ;
198+ var path ;
199+ var request_ip ;
200+ var response_time ;
201+
202+ // Error handling for empty fields
203+ try {
204+ country = e . fields . request_ip_country [ 0 ]
205+ } catch ( e ) {
206+ country = "" ;
207+ }
208+
209+ try {
210+ path = e . fields . request_path [ 0 ] ;
211+ } catch ( e ) {
212+ path = "" ;
213+ }
214+
215+ try {
216+ request_ip = e . fields . request_ip [ 0 ] ;
217+ } catch ( e ) {
218+ request_ip = "" ;
219+ }
220+
221+ try {
222+ response_time = e . fields . response_time [ 0 ] ;
223+ } catch ( e ) {
224+ response_time = "" ;
225+ }
226+
227+ dataSet . push ( {
228+ "time" : e . fields . request_at [ 0 ] ,
229+ "country" : country ,
230+ "path" : path ,
231+ "ip" : request_ip ,
232+ "response" : response_time
233+ } ) ;
234+ } ) ;
235+ return dataSet ;
236+ }
237+
238+ RefreshTable ( ) ;
239+
240+ // removing loading state once loaded
241+ $ ( '#loadingState' ) . html ( "Loaded! Took <b>" + took + "</b>ms" ) ;
242+ dc . renderAll ( ) ;
243+ } ;
145244} ;
0 commit comments