sidekiq 6.5.4 → 6.5.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

@@ -0,0 +1,7 @@
1
+ /*!
2
+ * chartjs-plugin-annotation v2.0.0
3
+ * https://www.chartjs.org/chartjs-plugin-annotation/index
4
+ * (c) 2022 chartjs-plugin-annotation Contributors
5
+ * Released under the MIT License
6
+ */
7
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js","chart.js/helpers"],e):(t="undefined"!=typeof globalThis?globalThis:t||self)["chartjs-plugin-annotation"]=e(t.Chart,t.Chart.helpers)}(this,(function(t,e){"use strict";const o={modes:{point:(t,e)=>r(t,e,{intersect:!0}),nearest:(t,o,n)=>function(t,o,n){let i=Number.POSITIVE_INFINITY;return r(t,o,n).reduce(((t,r)=>{const s=r.getCenterPoint(),a=function(t,e,o){if("x"===o)return{x:t.x,y:e.y};if("y"===o)return{x:e.x,y:t.y};return e}(o,s,n.axis),d=e.distanceBetweenPoints(o,a);return d<i?(t=[r],i=d):d===i&&t.push(r),t}),[]).sort(((t,e)=>t._index-e._index)).slice(0,1)}(t,o,n),x:(t,e,o)=>r(t,e,{intersect:o.intersect,axis:"x"}),y:(t,e,o)=>r(t,e,{intersect:o.intersect,axis:"y"})}};function n(t,e,n){return(o.modes[n.mode]||o.modes.nearest)(t,e,n)}function r(t,e,o){return t.visibleElements.filter((t=>o.intersect?t.inRange(e.x,e.y):function(t,e,o){return"x"!==o&&"y"!==o?t.inRange(e.x,e.y,"x",!0)||t.inRange(e.x,e.y,"y",!0):t.inRange(e.x,e.y,o,!0)}(t,e,o.axis)))}const i=["enter","leave"],s=i.concat("click");function a(t,e,o){if(t.listened)switch(e.type){case"mousemove":case"mouseout":return function(t,e,o){if(!t.moveListened)return;let r;r="mousemove"===e.type?n(t,e,o.interaction):[];const i=t.hovered;t.hovered=r;const s={state:t,event:e};let a=d(s,"leave",i,r);return d(s,"enter",r,i)||a}(t,e,o);case"click":return function(t,e,o){const r=t.listeners,i=n(t,e,o.interaction);let s;for(const t of i)s=l(t.options.click||r.click,t,e)||s;return s}(t,e,o)}}function d({state:t,event:e},o,n,r){let i;for(const s of n)r.indexOf(s)<0&&(i=l(s.options[o]||t.listeners[o],s,e)||i);return i}function l(t,o,n){return!0===e.callback(t,[o.$context,n])}const c=(t,e)=>e>t||t.length>e.length&&t.slice(0,e.length)===e,h=.001,u=(t,e,o)=>Math.min(o,Math.max(e,t));function f(t,e,o){for(const n of Object.keys(t))t[n]=u(t[n],e,o);return t}function x(t,{x:e,y:o,x2:n,y2:r},i,s){const a=s/2,d=t.x>=e-a-h&&t.x<=n+a+h,l=t.y>=o-a-h&&t.y<=r+a+h;return"x"===i?d:("y"===i||d)&&l}function y(t,e){const{centerX:o,centerY:n}=t.getProps(["centerX","centerY"],e);return{x:o,y:n}}const p=t=>"string"==typeof t&&t.endsWith("%"),b=t=>u(parseFloat(t)/100,0,1);function g(t,e){return"start"===e?0:"end"===e?t:p(e)?b(e)*t:t/2}function m(t,e){return"number"==typeof e?e:p(e)?b(e)*t:t}function w(t){return e.isObject(t)?{x:e.valueOrDefault(t.x,"center"),y:e.valueOrDefault(t.y,"center")}:{x:t=e.valueOrDefault(t,"center"),y:t}}function v(t){return t&&(e.defined(t.xValue)||e.defined(t.yValue))}const S=new Map;function M(t){if(t&&"object"==typeof t){const e=t.toString();return"[object HTMLImageElement]"===e||"[object HTMLCanvasElement]"===e}}function C(t,{x:o,y:n},r){r&&(t.translate(o,n),t.rotate(e.toRadians(r)),t.translate(-o,-n))}function D(t,e){if(e&&e.borderWidth)return t.lineCap=e.borderCapStyle,t.setLineDash(e.borderDash),t.lineDashOffset=e.borderDashOffset,t.lineJoin=e.borderJoinStyle,t.lineWidth=e.borderWidth,t.strokeStyle=e.borderColor,!0}function j(t,e){t.shadowColor=e.backgroundShadowColor,t.shadowBlur=e.shadowBlur,t.shadowOffsetX=e.shadowOffsetX,t.shadowOffsetY=e.shadowOffsetY}function k(t,o){const n=o.content;if(M(n))return{width:m(n.width,o.width),height:m(n.height,o.height)};const r=e.toFont(o.font),i=o.textStrokeWidth,s=e.isArray(n)?n:[n],a=s.join()+r.string+i+(t._measureText?"-spriting":"");if(!S.has(a)){t.save(),t.font=r.string;const e=s.length;let o=0;for(let n=0;n<e;n++){const e=s[n];o=Math.max(o,t.measureText(e).width+i)}t.restore();const n=e*r.lineHeight+i;S.set(a,{width:o,height:n})}return S.get(a)}function P(t,o,n){const{x:r,y:i,width:s,height:a}=o;t.save(),j(t,n);const d=D(t,n);t.fillStyle=n.backgroundColor,t.beginPath(),e.addRoundedRectPath(t,{x:r,y:i,w:s,h:a,radius:f(e.toTRBLCorners(n.borderRadius),0,Math.min(s,a)/2)}),t.closePath(),t.fill(),d&&(t.shadowColor=n.borderShadowColor,t.stroke()),t.restore()}function O(t,o,n){return o="number"==typeof o?o:t.parse(o),e.isFinite(o)?t.getPixelForValue(o):n}function A(t,e,o){const n=e[o];if(n||"scaleID"===o)return n;const r=o.charAt(0),i=Object.values(t).filter((t=>t.axis&&t.axis===r));return i.length?i[0].id:r}function I(t,e){if(t){const o=t.options.reverse;return{start:O(t,e.min,o?e.end:e.start),end:O(t,e.max,o?e.start:e.end)}}}function Y(t,e){const{chartArea:o,scales:n}=t,r=n[A(n,e,"xScaleID")],i=n[A(n,e,"yScaleID")];let s=o.width/2,a=o.height/2;return r&&(s=O(r,e.xValue,r.left+r.width/2)),i&&(a=O(i,e.yValue,i.top+i.height/2)),{x:s,y:a}}function R(t,e){const o=t.scales,n=o[A(o,e,"xScaleID")],r=o[A(o,e,"yScaleID")];if(!n&&!r)return{};let{left:i,right:s}=n||t.chartArea,{top:a,bottom:d}=r||t.chartArea;const l=X(n,{min:e.xMin,max:e.xMax,start:i,end:s});i=l.start,s=l.end;const c=X(r,{min:e.yMin,max:e.yMax,start:d,end:a});return a=c.start,d=c.end,{x:i,y:a,x2:s,y2:d,width:s-i,height:d-a,centerX:i+(s-i)/2,centerY:a+(d-a)/2}}function W(t,e){if(!v(e)){const o=R(t,e);let n=e.radius;n&&!isNaN(n)||(n=Math.min(o.width,o.height)/2,e.radius=n);const r=2*n;return{x:o.x+e.xAdjust,y:o.y+e.yAdjust,x2:o.x+r+e.xAdjust,y2:o.y+r+e.yAdjust,centerX:o.centerX+e.xAdjust,centerY:o.centerY+e.yAdjust,width:r,height:r}}return function(t,e){const o=Y(t,e),n=2*e.radius;return{x:o.x-e.radius+e.xAdjust,y:o.y-e.radius+e.yAdjust,x2:o.x+e.radius+e.xAdjust,y2:o.y+e.radius+e.yAdjust,centerX:o.x+e.xAdjust,centerY:o.y+e.yAdjust,width:n,height:n}}(t,e)}function X(t,e){const o=I(t,e)||e;return{start:Math.min(o.start,o.end),end:Math.max(o.start,o.end)}}function E(t,e,o){const n=Math.cos(o),r=Math.sin(o),i=e.x,s=e.y;return{x:i+n*(t.x-i)-r*(t.y-s),y:s+r*(t.x-i)+n*(t.y-s)}}function T(t,o,n){const r=function(t,o,n){const r=o.axis,i=o.id,s=r+"ScaleID",a={min:e.valueOrDefault(o.min,Number.NEGATIVE_INFINITY),max:e.valueOrDefault(o.max,Number.POSITIVE_INFINITY)};for(const e of n)e.scaleID===i?V(e,o,["value","endValue"],a):A(t,e,s)===i&&V(e,o,[r+"Min",r+"Max",r+"Value"],a);return a}(t.scales,o,n);let i=z(o,r,"min","suggestedMin");i=z(o,r,"max","suggestedMax")||i,i&&"function"==typeof o.handleTickRangeOptions&&o.handleTickRangeOptions()}function z(t,o,n,r){if(e.isFinite(o[n])&&!function(t,o,n){return e.defined(t[o])||e.defined(t[n])}(t.options,n,r)){const e=t[n]!==o[n];return t[n]=o[n],e}}function _(t,e){for(const o of["scaleID","xScaleID","yScaleID"]){const n=A(e,t,o);n&&!e[n]&&N(t,o)&&console.warn(`No scale found with id '${n}' for annotation '${t.id}'`)}}function N(t,o){if("scaleID"===o)return!0;const n=o.charAt(0);for(const o of["Min","Max","Value"])if(e.defined(t[n+o]))return!0;return!1}function V(t,o,n,r){for(const i of n){const n=t[i];if(e.defined(n)){const t=o.parse(n);r.min=Math.min(r.min,t),r.max=Math.max(r.max,t)}}}class B extends t.Element{inRange(t,o,n,r){const{x:i,y:s}=E({x:t,y:o},this.getCenterPoint(r),e.toRadians(-this.options.rotation));return x({x:i,y:s},this.getProps(["x","y","x2","y2"],r),n,this.options.borderWidth)}getCenterPoint(t){return y(this,t)}draw(t){t.save(),C(t,this.getCenterPoint(),this.options.rotation),P(t,this,this.options),t.restore()}get label(){return this.elements&&this.elements[0]}resolveElementProperties(t,e){const o=R(t,e),{x:n,y:r}=o;return o.elements=[{type:"label",optionScope:"label",properties:L(t,o,e)}],o.initProperties={x:n,y:r},o}}function H(t,e){const{start:o,end:n,borderWidth:r}=t,{position:i,padding:{start:s,end:a},adjust:d}=e;return o+r/2+d+g(n-r-o-s-a-e.size,i)}function L(t,o,n){const r=n.label;r.backgroundColor="transparent",r.callout.display=!1;const i=w(r.position),s=e.toPadding(r.padding),a=k(t.ctx,r),d=function({properties:t,options:e},o,n,r){const{x:i,x2:s,width:a}=t;return H({start:i,end:s,size:a,borderWidth:e.borderWidth},{position:n.x,padding:{start:r.left,end:r.right},adjust:e.label.xAdjust,size:o.width})}({properties:o,options:n},a,i,s),l=function({properties:t,options:e},o,n,r){const{y:i,y2:s,height:a}=t;return H({start:i,end:s,size:a,borderWidth:e.borderWidth},{position:n.y,padding:{start:r.top,end:r.bottom},adjust:e.label.yAdjust,size:o.height})}({properties:o,options:n},a,i,s),c=a.width+s.width,h=a.height+s.height;return{x:d,y:l,x2:d+c,y2:l+h,width:c,height:h,centerX:d+c/2,centerY:l+h/2,rotation:r.rotation}}B.id="boxAnnotation",B.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:0,borderShadowColor:"transparent",borderWidth:1,display:!0,label:{backgroundColor:"transparent",borderWidth:0,callout:{display:!1},color:"black",content:null,display:!1,drawTime:void 0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:"bold"},height:void 0,padding:6,position:"center",rotation:void 0,textAlign:"start",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,yAdjust:0,z:void 0},rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},B.defaultRoutes={borderColor:"color",backgroundColor:"color"},B.descriptors={label:{_fallback:!0}};const $=(t,e,o)=>({x:t.x+o*(e.x-t.x),y:t.y+o*(e.y-t.y)}),F=(t,e,o)=>$(e,o,Math.abs((t-e.y)/(o.y-e.y))).x,J=(t,e,o)=>$(e,o,Math.abs((t-e.x)/(o.x-e.x))).y,q=t=>t*t;class U extends t.Element{inRange(t,e,o,n){const r=this.options.borderWidth/2;if("x"!==o&&"y"!==o){const o={mouseX:t,mouseY:e};return function(t,{mouseX:e,mouseY:o},n=.001,r){const{x:i,y:s,x2:a,y2:d}=t.getProps(["x","y","x2","y2"],r),l=a-i,c=d-s,h=q(l)+q(c),u=0===h?-1:((e-i)*l+(o-s)*c)/h;let f,x;u<0?(f=i,x=s):u>1?(f=a,x=d):(f=i+u*l,x=s+u*c);return q(e-f)+q(o-x)<=n}(this,o,q(r),n)||Q(this,o,n)}const i=((t,e,{x:o,y:n,x2:r,y2:i},s)=>"y"===s?{start:Math.min(n,i),end:Math.max(n,i),value:e}:{start:Math.min(o,r),end:Math.max(o,r),value:t})(t,e,this.getProps(["x","y","x2","y2"],n),o);return i.value>=i.start-r&&i.value<=i.end+r||Q(this,{mouseX:t,mouseY:e},n,o)}getCenterPoint(t){return y(this,t)}draw(t){const{x:e,y:o,x2:n,y2:r,options:i}=this;if(t.save(),!D(t,i))return t.restore();j(t,i);const s=Math.atan2(r-o,n-e),a=Math.sqrt(Math.pow(n-e,2)+Math.pow(r-o,2)),{startOpts:d,endOpts:l,startAdjust:c,endAdjust:h}=function(t){const e=t.options,o=e.arrowHeads&&e.arrowHeads.start,n=e.arrowHeads&&e.arrowHeads.end;return{startOpts:o,endOpts:n,startAdjust:nt(t,o),endAdjust:nt(t,n)}}(this);t.translate(e,o),t.rotate(s),t.beginPath(),t.moveTo(0+c,0),t.lineTo(a-h,0),t.shadowColor=i.borderShadowColor,t.stroke(),rt(t,0,c,d),rt(t,a,-h,l),t.restore()}get label(){return this.elements&&this.elements[0]}resolveElementProperties(t,e){const{scales:o,chartArea:n}=t,r=o[e.scaleID],i={x:n.left,y:n.top,x2:n.right,y2:n.bottom};let s,a;if(r)s=O(r,e.value,NaN),a=O(r,e.endValue,s),r.isHorizontal()?(i.x=s,i.x2=a):(i.y=s,i.y2=a);else{const t=o[A(o,e,"xScaleID")],n=o[A(o,e,"yScaleID")];t&&Z(i,t,{min:e.xMin,max:e.xMax,start:t.left,end:t.right,startProp:"x",endProp:"x2"}),n&&Z(i,n,{min:e.yMin,max:e.yMax,start:n.bottom,end:n.top,startProp:"y",endProp:"y2"})}const{x:d,y:l,x2:c,y2:h}=i,u=function({x:t,y:e,x2:o,y2:n},{top:r,right:i,bottom:s,left:a}){return!(t<a&&o<a||t>i&&o>i||e<r&&n<r||e>s&&n>s)}(i,t.chartArea),f=u?function(t,e,o){const{x:n,y:r}=K(t,e,o),{x:i,y:s}=K(e,t,o);return{x:n,y:r,x2:i,y2:s,width:Math.abs(i-n),height:Math.abs(s-r)}}({x:d,y:l},{x:c,y:h},t.chartArea):{x:d,y:l,x2:c,y2:h,width:Math.abs(c-d),height:Math.abs(h-l)};return f.centerX=(c+d)/2,f.centerY=(h+l)/2,u||(e.label.display=!1),f.elements=[{type:"label",optionScope:"label",properties:tt(t,f,e.label)}],f}}U.id="lineAnnotation";const G={backgroundColor:void 0,backgroundShadowColor:void 0,borderColor:void 0,borderDash:void 0,borderDashOffset:void 0,borderShadowColor:void 0,borderWidth:void 0,display:void 0,fill:void 0,length:void 0,shadowBlur:void 0,shadowOffsetX:void 0,shadowOffsetY:void 0,width:void 0};function K({x:t,y:e},o,{top:n,right:r,bottom:i,left:s}){return t<s&&(e=J(s,{x:t,y:e},o),t=s),t>r&&(e=J(r,{x:t,y:e},o),t=r),e<n&&(t=F(n,{x:t,y:e},o),e=n),e>i&&(t=F(i,{x:t,y:e},o),e=i),{x:t,y:e}}function Q(t,{mouseX:e,mouseY:o},n,r){const i=t.label;return i.options.display&&i.inRange(e,o,r,n)}function Z(t,e,o){const n=I(e,o);t[o.startProp]=n.start,t[o.endProp]=n.end}function tt(t,o,n){n.callout.display=!1;const r=n.borderWidth,i=e.toPadding(n.padding),s=k(t.ctx,n);return function(t,o,n,r){const{width:i,height:s,padding:a}=n,{xAdjust:d,yAdjust:l}=o,c={x:t.x,y:t.y},h={x:t.x2,y:t.y2},u="auto"===o.rotation?function(t){const{x:o,y:n,x2:r,y2:i}=t,s=Math.atan2(i-n,r-o);return s>e.PI/2?s-e.PI:s<e.PI/-2?s+e.PI:s}(t):e.toRadians(o.rotation),f=function(t,e,o){const n=Math.cos(o),r=Math.sin(o);return{w:Math.abs(t*n)+Math.abs(e*r),h:Math.abs(t*r)+Math.abs(e*n)}}(i,s,u),x=function(t,e,o,n){let r;const i=function(t,e){const{x:o,x2:n,y:r,y2:i}=t,s=Math.min(r,i)-e.top,a=Math.min(o,n)-e.left,d=e.bottom-Math.max(r,i),l=e.right-Math.max(o,n);return{x:Math.min(a,l),y:Math.min(s,d),dx:a<=l?1:-1,dy:s<=d?1:-1}}(t,n);r="start"===e.position?et({w:t.x2-t.x,h:t.y2-t.y},o,e,i):"end"===e.position?1-et({w:t.x-t.x2,h:t.y-t.y2},o,e,i):g(1,e.position);return r}(t,o,{labelSize:f,padding:a},r),y=$(c,h,x),p={size:f.w,min:r.left,max:r.right,padding:a.left},b={size:f.h,min:r.top,max:r.bottom,padding:a.top},m=ot(y.x,p)+d,w=ot(y.y,b)+l;return{x:m-i/2,y:w-s/2,x2:m+i/2,y2:w+s/2,centerX:m,centerY:w,width:i,height:s,rotation:e.toDegrees(u)}}(o,n,{width:s.width+i.width+r,height:s.height+i.height+r,padding:i},t.chartArea)}function et(t,e,o,n){const{labelSize:r,padding:i}=e,s=t.w*n.dx,a=t.h*n.dy,d=s>0&&(r.w/2+i.left-n.x)/s,l=a>0&&(r.h/2+i.top-n.y)/a;return u(Math.max(d,l),0,.25)}function ot(t,e){const{size:o,min:n,max:r,padding:i}=e,s=o/2;return o>r-n?(r+n)/2:(n>=t-i-s&&(t=n+i+s),r<=t+i+s&&(t=r-i-s),t)}function nt(t,e){if(!e||!e.display)return 0;const{length:o,width:n}=e,r=t.options.borderWidth/2,i={x:o,y:n+r},s={x:0,y:r};return Math.abs(F(0,i,s))}function rt(t,e,o,n){if(!n||!n.display)return;const{length:r,width:i,fill:s,backgroundColor:a,borderColor:d}=n,l=Math.abs(e-r)+o;t.beginPath(),j(t,n),D(t,n),t.moveTo(l,-i),t.lineTo(e+o,0),t.lineTo(l,i),!0===s?(t.fillStyle=a||d,t.closePath(),t.fill(),t.shadowColor="transparent"):t.shadowColor=n.borderShadowColor,t.stroke()}U.defaults={adjustScaleRange:!0,arrowHeads:{display:!1,end:Object.assign({},G),fill:!1,length:12,start:Object.assign({},G),width:6},borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:2,display:!0,endValue:void 0,label:{backgroundColor:"rgba(0,0,0,0.8)",backgroundShadowColor:"transparent",borderCapStyle:"butt",borderColor:"black",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:6,borderShadowColor:"transparent",borderWidth:0,callout:{display:!1},color:"#fff",content:null,display:!1,drawTime:void 0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:"bold"},height:void 0,padding:6,position:"center",rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,textAlign:"center",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,yAdjust:0,z:void 0},scaleID:void 0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,value:void 0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},U.descriptors={arrowHeads:{start:{_fallback:!0},end:{_fallback:!0},_fallback:!0}},U.defaultRoutes={borderColor:"color"};class it extends t.Element{inRange(t,o,n,r){const i=this.options.rotation,s=this.options.borderWidth;if("x"!==n&&"y"!==n)return function(t,o,n,r){const{width:i,height:s,centerX:a,centerY:d}=o,l=i/2,c=s/2;if(l<=0||c<=0)return!1;const h=e.toRadians(n||0),u=r/2||0,f=Math.cos(h),x=Math.sin(h),y=Math.pow(f*(t.x-a)+x*(t.y-d),2),p=Math.pow(x*(t.x-a)-f*(t.y-d),2);return y/Math.pow(l+u,2)+p/Math.pow(c+u,2)<=1.0001}({x:t,y:o},this.getProps(["width","height","centerX","centerY"],r),i,s);const{x:a,y:d,x2:l,y2:c}=this.getProps(["x","y","x2","y2"],r),u=s/2,f="y"===n?{start:d,end:c}:{start:a,end:l},x=E({x:t,y:o},this.getCenterPoint(r),e.toRadians(-i));return x[n]>=f.start-u-h&&x[n]<=f.end+u+h}getCenterPoint(t){return y(this,t)}draw(t){const{width:o,height:n,centerX:r,centerY:i,options:s}=this;t.save(),C(t,this.getCenterPoint(),s.rotation),j(t,this.options),t.beginPath(),t.fillStyle=s.backgroundColor;const a=D(t,s);t.ellipse(r,i,n/2,o/2,e.PI/2,0,2*e.PI),t.fill(),a&&(t.shadowColor=s.borderShadowColor,t.stroke()),t.restore()}resolveElementProperties(t,e){return R(t,e)}}it.id="ellipseAnnotation",it.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:1,display:!0,rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},it.defaultRoutes={borderColor:"color",backgroundColor:"color"};const st=["left","bottom","top","right"];class at extends t.Element{inRange(t,o,n,r){const{x:i,y:s}=E({x:t,y:o},this.getCenterPoint(r),e.toRadians(-this.rotation));return x({x:i,y:s},this.getProps(["x","y","x2","y2"],r),n,this.options.borderWidth)}getCenterPoint(t){return y(this,t)}draw(t){const o=this.options;o.display&&o.content&&(t.save(),C(t,this.getCenterPoint(),this.rotation),function(t,o){const{pointX:n,pointY:r,options:i}=o,s=i.callout,a=s&&s.display&&function(t,o){const n=o.position;if(st.includes(n))return n;return function(t,o){const{x:n,y:r,x2:i,y2:s,width:a,height:d,pointX:l,pointY:c,centerX:h,centerY:u,rotation:f}=t,x={x:h,y:u},y=o.start,p=m(a,y),b=m(d,y),g=[n,n+p,n+p,i],w=[r+b,s,r,s],v=[];for(let t=0;t<4;t++){const o=E({x:g[t],y:w[t]},x,e.toRadians(f));v.push({position:st[t],distance:e.distanceBetweenPoints(o,{x:l,y:c})})}return v.sort(((t,e)=>t.distance-e.distance))[0].position}(t,o)}(o,s);if(!a||function(t,e,o){const{pointX:n,pointY:r}=t,i=e.margin;let s=n,a=r;"left"===o?s+=i:"right"===o?s-=i:"top"===o?a+=i:"bottom"===o&&(a-=i);return t.inRange(s,a)}(o,s,a))return;t.save(),t.beginPath();if(!D(t,s))return t.restore();const{separatorStart:d,separatorEnd:l}=function(t,e){const{x:o,y:n,x2:r,y2:i}=t,s=function(t,e){const{width:o,height:n,options:r}=t,i=r.callout.margin+r.borderWidth/2;if("right"===e)return o+i;if("bottom"===e)return n+i;return-i}(t,e);let a,d;"left"===e||"right"===e?(a={x:o+s,y:n},d={x:a.x,y:i}):(a={x:o,y:n+s},d={x:r,y:a.y});return{separatorStart:a,separatorEnd:d}}(o,a),{sideStart:c,sideEnd:h}=function(t,e,o){const{y:n,width:r,height:i,options:s}=t,a=s.callout.start,d=function(t,e){const o=e.side;if("left"===t||"top"===t)return-o;return o}(e,s.callout);let l,c;"left"===e||"right"===e?(l={x:o.x,y:n+m(i,a)},c={x:l.x+d,y:l.y}):(l={x:o.x+m(r,a),y:o.y},c={x:l.x,y:l.y+d});return{sideStart:l,sideEnd:c}}(o,a,d);(s.margin>0||0===i.borderWidth)&&(t.moveTo(d.x,d.y),t.lineTo(l.x,l.y));t.moveTo(c.x,c.y),t.lineTo(h.x,h.y);const u=E({x:n,y:r},o.getCenterPoint(),e.toRadians(-o.rotation));t.lineTo(u.x,u.y),t.stroke(),t.restore()}(t,this),P(t,this,o),function(t,o,n){const r=n.content;if(M(r))return void t.drawImage(r,o.x,o.y,o.width,o.height);const i=e.isArray(r)?r:[r],s=e.toFont(n.font),a=s.lineHeight,d=function(t,e){const{x:o,width:n}=t,r=e.textAlign;return"center"===r?o+n/2:"end"===r||"right"===r?o+n:o}(o,n),l=o.y+a/2+n.textStrokeWidth/2;t.save(),t.font=s.string,t.textBaseline="middle",t.textAlign=n.textAlign,function(t,e){if(e.textStrokeWidth>0)return t.lineJoin="round",t.miterLimit=2,t.lineWidth=e.textStrokeWidth,t.strokeStyle=e.textStrokeColor,!0}(t,n)&&i.forEach(((e,o)=>t.strokeText(e,d,l+o*a))),t.fillStyle=n.color,i.forEach(((e,o)=>t.fillText(e,d,l+o*a))),t.restore()}(t,function({x:t,y:o,width:n,height:r,options:i}){const s=i.borderWidth/2,a=e.toPadding(i.padding);return{x:t+a.left+s,y:o+a.top+s,width:n-a.left-a.right-i.borderWidth,height:r-a.top-a.bottom-i.borderWidth}}(this),o),t.restore())}resolveElementProperties(t,o){let n;if(v(o))n=Y(t,o);else{const{centerX:e,centerY:r}=R(t,o);n={x:e,y:r}}const r=e.toPadding(o.padding),i=function(t,e,o,n){const r=e.width+n.width+o.borderWidth,i=e.height+n.height+o.borderWidth,s=w(o.position),a=dt(t.x,r,o.xAdjust,s.x),d=dt(t.y,i,o.yAdjust,s.y);return{x:a,y:d,x2:a+r,y2:d+i,width:r,height:i,centerX:a+r/2,centerY:d+i/2}}(n,k(t.ctx,o),o,r);return{pointX:n.x,pointY:n.y,...i,rotation:o.rotation}}}function dt(t,e,o=0,n){return t-g(e,n)+o}at.id="labelAnnotation",at.defaults={adjustScaleRange:!0,backgroundColor:"transparent",backgroundShadowColor:"transparent",borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:0,borderShadowColor:"transparent",borderWidth:0,callout:{borderCapStyle:"butt",borderColor:void 0,borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:1,display:!1,margin:5,position:"auto",side:5,start:"50%"},color:"black",content:null,display:!0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:void 0},height:void 0,padding:6,position:"center",rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,textAlign:"center",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,xMax:void 0,xMin:void 0,xScaleID:void 0,xValue:void 0,yAdjust:0,yMax:void 0,yMin:void 0,yScaleID:void 0,yValue:void 0,z:0},at.defaultRoutes={borderColor:"color"};class lt extends t.Element{inRange(t,e,o,n){const{x:r,y:i,x2:s,y2:a,width:d}=this.getProps(["x","y","x2","y2","width"],n),l=this.options.borderWidth;if("x"!==o&&"y"!==o)return function(t,e,o,n){if(!t||!e||o<=0)return!1;const r=n/2;return Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)<=Math.pow(o+r,2)}({x:t,y:e},this.getCenterPoint(n),d/2,l);const c=l/2,h="y"===o?{start:i,end:a,value:e}:{start:r,end:s,value:t};return h.value>=h.start-c&&h.value<=h.end+c}getCenterPoint(t){return y(this,t)}draw(t){const o=this.options,n=o.borderWidth;if(o.radius<.1)return;t.save(),t.fillStyle=o.backgroundColor,j(t,o);const r=D(t,o);o.borderWidth=0,e.drawPoint(t,o,this.centerX,this.centerY),r&&!M(o.pointStyle)&&(t.shadowColor=o.borderShadowColor,t.stroke()),t.restore(),o.borderWidth=n}resolveElementProperties(t,e){return W(t,e)}}lt.id="pointAnnotation",lt.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:1,display:!0,pointStyle:"circle",radius:10,rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xAdjust:0,xMax:void 0,xMin:void 0,xScaleID:void 0,xValue:void 0,yAdjust:0,yMax:void 0,yMin:void 0,yScaleID:void 0,yValue:void 0,z:0},lt.defaultRoutes={borderColor:"color",backgroundColor:"color"};class ct extends t.Element{inRange(t,o,n,r){if("x"!==n&&"y"!==n)return this.options.radius>=.1&&this.elements.length>1&&function(t,e,o,n){let r=!1,i=t[t.length-1].getProps(["bX","bY"],n);for(const s of t){const t=s.getProps(["bX","bY"],n);t.bY>o!=i.bY>o&&e<(i.bX-t.bX)*(o-t.bY)/(i.bY-t.bY)+t.bX&&(r=!r),i=t}return r}(this.elements,t,o,r);const i=E({x:t,y:o},this.getCenterPoint(r),e.toRadians(-this.options.rotation)),s=this.elements.map((t=>"y"===n?t.bY:t.bX)),a=Math.min(...s),d=Math.max(...s);return i[n]>=a&&i[n]<=d}getCenterPoint(t){return y(this,t)}draw(t){const{elements:e,options:o}=this;t.save(),t.beginPath(),t.fillStyle=o.backgroundColor,j(t,o);const n=D(t,o);let r=!0;for(const o of e)r?(t.moveTo(o.x,o.y),r=!1):t.lineTo(o.x,o.y);t.closePath(),t.fill(),n&&(t.shadowColor=o.borderShadowColor,t.stroke()),t.restore()}resolveElementProperties(t,o){const n=W(t,o),{x:r,y:i}=n,{sides:s,rotation:a}=o,d=[],l=2*e.PI/s;let c=a*e.RAD_PER_DEG;for(let t=0;t<s;t++,c+=l)d.push(ht(n,o,c));return n.elements=d,n.initProperties={x:r,y:i},n}}function ht({centerX:t,centerY:e},{radius:o,borderWidth:n},r){const i=n/2,s=Math.sin(r),a=Math.cos(r),d={x:t+s*o,y:e-a*o};return{type:"point",optionScope:"point",properties:{x:d.x,y:d.y,centerX:d.x,centerY:d.y,bX:t+s*(o+i),bY:e-a*(o+i)}}}ct.id="polygonAnnotation",ct.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderShadowColor:"transparent",borderWidth:1,display:!0,point:{radius:0},radius:10,rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,sides:3,xAdjust:0,xMax:void 0,xMin:void 0,xScaleID:void 0,xValue:void 0,yAdjust:0,yMax:void 0,yMin:void 0,yScaleID:void 0,yValue:void 0,z:0},ct.defaultRoutes={borderColor:"color",backgroundColor:"color"};const ut={box:B,ellipse:it,label:at,line:U,point:lt,polygon:ct};Object.keys(ut).forEach((e=>{t.defaults.describe(`elements.${ut[e].id}`,{_fallback:"plugins.annotation.common"})}));const ft={update:Object.assign};function xt(t="line"){return ut[t]?t:(console.warn(`Unknown annotation type: '${t}', defaulting to 'line'`),"line")}function yt(o,n,r,i){const s=function(e,o,n){if("reset"===n||"none"===n||"resize"===n)return ft;return new t.Animations(e,o)}(o,r.animations,i),a=n.annotations,d=function(t,e){const o=e.length,n=t.length;if(n<o){const e=o-n;t.splice(n,0,...new Array(e))}else n>o&&t.splice(o,n-o);return t}(n.elements,a);for(let t=0;t<a.length;t++){const n=a[t],r=gt(d,t,n.type),i=n.setContext(vt(o,r,n)),l=r.resolveElementProperties(o,i);l.skip=pt(l),"elements"in l&&(bt(r,l,i,s),delete l.elements),e.defined(r.x)||Object.assign(r,l),l.options=mt(i),s.update(r,l)}}function pt(t){return isNaN(t.x)||isNaN(t.y)}function bt(t,{elements:e,initProperties:o},n,r){const i=t.elements||(t.elements=[]);i.length=e.length;for(let t=0;t<e.length;t++){const s=e[t],a=s.properties,d=gt(i,t,s.type,o),l=n[s.optionScope].override(s);a.options=mt(l),r.update(d,a)}}function gt(t,o,n,r){const i=ut[xt(n)];let s=t[o];return s&&s instanceof i||(s=t[o]=new i,e.isObject(r)&&Object.assign(s,r)),s}function mt(t){const e=ut[xt(t.type)],o={};o.id=t.id,o.type=t.type,o.drawTime=t.drawTime,Object.assign(o,wt(t,e.defaults),wt(t,e.defaultRoutes));for(const e of s)o[e]=t[e];return o}function wt(t,o){const n={};for(const r of Object.keys(o)){const i=o[r],s=t[r];n[r]=e.isObject(i)?wt(s,i):s}return n}function vt(t,e,o){return e.$context||(e.$context=Object.assign(Object.create(t.getContext()),{element:e,id:o.id,type:"annotation"}))}const St=new Map;var Mt={id:"annotation",version:"2.0.0",beforeRegister(){!function(t,e,o,n=!0){const r=o.split(".");let i=0;for(const s of e.split(".")){const a=r[i++];if(parseInt(s,10)<parseInt(a,10))break;if(c(a,s)){if(n)throw new Error(`${t} v${o} is not supported. v${e} or newer is required.`);return!1}}}("chart.js","3.7",t.Chart.version)},afterRegister(){t.Chart.register(ut)},afterUnregister(){t.Chart.unregister(ut)},beforeInit(t){St.set(t,{annotations:[],elements:[],visibleElements:[],listeners:{},listened:!1,moveListened:!1,hovered:[]})},beforeUpdate(t,o,n){const r=St.get(t).annotations=[];let i=n.annotations;e.isObject(i)?Object.keys(i).forEach((t=>{const o=i[t];e.isObject(o)&&(o.id=t,r.push(o))})):e.isArray(i)&&r.push(...i),function(t,e){for(const o of t)_(o,e)}(r,t.scales)},afterDataLimits(t,e){const o=St.get(t);T(t,e.scale,o.annotations.filter((t=>t.display&&t.adjustScaleRange)))},afterUpdate(t,o,r){const a=St.get(t);!function(t,o,r){o.listened=!1,o.moveListened=!1,o._getElements=n,s.forEach((t=>{"function"==typeof r[t]?(o.listened=!0,o.listeners[t]=r[t]):e.defined(o.listeners[t])&&delete o.listeners[t]})),i.forEach((t=>{"function"==typeof r[t]&&(o.moveListened=!0)})),o.listened&&o.moveListened||o.annotations.forEach((t=>{o.listened||"function"!=typeof t.click||(o.listened=!0),o.moveListened||i.forEach((e=>{"function"==typeof t[e]&&(o.listened=!0,o.moveListened=!0)}))}))}(0,a,r),yt(t,a,r,o.mode),a.visibleElements=a.elements.filter((t=>!t.skip&&t.options.display))},beforeDatasetsDraw(t,e,o){Ct(t,"beforeDatasetsDraw",o.clip)},afterDatasetsDraw(t,e,o){Ct(t,"afterDatasetsDraw",o.clip)},beforeDraw(t,e,o){Ct(t,"beforeDraw",o.clip)},afterDraw(t,e,o){Ct(t,"afterDraw",o.clip)},beforeEvent(t,e,o){a(St.get(t),e.event,o)&&(e.changed=!0)},destroy(t){St.delete(t)},_getState:t=>St.get(t),defaults:{animations:{numbers:{properties:["x","y","x2","y2","width","height","centerX","centerY","pointX","pointY","radius"],type:"number"}},clip:!0,interaction:{mode:void 0,axis:void 0,intersect:void 0},common:{drawTime:"afterDatasetsDraw",label:{}}},descriptors:{_indexable:!1,_scriptable:t=>!s.includes(t),annotations:{_allKeys:!1,_fallback:(t,e)=>`elements.${ut[xt(e.type)].id}`},interaction:{_fallback:!0},common:{label:{_fallback:!0}}},additionalOptionScopes:[""]};function Ct(t,o,n){const{ctx:r,chartArea:i}=t,{visibleElements:s}=St.get(t);n&&e.clipArea(r,i);const a=function(t,e){const o=[];for(const n of t)if(n.options.drawTime===e&&o.push(n),n.elements&&n.elements.length)for(const t of n.elements)t.options.display&&t.options.drawTime===e&&o.push(t);return o}(s,o).sort(((t,e)=>t.options.z-e.options.z));for(const e of a)e.draw(t.ctx,i);n&&e.unclipArea(r)}return t.Chart.register(Mt),Mt}));
@@ -0,0 +1,262 @@
1
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
2
+ Chart.defaults.borderColor = "#333"
3
+ Chart.defaults.color = "#aaa"
4
+ }
5
+
6
+ class BaseChart {
7
+ constructor(id, options) {
8
+ this.ctx = document.getElementById(id);
9
+ this.options = options
10
+ this.fallbackColor = "#999";
11
+ this.colors = [
12
+ // Colors taken from https://www.chartjs.org/docs/latest/samples/utils.html
13
+ "#537bc4",
14
+ "#4dc9f6",
15
+ "#f67019",
16
+ "#f53794",
17
+ "#acc236",
18
+ "#166a8f",
19
+ "#00a950",
20
+ "#58595b",
21
+ "#8549ba",
22
+ "#991b1b",
23
+ ];
24
+
25
+ this.chart = new Chart(this.ctx, {
26
+ type: this.options.chartType,
27
+ data: { labels: this.options.labels, datasets: this.datasets },
28
+ options: this.chartOptions,
29
+ });
30
+ }
31
+
32
+ addMarksToChart() {
33
+ this.options.marks.forEach(([bucket, label], i) => {
34
+ this.chart.options.plugins.annotation.annotations[`deploy-${i}`] = {
35
+ type: "line",
36
+ xMin: bucket,
37
+ xMax: bucket,
38
+ borderColor: "rgba(220, 38, 38, 0.4)",
39
+ borderWidth: 2,
40
+ };
41
+ });
42
+ }
43
+ }
44
+
45
+ class JobMetricsOverviewChart extends BaseChart {
46
+ constructor(id, options) {
47
+ super(id, { ...options, chartType: "line" });
48
+ this.swatches = [];
49
+
50
+ this.addMarksToChart();
51
+ this.chart.update();
52
+ }
53
+
54
+ registerSwatch(id) {
55
+ const el = document.getElementById(id);
56
+ el.onchange = () => this.toggle(el.value, el.checked);
57
+ this.swatches[el.value] = el;
58
+ this.updateSwatch(el.value);
59
+ }
60
+
61
+ updateSwatch(kls) {
62
+ const el = this.swatches[kls];
63
+ const ds = this.chart.data.datasets.find((ds) => ds.label == kls);
64
+ el.checked = !!ds;
65
+ el.style.color = ds ? ds.borderColor : null;
66
+ }
67
+
68
+ toggle(kls, visible) {
69
+ if (visible) {
70
+ this.chart.data.datasets.push(this.dataset(kls));
71
+ } else {
72
+ const i = this.chart.data.datasets.findIndex((ds) => ds.label == kls);
73
+ this.colors.unshift(this.chart.data.datasets[i].borderColor);
74
+ this.chart.data.datasets.splice(i, 1);
75
+ }
76
+
77
+ this.updateSwatch(kls);
78
+ this.chart.update();
79
+ }
80
+
81
+ dataset(kls) {
82
+ const color = this.colors.shift() || this.fallbackColor;
83
+
84
+ return {
85
+ label: kls,
86
+ data: this.options.series[kls],
87
+ borderColor: color,
88
+ backgroundColor: color,
89
+ borderWidth: 2,
90
+ pointRadius: 2,
91
+ };
92
+ }
93
+
94
+ get datasets() {
95
+ return Object.entries(this.options.series)
96
+ .filter(([kls, _]) => this.options.visible.includes(kls))
97
+ .map(([kls, _]) => this.dataset(kls));
98
+ }
99
+
100
+ get chartOptions() {
101
+ return {
102
+ aspectRatio: 4,
103
+ scales: {
104
+ y: {
105
+ beginAtZero: true,
106
+ title: {
107
+ text: "Total execution time (sec)",
108
+ display: true,
109
+ },
110
+ },
111
+ },
112
+ interaction: {
113
+ mode: "x",
114
+ },
115
+ plugins: {
116
+ legend: {
117
+ display: false,
118
+ },
119
+ tooltip: {
120
+ callbacks: {
121
+ title: (items) => `${items[0].label} UTC`,
122
+ label: (item) =>
123
+ `${item.dataset.label}: ${item.parsed.y.toFixed(1)} seconds`,
124
+ footer: (items) => {
125
+ const bucket = items[0].label;
126
+ const marks = this.options.marks.filter(([b, _]) => b == bucket);
127
+ return marks.map(([b, msg]) => `Deploy: ${msg}`);
128
+ },
129
+ },
130
+ },
131
+ },
132
+ };
133
+ }
134
+ }
135
+
136
+ class HistTotalsChart extends BaseChart {
137
+ constructor(id, options) {
138
+ super(id, { ...options, chartType: "bar" });
139
+ }
140
+
141
+ get datasets() {
142
+ return [{
143
+ data: this.options.series,
144
+ backgroundColor: this.colors[0],
145
+ borderWidth: 0,
146
+ }];
147
+ }
148
+
149
+ get chartOptions() {
150
+ return {
151
+ aspectRatio: 6,
152
+ scales: {
153
+ y: {
154
+ beginAtZero: true,
155
+ title: {
156
+ text: "Total jobs",
157
+ display: true,
158
+ },
159
+ },
160
+ },
161
+ interaction: {
162
+ mode: "x",
163
+ },
164
+ plugins: {
165
+ legend: {
166
+ display: false,
167
+ },
168
+ tooltip: {
169
+ callbacks: {
170
+ label: (item) => `${item.parsed.y} jobs`,
171
+ },
172
+ },
173
+ },
174
+ };
175
+ }
176
+ }
177
+
178
+ class HistBubbleChart extends BaseChart {
179
+ constructor(id, options) {
180
+ super(id, { ...options, chartType: "bubble" });
181
+
182
+ this.addMarksToChart();
183
+ this.chart.update();
184
+ }
185
+
186
+ get datasets() {
187
+ const data = [];
188
+ let maxCount = 0;
189
+
190
+ Object.entries(this.options.hist).forEach(([bucket, hist]) => {
191
+ hist.forEach((count, histBucket) => {
192
+ if (count > 0) {
193
+ data.push({
194
+ x: bucket,
195
+ // histogram data is ordered fastest to slowest, but this.histIntervals is
196
+ // slowest to fastest (so it displays correctly on the chart).
197
+ y:
198
+ this.options.histIntervals[this.options.histIntervals.length - 1 - histBucket] /
199
+ 1000,
200
+ count: count,
201
+ });
202
+
203
+ if (count > maxCount) maxCount = count;
204
+ }
205
+ });
206
+ });
207
+
208
+ // Chart.js will not calculate the bubble size. We have to do that.
209
+ const maxRadius = this.ctx.offsetWidth / this.options.labels.length;
210
+ const minRadius = 1
211
+ const multiplier = (maxRadius / maxCount) * 1.5;
212
+ data.forEach((entry) => {
213
+ entry.r = entry.count * multiplier + minRadius;
214
+ });
215
+
216
+ return [{
217
+ data: data,
218
+ backgroundColor: "#537bc4",
219
+ borderColor: "#537bc4",
220
+ }];
221
+ }
222
+
223
+ get chartOptions() {
224
+ return {
225
+ aspectRatio: 3,
226
+ scales: {
227
+ x: {
228
+ type: "category",
229
+ labels: this.options.labels,
230
+ },
231
+ y: {
232
+ title: {
233
+ text: "Execution time (sec)",
234
+ display: true,
235
+ },
236
+ },
237
+ },
238
+ interaction: {
239
+ mode: "x",
240
+ },
241
+ plugins: {
242
+ legend: {
243
+ display: false,
244
+ },
245
+ tooltip: {
246
+ callbacks: {
247
+ title: (items) => `${items[0].raw.x} UTC`,
248
+ label: (item) =>
249
+ `${item.parsed.y} seconds: ${item.raw.count} job${
250
+ item.raw.count == 1 ? "" : "s"
251
+ }`,
252
+ footer: (items) => {
253
+ const bucket = items[0].raw.x;
254
+ const marks = this.options.marks.filter(([b, _]) => b == bucket);
255
+ return marks.map(([b, msg]) => `Deploy: ${msg}`);
256
+ },
257
+ },
258
+ },
259
+ },
260
+ };
261
+ }
262
+ }
@@ -67,10 +67,15 @@ body {
67
67
  padding: 0 20px;
68
68
  }
