Skip to content
This repository was archived by the owner on Sep 10, 2021. It is now read-only.

Commit ef84fb4

Browse files
committed
Merge branch '0917-UserScalarSumissions-zach_mullen'
* 0917-UserScalarSumissions-zach_mullen: ENH: refs #917. Add config option for temp scalar TTL ENH: refs #917. Schedule temp/unofficial scalars for deletion ENH: refs #917. Use line color itself to render official scalars ENH: refs #917. Put note about red points in trend view page ENH: refs #917. Display unofficial scalar points in different color ENH: refs #917. Add ability to delete a trend ENH: refs #917. Show user's unofficial submissions on trend plot ENH: refs #917. Add user_id and official fields to tracker_scalar
2 parents 7119edc + eefd6d7 commit ef84fb4

18 files changed

Lines changed: 623 additions & 31 deletions
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
(function($) {
2+
3+
// Class: $.jqplot.LinDifferentColorMarkerLineRenderereRenderer
4+
// Line renderer for jqPlot, this class has one
5+
// markerColors: []
6+
// Draws series as a line.
7+
$.jqplot.DifferentColorMarkerLineRenderer = function(options){
8+
this.markerColors = [];
9+
this.shapeRenderer = new $.jqplot.ShapeRenderer();
10+
this.shadowRenderer = new $.jqplot.ShadowRenderer();
11+
$.extend(true, this, options);
12+
};
13+
14+
// called with scope of series.
15+
$.jqplot.DifferentColorMarkerLineRenderer.prototype.init = function(options) {
16+
//$.extend(true, this, options);
17+
// set the shape renderer options
18+
this.markerColors = options.markerColors || this.markerColors;
19+
var opts = {
20+
lineJoin:'round',
21+
lineCap:'round',
22+
fill:this.fill,
23+
isarc:false,
24+
strokeStyle:this.color,
25+
fillStyle:this.fillColor,
26+
lineWidth:this.lineWidth,
27+
closePath:this.fill
28+
29+
};
30+
this.renderer.shapeRenderer.init(opts);
31+
// set the shadow renderer options
32+
// scale the shadowOffset to the width of the line.
33+
if (this.lineWidth > 2.5) {
34+
var shadow_offset = this.shadowOffset* (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6);
35+
// var shadow_offset = this.shadowOffset;
36+
}
37+
// for skinny lines, don't make such a big shadow.
38+
else {
39+
var shadow_offset = this.shadowOffset*Math.atan((this.lineWidth/2.5))/0.785398163;
40+
}
41+
var sopts = {
42+
lineJoin:'round',
43+
lineCap:'round',
44+
fill:this.fill,
45+
isarc:false,
46+
angle:this.shadowAngle,
47+
offset:shadow_offset,
48+
alpha:this.shadowAlpha,
49+
depth:this.shadowDepth,
50+
lineWidth:this.lineWidth,
51+
closePath:this.fill
52+
};
53+
this.renderer.shadowRenderer.init(sopts);
54+
};
55+
56+
// Method: setGridData
57+
// converts the user data values to grid coordinates and stores them
58+
// in the gridData array.
59+
// Called with scope of a series.
60+
$.jqplot.DifferentColorMarkerLineRenderer.prototype.setGridData = function(plot) {
61+
// recalculate the grid data
62+
var xp = this._xaxis.series_u2p;
63+
var yp = this._yaxis.series_u2p;
64+
var data = this._plotData;
65+
var pdata = this._prevPlotData;
66+
this.gridData = [];
67+
this._prevGridData = [];
68+
for (var i=0; i<this.data.length; i++) {
69+
if (data[i] != null) {
70+
this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
71+
}
72+
if (pdata[i] != null) {
73+
this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]);
74+
}
75+
}
76+
};
77+
78+
// Method: makeGridData
79+
// converts any arbitrary data values to grid coordinates and
80+
// returns them. This method exists so that plugins can use a series'
81+
// linerenderer to generate grid data points without overwriting the
82+
// grid data associated with that series.
83+
// Called with scope of a series.
84+
$.jqplot.DifferentColorMarkerLineRenderer.prototype.makeGridData = function(data, plot) {
85+
// recalculate the grid data
86+
var xp = this._xaxis.series_u2p;
87+
var yp = this._yaxis.series_u2p;
88+
var gd = [];
89+
var pgd = [];
90+
for (var i=0; i<data.length; i++) {
91+
if (data[i] != null) {
92+
gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]);
93+
}
94+
}
95+
return gd;
96+
};
97+
98+
99+
// called within scope of series.
100+
$.jqplot.DifferentColorMarkerLineRenderer.prototype.draw = function(ctx, gd, options) {
101+
var i;
102+
var opts = (options != undefined) ? options : {};
103+
var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
104+
var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
105+
var fill = (opts.fill != undefined) ? opts.fill : this.fill;
106+
var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
107+
ctx.save();
108+
if (gd.length) {
109+
if (showLine) {
110+
// if we fill, we'll have to add points to close the curve.
111+
if (fill) {
112+
if (this.fillToZero) {
113+
// have to break line up into shapes at axis crossings
114+
var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
115+
var negativeColor = negativeColors.get(this.index);
116+
if (! this.useNegativeColors) {
117+
negativeColor = opts.fillStyle;
118+
}
119+
var isnegative = false;
120+
var posfs = opts.fillStyle;
121+
122+
// if stoking line as well as filling, get a copy of line data.
123+
if (fillAndStroke) {
124+
var fasgd = gd.slice(0);
125+
}
126+
// if not stacked, fill down to axis
127+
if (this.index == 0 || !this._stack) {
128+
129+
var tempgd = [];
130+
var pyzero = this._yaxis.series_u2p(0);
131+
var pxzero = this._xaxis.series_u2p(0);
132+
133+
if (this.fillAxis == 'y') {
134+
tempgd.push([gd[0][0], pyzero]);
135+
136+
for (var i=0; i<gd.length-1; i++) {
137+
tempgd.push(gd[i]);
138+
// do we have an axis crossing?
139+
if (this._plotData[i][1] * this._plotData[i+1][1] < 0) {
140+
if (this._plotData[i][1] < 0) {
141+
isnegative = true;
142+
opts.fillStyle = negativeColor;
143+
}
144+
else {
145+
isnegative = false;
146+
opts.fillStyle = posfs;
147+
}
148+
149+
var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]);
150+
tempgd.push([xintercept, pyzero]);
151+
// now draw this shape and shadow.
152+
if (shadow) {
153+
this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
154+
}
155+
this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
156+
// now empty temp array and continue
157+
tempgd = [[xintercept, pyzero]];
158+
}
159+
}
160+
if (this._plotData[gd.length-1][1] < 0) {
161+
isnegative = true;
162+
opts.fillStyle = negativeColor;
163+
}
164+
else {
165+
isnegative = false;
166+
opts.fillStyle = posfs;
167+
}
168+
tempgd.push(gd[gd.length-1]);
169+
tempgd.push([gd[gd.length-1][0], pyzero]);
170+
}
171+
// now draw this shape and shadow.
172+
if (shadow) {
173+
this.renderer.shadowRenderer.draw(ctx, tempgd, opts);
174+
}
175+
this.renderer.shapeRenderer.draw(ctx, tempgd, opts);
176+
177+
178+
// var gridymin = this._yaxis.series_u2p(0);
179+
// // IE doesn't return new length on unshift
180+
// gd.unshift([gd[0][0], gridymin]);
181+
// len = gd.length;
182+
// gd.push([gd[len - 1][0], gridymin]);
183+
}
184+
// if stacked, fill to line below
185+
else {
186+
var prev = this._prevGridData;
187+
for (var i=prev.length; i>0; i--) {
188+
gd.push(prev[i-1]);
189+
}
190+
if (shadow) {
191+
this.renderer.shadowRenderer.draw(ctx, gd, opts);
192+
}
193+
194+
this.renderer.shapeRenderer.draw(ctx, gd, opts);
195+
}
196+
}
197+
else {
198+
// if stoking line as well as filling, get a copy of line data.
199+
if (fillAndStroke) {
200+
var fasgd = gd.slice(0);
201+
}
202+
// if not stacked, fill down to axis
203+
if (this.index == 0 || !this._stack) {
204+
// var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2;
205+
var gridymin = ctx.canvas.height;
206+
// IE doesn't return new length on unshift
207+
gd.unshift([gd[0][0], gridymin]);
208+
len = gd.length;
209+
gd.push([gd[len - 1][0], gridymin]);
210+
}
211+
// if stacked, fill to line below
212+
else {
213+
var prev = this._prevGridData;
214+
for (var i=prev.length; i>0; i--) {
215+
gd.push(prev[i-1]);
216+
}
217+
}
218+
if (shadow) {
219+
this.renderer.shadowRenderer.draw(ctx, gd, opts);
220+
}
221+
222+
this.renderer.shapeRenderer.draw(ctx, gd, opts);
223+
}
224+
if (fillAndStroke) {
225+
var fasopts = $.extend(true, {}, opts, {
226+
fill:false,
227+
closePath:false
228+
});
229+
this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts);
230+
//////////
231+
// TODO: figure out some way to do shadows nicely
232+
// if (shadow) {
233+
// this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts);
234+
// }
235+
// now draw the markers
236+
if (this.markerRenderer.show) {
237+
for (i=0; i<fasgd.length; i++) {
238+
this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions);
239+
}
240+
}
241+
}
242+
}
243+
else {
244+
if (shadow) {
245+
this.renderer.shadowRenderer.draw(ctx, gd, opts);
246+
}
247+
this.renderer.shapeRenderer.draw(ctx, gd, opts);
248+
}
249+
}
250+
251+
// now draw the markers
252+
if (this.markerRenderer.show && !fill) {
253+
for (i=0; i<gd.length; i++) {
254+
// Grab each color and send it to a new markerRenderer.
255+
opts.markerOptions = {
256+
color: this.markerColors[i] || this.color
257+
};
258+
this.markerRenderer = new $.jqplot.MarkerRenderer;
259+
this.markerRenderer.init(opts.markerOptions);
260+
this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions);
261+
}
262+
}
263+
}
264+
265+
ctx.restore();
266+
};
267+
268+
$.jqplot.DifferentColorMarkerLineRenderer.prototype.drawShadow = function(ctx, gd, options) {
269+
// This is a no-op, shadows drawn with lines.
270+
};
271+
272+
})(jQuery);

