sidekiq 6.5.4 → 6.5.6
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.
- checksums.yaml +4 -4
- data/Changes.md +12 -0
- data/lib/sidekiq/api.rb +2 -2
- data/lib/sidekiq/client.rb +1 -1
- data/lib/sidekiq/fetch.rb +2 -2
- data/lib/sidekiq/job_retry.rb +1 -0
- data/lib/sidekiq/launcher.rb +2 -2
- data/lib/sidekiq/metrics/deploy.rb +2 -2
- data/lib/sidekiq/metrics/query.rb +102 -73
- data/lib/sidekiq/redis_connection.rb +0 -2
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +7 -7
- data/lib/sidekiq/web.rb +1 -1
- data/sidekiq.gemspec +2 -2
- data/web/assets/javascripts/chart.min.js +13 -0
- data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/web/assets/javascripts/metrics.js +262 -0
- data/web/assets/stylesheets/application.css +44 -1
- data/web/locales/el.yml +43 -19
- data/web/locales/en.yml +4 -1
- data/web/views/metrics.erb +47 -37
- data/web/views/metrics_for_job.erb +77 -82
- metadata +13 -4
@@ -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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
43
|
+
# KillAll: Kill All
|
44
|
+
NoRetriesFound: Δεν βρέθηκαν εργασίες προς επαναλήψη
|
42
45
|
Error: Σφάλμα
|
43
46
|
ErrorClass: Κλάση σφάλματος
|
44
47
|
ErrorMessage: Μήνυμα Σφάλματος
|
45
|
-
ErrorBacktrace:
|
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
|
data/web/views/metrics.erb
CHANGED
@@ -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
|
-
<
|
5
|
+
<h2>Total execution time</h2>
|
3
6
|
|
4
|
-
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
<
|
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
|
43
|
-
<%
|
40
|
+
<% if job_results.any? %>
|
41
|
+
<% job_results.each_with_index do |(kls, jr), i| %>
|
44
42
|
<tr>
|
45
|
-
<td
|
46
|
-
|
47
|
-
|
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=
|
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>
|