69
69
 
70
- h3 {
70
+ h1, h2, h3 {
71
+ font-size: 24px;
71
72
  line-height: 45px;
72
73
  }
73
74
 
75
+ .header-with-subheader h2 {
76
+ margin-top: -18px;
77
+ }
78
+
74
79
  .centered {
75
80
  text-align: center;
76
81
  }
@@ -954,3 +959,41 @@ div.interval-slider input {
954
959
  padding: 3px 7px;
955
960
  margin-left: 5px;
956
961
  }
962
+
963
+ .metrics-swatch-wrapper {
964
+ display: flex;
965
+ align-items: center;
966
+ gap: 6px;
967
+ }
968
+
969
+ .metrics-swatch[type=checkbox] {
970
+ display: inline-block;
971
+ width: 16px;
972
+ height: 16px;
973
+ margin: 0;
974
+ border-radius: 2px;
975
+ appearance: none;
976
+ -webkit-appearance: none;
977
+ -moz-appearance: none;
978
+ border: 1px solid #bbb;
979
+ color: white;
980
+ background-color: currentColor;
981
+ }
982
+
983
+ /* We need to add the checkmark since we've taken over the appearance */
984
+ .metrics-swatch[type=checkbox]:checked {
985
+ border-color: currentColor;
986
+ background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
987
+ background-size: 100% 100%;
988
+ background-position: center;
989
+ background-repeat: no-repeat;
990
+ }
991
+
992
+ .metrics-swatch[type=checkbox]:focus {
993
+ outline: 1px solid #888;
994
+ outline-offset: 2px;
995
+ }
996
+
997
+ canvas {
998
+ margin: 20px 0 30px;
999
+ }
data/web/locales/el.yml CHANGED
@@ -6,11 +6,12 @@ el: # <---- change this to your locale code
6
6
  Namespace: Namespace
7
7
  Realtime: Τρέχουσα Κατάσταση
8
8
  History: Ιστορικό
9
- Busy: Απασχολημένο
10
- Processed: Επεξεργάστηκε
11
- Failed: Απέτυχε
12
- Scheduled: Προγραματίστηκε
13
- Retries: Προσπάθειες
9
+ Busy: Υπό επεξεργασία
10
+ Utilization: Σε χρήση
11
+ Processed: Επεξεργάστηκαν
12
+ Failed: Απέτυχαν
13
+ Scheduled: Προγραμματισμένα
14
+ Retries: Επαναλήψεις
14
15
  Enqueued: Μπήκαν στην στοίβα
15
16
  Worker: Εργάτης
16
17
  LivePoll: Τρέχουσα Κατάσταση
@@ -20,40 +21,42 @@ el: # <---- change this to your locale code
20
21
  Job: Εργασία
21
22
  Arguments: Ορίσματα
22
23
  Extras: Extras
23
- Started: Ξεκίνησαν
24
+ Started: Ξεκίνησε
24
25
  ShowAll: Εμφάνιση Όλων
25
26
  CurrentMessagesInQueue: Τρέχουσες εργασίες <span class='title'>%{queue}</span>
26
27
  Delete: Διαγραφή
27
28
  AddToQueue: Προσθήκη στην στοίβα
28
- AreYouSureDeleteJob: Θέλετε να διαγράψετε την εργασία αυτη;
29
- AreYouSureDeleteQueue: Θέλετε να διαγράψετε την %{queue} στοίβα?
29
+ AreYouSureDeleteJob: Θέλετε να διαγράψετε αυτή την εργασία;
30
+ AreYouSureDeleteQueue: Θέλετε να διαγράψετε την στοίβα %{queue}; Αυτό θα διαγράψει όλες τις εργασίες εντός της στοίβας, θα εμφανιστεί ξανά εάν προωθήσετε περισσότερες εργασίες σε αυτήν στο μέλλον.
30
31
  Queues: Στοίβες
31
32
  Size: Μέγεθος
32
33
  Actions: Ενέργειες
33
- NextRetry: Επόμενη προσπάθεια
34
- RetryCount: Αριθμός προσπαθειών
35
- RetryNow: Προσπάθησε τώρα
36
- LastRetry: Τελευταία προσπάθεια
34
+ NextRetry: Επόμενη Προσπάθεια
35
+ RetryCount: Αριθμός Προσπαθειών
36
+ RetryNow: Επανάληψη Τώρα
37
+ # Kill: Kill
38
+ LastRetry: Τελευταία Προσπάθεια
37
39
  OriginallyFailed: Αρχικές Αποτυχίες
38
- AreYouSure: Είστε σίγουρος?
39
- DeleteAll: Διαγραφή όλων
40
+ AreYouSure: Είστε σίγουρος;
41
+ DeleteAll: Διαγραφή Όλων
40
42
  RetryAll: Επανάληψη Όλων
41
- NoRetriesFound: Δεν βρέθηκαν προσπάθειες
43
+ # KillAll: Kill All
44
+ NoRetriesFound: Δεν βρέθηκαν εργασίες προς επαναλήψη
42
45
  Error: Σφάλμα
43
46
  ErrorClass: Κλάση σφάλματος
44
47
  ErrorMessage: Μήνυμα Σφάλματος
45
- ErrorBacktrace: Σφάλμα Backtrace
48
+ ErrorBacktrace: Backtrace Σφάλματος
46
49
  GoBack: ← Πίσω
47
50
  NoScheduledFound: Δεν βρέθηκαν προγραμματισμένες εργασίες
48
51
  When: Πότε
49
52
  ScheduledJobs: Προγραμματισμένες Εργασίες
50
- idle: αδρανής
51
- active: ενεργή
53
+ idle: αδρανές
54
+ active: ενεργό
52
55
  Version: Έκδοση
53
56
  Connections: Συνδέσεις
54
57
  MemoryUsage: Χρήση Μνήμης
55
58
  PeakMemoryUsage: Μέγιστη Χρήση Μνήμης
56
- Uptime: Διάρκεια Λειτουργείας (ημέρες)
59
+ Uptime: Ημέρες Λειτουργίας
57
60
  OneWeek: 1 εβδομάδα
58
61
  OneMonth: 1 μήνας
59
62
  ThreeMonths: 3 μήνες
@@ -62,7 +65,28 @@ el: # <---- change this to your locale code
62
65
  DeadJobs: Αδρανείς Εργασίες
63
66
  NoDeadJobsFound: Δεν βρέθηκαν αδρανείς εργασίες
64
67
  Dead: Αδρανείς
68
+ Process: Διεργασία
65
69
  Processes: Διεργασίες
70
+ Name: Όνομα
66
71
  Thread: Νήμα
67
72
  Threads: Νήματα
68
73
  Jobs: Εργασίες
74
+ Paused: Σε παύση
75
+ Stop: Διακοπή
76
+ Quiet: Σίγαση
77
+ StopAll: Διακοπή Όλων
78
+ QuietAll: Σίγαση Όλων
79
+ PollingInterval: Συχνότητα Ανανέωσης
80
+ Plugins: Πρόσθετα
81
+ NotYetEnqueued: Δεν προστέθηκε στην στοίβα ακόμη
82
+ CreatedAt: Δημιουργήθηκε στις
83
+ BackToApp: Πίσω στην Εφαρμογή
84
+ Latency: Καθυστέρηση
85
+ Pause: Παύση
86
+ Unpause: Κατάργηση Παύσης
87
+ Metrics: Μετρήσεις
88
+ NoDataFound: Δεν βρέθηκαν δεδομένα
89
+ ExecutionTime: Συνολικός Χρόνος Εκτέλεσης
90
+ AvgExecutionTime: Μέσος Χρόνος Εκτέλεσης
91
+ # Context: Context
92
+
data/web/locales/en.yml CHANGED
@@ -86,5 +86,8 @@ en: # <---- change this to your locale code
86
86
  Unpause: Unpause
87
87
  Metrics: Metrics
88
88
  NoDataFound: No data found
89
- ExecutionTime: Execution Time
89
+ ExecutionTime: Total Execution Time
90
+ AvgExecutionTime: Average Execution Time
90
91
  Context: Context
92
+ Bucket: Bucket
93
+ NoJobMetricsFound: No recent job metrics were found
@@ -1,59 +1,69 @@
1
+ <script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
2
+ <script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
3
+ <script type="text/javascript" src="<%= root_path %>javascripts/metrics.js"></script>
1
4
 
2
- <h1><%= t('Metrics') %></h1>
5
+ <h2>Total execution time</h2>
3
6
 
4
- <h3>Top Jobs by Processed Count</h3>
5
- <% top = @resultset[:top_classes] %>
7
+ <%
8
+ table_limit = 20
9
+ chart_limit = 5
10
+ job_results = @query_result.job_results.sort_by { |(kls, jr)| jr.totals["s"] }.reverse.first(table_limit)
11
+ visible_kls = job_results.first(chart_limit).map(&:first)
12
+ %>
6
13
 
7
- <% topp = top["p"]&.first(10) %>
8
- <div class="table_container">
9
- <table class="table table-bordered table-striped table-hover">
10
- <tbody>
11
- <tr>
12
- <th><%= t('Name') %></th>
13
- <th><%= t('Processed') %></th>
14
- <th><%= t('ExecutionTime') %></th>
15
- </tr>
16
- <% if topp %>
17
- <% topp.each do |kls, val| %>
18
- <tr>
19
- <td><code><a href="<%= root_path %>metrics/<%= kls %>"><%= kls %></a></code></td>
20
- <td><%= val %></td>
21
- <td><%= top.dig("ms", kls) %></td>
22
- </tr>
23
- <% end %>
24
- <% else %>
25
- <tr><td colspan=3><%= t("NoDataFound") %></td></tr>
26
- <% end %>
27
- </tbody>
28
- </table>
29
- </div>
14
+ <canvas id="job-metrics-overview-chart"></canvas>
15
+
16
+ <script>
17
+ window.jobMetricsChart = new JobMetricsOverviewChart(
18
+ "job-metrics-overview-chart",
19
+ <%= Sidekiq.dump_json({
20
+ series: job_results.map { |(kls, jr)| [kls, jr.dig("series", "s")] }.to_h,
21
+ marks: @query_result.marks.map { |m| [m.bucket, m.label] },
22
+ visible: visible_kls,
23
+ labels: @query_result.buckets,
24
+ }) %>
25
+ )
26
+ </script>
30
27
 
31
- <h3>Top Jobs by Execution Time</h3>
28
+ <h2>Most Time-Consuming Jobs</h2>
32
29
 
33
- <% topms = top["ms"]&.first(10) %>
34
30
  <div class="table_container">
35
31
  <table class="table table-bordered table-striped table-hover">
36
32
  <tbody>
37
33
  <tr>
38
34
  <th><%= t('Name') %></th>
39
35
  <th><%= t('Processed') %></th>
36
+ <th><%= t('Failed') %></th>
40
37
  <th><%= t('ExecutionTime') %></th>
38
+ <th><%= t('AvgExecutionTime') %></th>
41
39
  </tr>
42
- <% if topms %>
43
- <% topms.each do |kls, val| %>
40
+ <% if job_results.any? %>
41
+ <% job_results.each_with_index do |(kls, jr), i| %>
44
42
  <tr>
45
- <td><code><a href="<%= root_path %>metrics/<%= kls %>"><%= kls %></a></code></td>
46
- <td><%= top.dig("p", kls) %></td>
47
- <td><%= val %></td>
43
+ <td>
44
+ <div class="metrics-swatch-wrapper">
45
+ <% id = "metrics-swatch-#{kls}" %>
46
+ <input
47
+ type="checkbox"
48
+ id="<%= id %>"
49
+ class="metrics-swatch"
50
+ value="<%= kls %>"
51
+ />
52
+ <code><a href="<%= root_path %>metrics/<%= kls %>"><%= kls %></a></code>
53
+ </div>
54
+ <script>jobMetricsChart.registerSwatch("<%= id %>")</script>
55
+ </td>
56
+ <td><%= jr.dig("totals", "p") %></td>
57
+ <td><%= jr.dig("totals", "f") %></td>
58
+ <td><%= jr.dig("totals", "s").round(2) %> seconds</td>
59
+ <td><%= jr.total_avg("s").round(2) %> seconds</td>
48
60
  </tr>
49
61
  <% end %>
50
62
  <% else %>
51
- <tr><td colspan=3><%= t("NoDataFound") %></td></tr>
63
+ <tr><td colspan=5><%= t("NoDataFound") %></td></tr>
52
64
  <% end %>
53
65
  </tbody>
54
66
  </table>
55
67
  </div>
56
68
 
57
- <p>
58
- Data from <%= @resultset[:starts_at] %> to <%= @resultset[:ends_at] %>
59
- </p>
69
+ <p><small>Data from <%= @query_result.starts_at %> to <%= @query_result.ends_at %></small></p>