modules/tracker/Notification.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Tracker_Notification extends ApiEnabled_Notification
1616
{
1717
public $moduleName = 'tracker';
1818
public $_models = array('User');
19-
public $_moduleModels = array('Trend');
19+
public $_moduleModels = array('Scalar', 'Trend');
2020
public $_moduleComponents = array('Api');
2121

2222
/** init notification process*/
@@ -33,6 +33,7 @@ public function init()
3333
$this->addCallBack('CALLBACK_CORE_USER_DELETED', 'userDeleted');
3434

3535
$this->addTask('TASK_TRACKER_SEND_THRESHOLD_NOTIFICATION', 'sendEmail', 'Send threshold violation email');
36+
$this->addTask('TASK_TRACKER_DELETE_TEMP_SCALAR', 'deleteTempScalar', 'Delete an unofficial/temporary scalar value');
3637
$this->enableWebAPI($this->moduleName);
3738
}//end init
3839

@@ -72,6 +73,17 @@ public function userDeleted($args)
7273
$user = $args['userDao'];
7374
}
7475

76+
/**
77+
* Delete temporary (unofficial) scalars after n hours, where n is specified as
78+
* a module configuration option
79+
*/
80+
public function deleteTempScalar($params)
81+
{
82+
$scalarId = $params['scalarId'];
83+
$scalar = $this->Tracker_Scalar->load($scalarId);
84+
$this->Tracker_Scalar->delete($scalar);
85+
}
86+
7587
/**
7688
* Send an email to the user that a threshold was crossed
7789
*/

