Commit | Line | Data |
---|---|---|
22bc3e48 | 1 | (function (angular, $, _) { |
22bc3e48 TO |
2 | |
3 | // example: | |
4 | // scope.myAbtest = new CrmMailingAB(); | |
5 | // <crm-mailing-ab-block-mailing="{fromAddressA: 1, fromAddressB: 1}" crm-abtest="myAbtest" /> | |
1d4d0279 | 6 | var simpleDirectives = { |
ef5d18a1 TO |
7 | crmMailingAbBlockMailing: '~/crmMailingAB/joint-mailing.html', |
8 | crmMailingAbBlockSetup: '~/crmMailingAB/setup.html' | |
1d4d0279 TO |
9 | }; |
10 | _.each(simpleDirectives, function (templateUrl, directiveName) { | |
dfc2a868 | 11 | angular.module('crmMailingAB').directive(directiveName, function ($parse, crmMailingABCriteria, crmUiHelp) { |
1d4d0279 TO |
12 | var scopeDesc = {crmAbtest: '@'}; |
13 | scopeDesc[directiveName] = '@'; | |
14 | ||
15 | return { | |
16 | scope: scopeDesc, | |
17 | templateUrl: templateUrl, | |
18 | link: function (scope, elm, attr) { | |
19 | var model = $parse(attr.crmAbtest); | |
20 | scope.abtest = model(scope.$parent); | |
21 | scope.crmMailingConst = CRM.crmMailing; | |
22 | scope.crmMailingABCriteria = crmMailingABCriteria; | |
5d8901af | 23 | scope.ts = CRM.ts(null); |
1ce7f3e4 | 24 | scope.hs = crmUiHelp({file: 'CRM/Mailing/MailingUI'}); |
22bc3e48 | 25 | |
1d4d0279 TO |
26 | var fieldsModel = $parse(attr[directiveName]); |
27 | scope.fields = fieldsModel(scope.$parent); | |
28 | } | |
29 | }; | |
30 | }); | |
cdb373b4 TO |
31 | }); |
32 | ||
33 | // example: <div crm-mailing-ab-slider ng-model="abtest.ab.group_percentage"></div> | |
efd95528 | 34 | angular.module('crmMailingAB').directive('crmMailingAbSlider', function () { |
cdb373b4 TO |
35 | return { |
36 | require: '?ngModel', | |
37 | scope: {}, | |
ef5d18a1 | 38 | templateUrl: '~/crmMailingAB/slider.html', |
cdb373b4 TO |
39 | link: function (scope, element, attrs, ngModel) { |
40 | var TEST_MIN = 1, TEST_MAX = 50; | |
41 | var sliders = $('.slider-test,.slider-win', element); | |
42 | var sliderTests = $('.slider-test', element); | |
43 | var sliderWin = $('.slider-win', element); | |
44 | ||
5d8901af | 45 | scope.ts = CRM.ts(null); |
cdb373b4 TO |
46 | scope.testValue = 0; |
47 | scope.winValue = 100; | |
48 | ||
49 | // set the base value (following a GUI event) | |
50 | function setValue(value) { | |
51 | value = Math.min(TEST_MAX, Math.max(TEST_MIN, value)); | |
52 | scope.$apply(function () { | |
53 | ngModel.$setViewValue(value); | |
54 | scope.testValue = value; | |
55 | scope.winValue = 100 - (2 * scope.testValue); | |
56 | sliderTests.slider('value', scope.testValue); | |
57 | sliderWin.slider('value', scope.winValue); | |
58 | }); | |
59 | } | |
60 | ||
61 | sliders.slider({ | |
62 | min: 0, | |
63 | max: 100, | |
64 | range: 'min', | |
65 | step: 1 | |
66 | }); | |
67 | sliderTests.slider({ | |
68 | slide: function slideTest(event, ui) { | |
69 | event.preventDefault(); | |
70 | setValue(ui.value); | |
71 | } | |
72 | }); | |
73 | sliderWin.slider({ | |
74 | slide: function slideWinner(event, ui) { | |
75 | event.preventDefault(); | |
76 | setValue(Math.round((100 - ui.value) / 2)); | |
77 | } | |
78 | }); | |
79 | ||
80 | ngModel.$render = function () { | |
81 | scope.testValue = ngModel.$viewValue; | |
82 | scope.winValue = 100 - (2 * scope.testValue); | |
83 | sliderTests.slider('value', scope.testValue); | |
84 | sliderWin.slider('value', scope.winValue); | |
85 | }; | |
86 | } | |
87 | }; | |
88 | }); | |
360aaa75 TO |
89 | |
90 | // FIXME: This code is long and hasn't been fully working for me, but I've moved it into a spot | |
91 | // where it at least fits in a bit better. | |
92 | ||
93 | // example: <div crm-mailing-ab-stats="{split_count: 6, criteria:'Open'}" crm-abtest="myabtest" /> | |
94 | // options (see also: Mailing.graph_stats API) | |
95 | // - split_count: int | |
96 | // - criteria: string | |
97 | // - target_date: string, date | |
98 | // - target_url: string | |
99 | angular.module('crmMailingAB').directive('crmMailingAbStats', function (crmApi, $parse, crmNow) { | |
100 | return { | |
101 | scope: { | |
102 | crmMailingAbStats: '@', | |
103 | crmAbtest: '@' | |
104 | }, | |
105 | template: '<div class="crm-mailing-ab-stats"></div>', | |
106 | link: function (scope, element, attrs) { | |
107 | var abtestModel = $parse(attrs.crmAbtest); | |
108 | var optionModel = $parse(attrs.crmMailingAbStats); | |
109 | var options = angular.extend({}, optionModel(scope.$parent), { | |
110 | criteria: 'Open', // e.g. 'Open', 'Total Unique Clicks' | |
111 | split_count: 5 | |
112 | }); | |
113 | ||
114 | scope.$watch(attrs.crmAbtest, refresh); | |
115 | function refresh() { | |
116 | var now = crmNow(); | |
117 | var abtest = abtestModel(scope.$parent); | |
118 | if (!abtest) { | |
119 | console.log('failed to draw stats - missing abtest'); | |
120 | return; | |
121 | } | |
122 | ||
123 | scope.graph_data = [ | |
124 | {}, | |
125 | {}, | |
126 | {}, | |
127 | {}, | |
128 | {} | |
129 | ]; | |
130 | var keep_cnt = 0; | |
131 | ||
132 | for (var i = 1; i <= options.split_count; i++) { | |
133 | var result = crmApi('MailingAB', 'graph_stats', { | |
134 | id: abtest.ab.id, | |
135 | target_date: abtest.ab.declare_winning_time ? abtest.ab.declare_winning_time : now, | |
136 | target_url: null, // FIXME | |
137 | criteria: options.criteria, | |
138 | split_count: options.split_count, | |
139 | split_count_select: i | |
140 | }); | |
175762a3 | 141 | /*jshint -W083 */ |
360aaa75 TO |
142 | result.then(function (data) { |
143 | var temp = 0; | |
144 | keep_cnt++; | |
145 | for (var key in data.values.A) { | |
146 | temp = key; | |
147 | } | |
148 | var t = data.values.A[temp].time.split(" "); | |
149 | var m = t[0]; | |
150 | var year = t[2]; | |
151 | var day = t[1].substr(0, t[1].length - 3); | |
f2bad133 | 152 | var t1, hur, hour, min; |
175762a3 | 153 | if (_.isEmpty(t[3])) { |
f2bad133 TO |
154 | t1 = t[4].split(":"); |
155 | hur = t1[0]; | |
360aaa75 TO |
156 | if (t[5] == "AM") { |
157 | hour = hur; | |
158 | if (hour == 12) { | |
159 | hour = 0; | |
160 | } | |
161 | } | |
162 | if (t[5] == "PM") { | |
163 | hour = parseInt(hur) + 12; | |
164 | } | |
f2bad133 | 165 | min = t1[1]; |
360aaa75 TO |
166 | } |
167 | else { | |
f2bad133 TO |
168 | t1 = t[3].split(":"); |
169 | hur = t1[0]; | |
360aaa75 TO |
170 | if (t[4] == "AM") { |
171 | hour = hur; | |
172 | if (hour == 12) { | |
173 | hour = 0; | |
174 | } | |
175 | } | |
176 | if (t[4] == "PM") { | |
177 | hour = parseInt(hur) + 12; | |
178 | } | |
f2bad133 | 179 | min = t1[1]; |
360aaa75 TO |
180 | } |
181 | var month = 0; | |
182 | switch (m) { | |
183 | case "January": | |
184 | month = 0; | |
185 | break; | |
186 | case "February": | |
187 | month = 1; | |
188 | break; | |
189 | case "March": | |
190 | month = 2; | |
191 | break; | |
192 | case "April": | |
193 | month = 3; | |
194 | break; | |
195 | case "May": | |
196 | month = 4; | |
197 | break; | |
198 | case "June": | |
199 | month = 5; | |
200 | break; | |
201 | case "July": | |
202 | month = 6; | |
203 | break; | |
204 | case "August": | |
205 | month = 7; | |
206 | break; | |
207 | case "September": | |
208 | month = 8; | |
209 | break; | |
210 | case "October": | |
211 | month = 9; | |
212 | break; | |
213 | case "November": | |
214 | month = 10; | |
215 | break; | |
216 | case "December": | |
217 | month = 11; | |
218 | break; | |
219 | ||
220 | } | |
221 | var tp = new Date(year, month, day, hour, min, 0, 0); | |
222 | scope.graph_data[temp - 1] = { | |
223 | time: tp, | |
224 | x: data.values.A[temp].count, | |
225 | y: data.values.B[temp].count | |
226 | }; | |
227 | ||
228 | if (keep_cnt == options.split_count) { | |
229 | scope.graphload = true; | |
f2bad133 | 230 | data = scope.graph_data; |
360aaa75 TO |
231 | |
232 | // set up a colour variable | |
233 | var color = d3.scale.category10(); | |
234 | ||
235 | // map one colour each to x, y and z | |
236 | // keys grabs the key value or heading of each key value pair in the json | |
237 | // but not time | |
238 | color.domain(d3.keys(data[0]).filter(function (key) { | |
239 | return key !== "time"; | |
240 | })); | |
241 | ||
242 | // create a nested series for passing to the line generator | |
243 | // it's best understood by console logging the data | |
244 | var series = color.domain().map(function (name) { | |
245 | return { | |
246 | name: name, | |
247 | values: data.map(function (d) { | |
248 | return { | |
249 | time: d.time, | |
250 | score: +d[name] | |
251 | }; | |
252 | }) | |
253 | }; | |
254 | }); | |
255 | ||
256 | // Set the dimensions of the canvas / graph | |
257 | var margin = { | |
258 | top: 30, | |
259 | right: 20, | |
260 | bottom: 40, | |
261 | left: 75 | |
262 | }, | |
263 | width = 550 - margin.left - margin.right, | |
264 | height = 350 - margin.top - margin.bottom; | |
265 | ||
266 | // Set the ranges | |
267 | //var x = d3.time.scale().range([0, width]).domain([0,10]); | |
268 | var x = d3.time.scale().range([0, width]); | |
269 | var y = d3.scale.linear().range([height, 0]); | |
270 | ||
271 | // Define the axes | |
272 | var xAxis = d3.svg.axis().scale(x) | |
273 | .orient("bottom").ticks(10); | |
274 | ||
275 | var yAxis = d3.svg.axis().scale(y) | |
276 | .orient("left").ticks(5); | |
277 | ||
278 | // Define the line | |
279 | // Note you plot the time / score pair from each key you created ealier | |
280 | var valueline = d3.svg.line() | |
281 | .x(function (d) { | |
282 | return x(d.time); | |
283 | }) | |
284 | .y(function (d) { | |
285 | return y(d.score); | |
286 | }); | |
287 | ||
288 | // Adds the svg canvas | |
289 | var svg = d3.select($('.crm-mailing-ab-stats', element)[0]) | |
290 | .append("svg") | |
291 | .attr("width", width + margin.left + margin.right) | |
292 | .attr("height", height + margin.top + margin.bottom) | |
293 | .append("g") | |
294 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
295 | ||
296 | // Scale the range of the data | |
297 | x.domain(d3.extent(data, function (d) { | |
298 | return d.time; | |
299 | })); | |
300 | ||
301 | // note the nested nature of this you need to dig an additional level | |
302 | y.domain([ | |
303 | d3.min(series, function (c) { | |
304 | return d3.min(c.values, function (v) { | |
305 | return v.score; | |
306 | }); | |
307 | }), | |
308 | d3.max(series, function (c) { | |
309 | return d3.max(c.values, function (v) { | |
310 | return v.score; | |
311 | }); | |
312 | }) | |
313 | ]); | |
314 | svg.append("text") // text label for the x axis | |
315 | .attr("x", width / 2) | |
316 | .attr("y", height + margin.bottom) | |
317 | .style("text-anchor", "middle") | |
318 | .text("Time"); | |
319 | ||
320 | svg.append("text") // text label for the x axis | |
321 | .style("text-anchor", "middle") | |
322 | .text(scope.winnercriteria).attr("transform",function (d) { | |
f2bad133 | 323 | return "rotate(-90)"; |
360aaa75 TO |
324 | }).attr("x", -height / 2) |
325 | .attr("y", -30); | |
326 | ||
327 | // create a variable called series and bind the date | |
328 | // for each series append a g element and class it as series for css styling | |
f2bad133 | 329 | series = svg.selectAll(".series") |
360aaa75 TO |
330 | .data(series) |
331 | .enter().append("g") | |
332 | .attr("class", "series"); | |
333 | ||
334 | // create the path for each series in the variable series i.e. x, y and z | |
335 | // pass each object called x, y nad z to the lne generator | |
336 | series.append("path") | |
337 | .attr("class", "line") | |
338 | .attr("d", function (d) { | |
339 | // console.log(d); // to see how d3 iterates through series | |
340 | return valueline(d.values); | |
341 | }) | |
342 | .style("stroke", function (d) { | |
343 | return color(d.name); | |
344 | }); | |
345 | ||
346 | // Add the X Axis | |
347 | svg.append("g") // Add the X Axis | |
348 | .attr("class", "x axis") | |
349 | .attr("transform", "translate(0," + height + ")") | |
350 | .call(xAxis) | |
351 | .selectAll("text") | |
352 | .attr("transform", function (d) { | |
353 | return "rotate(-30)"; | |
354 | }); | |
355 | ||
356 | // Add the Y Axis | |
357 | svg.append("g") // Add the Y Axis | |
358 | .attr("class", "y axis") | |
359 | .call(yAxis); | |
360 | } | |
361 | }); | |
362 | } | |
363 | } | |
364 | } // link() | |
365 | }; | |
366 | }); | |
22bc3e48 | 367 | })(angular, CRM.$, CRM._); |