modules/tracker/controllers/ConfigController.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ function indexAction()
3030
{
3131
$this->requireAdminPrivileges();
3232
$this->view->repoBrowserUrl = $this->Setting->getValueByName('repoBrowserUrl', $this->moduleName);
33+
$this->view->tempScalarTtl = $this->Setting->getValueByName('tempScalarTtl', $this->moduleName);
34+
if(!$this->view->tempScalarTtl)
35+
{
36+
$this->view->tempScalarTtl = 24; //default to 24 hours
37+
}
3338

3439
$breadcrumbs = array();
3540
$breadcrumbs[] = array('type' => 'moduleList');
@@ -49,7 +54,9 @@ function submitAction()
4954
$this->disableView();
5055

5156
$repoBrowserUrl = $this->_getParam('repoBrowserUrl');
52-
$this->Setting->setConfig('repoBrowserUrl', $repoBrowserUrl, $this->moduleName);
57+
$tempScalarTtl = $this->_getParam('tempScalarTtl');
58+
$this->Setting->setConfig('repoBrowserUrl', $repoBrowserUrl, $this->moduleName);
59+
$this->Setting->setConfig('tempScalarTtl', $tempScalarTtl, $this->moduleName);
5360

5461
echo JsonComponent::encode(array('message' => 'Changes saved', 'status' => 'ok'));
5562
}

modules/tracker/controllers/ScalarController.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ public function detailsAction()
5252
}
5353
$this->view->resultItems = $this->Tracker_Scalar->getAssociatedItems($scalar);
5454
$this->view->otherValues = $this->Tracker_Scalar->getOtherValuesFromSubmission($scalar);
55+
56+
if($scalar->getUserId() != -1)
57+
{
58+
$this->view->submittedBy = $scalar->getUser();
59+
}
60+
else
61+
{
62+
$this->view->submittedBy = null;
63+
}
5564
}
5665

5766
/**

0 commit comments

Comments
 (0)