From 28a1c386c4c07005bc757b4bab87619e72a2c567 Mon Sep 17 00:00:00 2001 From: Jeroen De Meerleer Date: Mon, 2 Aug 2021 17:07:41 +0200 Subject: [PATCH] NEW FEATURE: added visual warnings for failed jobs --- .env.sample | 3 +++ assets/fonts/icomoon.svg | 1 + assets/fonts/icomoon.ttf | Bin 1676 -> 1868 bytes assets/fonts/icomoon.woff | Bin 1752 -> 1944 bytes assets/fonts/selection.json | 2 +- assets/js/job/add.js | 7 ++++++ assets/scss/icons.scss | 41 +++++++++++++++++++++++----------- assets/scss/job/index.scss | 8 +++++++ assets/scss/vars.scss | 8 +++++++ lib/Framework/Twig.php | 8 +++++-- src/Repository/Job.php | 18 +++++++++++++++ src/Repository/Run.php | 2 +- templates/job/add.html.twig | 11 +++++++++ templates/job/edit.html.twig | 11 +++++++++ templates/job/index.html.twig | 6 ++++- 15 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 assets/scss/vars.scss diff --git a/.env.sample b/.env.sample index f226956..4a120f8 100644 --- a/.env.sample +++ b/.env.sample @@ -31,6 +31,9 @@ HASHING_METHOD="sha256" ### ENVIRONMENT ### ################### +## Debug mode is solely for development purposes only. It disables caching and enables your webcam to stream it to the web +DEBUG=false + ## Cookies are used for saving autologin credentials. This sets the amount of time in seconds the credentials are saved. ## Grandma probably has a calculator somewhere if you need to check how many seconds are in a week :) COOKIE_LIFETIME=2592000 diff --git a/assets/fonts/icomoon.svg b/assets/fonts/icomoon.svg index 7c3b0b5..20a10fe 100644 --- a/assets/fonts/icomoon.svg +++ b/assets/fonts/icomoon.svg @@ -10,5 +10,6 @@ + \ No newline at end of file diff --git a/assets/fonts/icomoon.ttf b/assets/fonts/icomoon.ttf index ac7222407b7bb5663cdfae47e87bf0980482c50b..1740fea599fe21d02db136d5031c3b2ba81d2e4c 100644 GIT binary patch delta 567 zcmZXQKTH#G6vyBD{W_O zA$STLh9;-(rSzQsn4AyvWpf3u^mG67F#vWEmlwRX5^03^fK*>t-k3iXr&;3X0DL+> z=gr=Dvfl$Be9F6dGWZ$(0FZZx6Zt}UJ#+$+Sly9WUdcW1ScEmnP(QW{-g*fvm?hpO z?k#$Sxfc^ggZLF)vQt`FD^sJi+6Gh}IKDjmxcP9J;5zlifzdp@KJ&-@NB-)?@8C}m zsN?^67@S9#^~HK`JqxQktgdR&+AhAu3Rewc1MAd@1ETs6Iz%3>O!%(xR+q!O=Itd;brfJk%u3@5Q@vt&ujsAiV zYmIz%_qD5nD-G&uq-s`lM;r7;|1P6?AiCmICh_QR#snK~!VLBWS|vv9tN4VM D@U3$6 delta 374 zcmX@Z*TY-Sz{tSBz|GLWz|3IaAFOZ0k<4KM6xjpB3CX#M1&50V! zKw1FE?*P&q={c2YZ(rxQ0QnOb7?_r1q$Z|FhsujGFtC^a<;^mH0?d0EUjq3#K)y;w zZb=2_CLoJ}1*o1uAtyiCF_FoHF$gHI0Vp7pn^;l6z{ijRR{a5z<7{x^H*kOMiu6d9D(utHeVUInZWw)>gGFx7!dmZZN}S- v+b7Gg$_n2GT7wEWCi}3;$T3cWigGcCFvu}vG3;Wv0`eYMImhNM)d=BqdVi{fIyO{ zKr+^ofWb%2-5GSslhZ|i82}3uSX219{ptCfzpA)H^7SU{xy8p1i8}${Qwj&-;C^A& zp8=4gl#^GuxW3(8Ad)z>SDyku!XJg=+6Hlz|6Fn(C951~ryn z$nS|^!A!|7&Jq_=^JfY#Cye^?(keAb(5B5iP8P>k!3BJBgN_I7U;_t6bN7TSycymK zSAPA8{zehSsOB7p(Xq{}&(=HZMXMcrz>exy+{ZEpNe$~*0}DK$Df@F-ZvcC>E=;-C zF_TY^WWDqdDk{;69i69ajyrCO{Hs@by|kAdamH{s<91@dCh|gy7nI0QurVe`ODS?f z6E!Jio7tE!+sLSqnIY?LUQk$-m7XF;WDHT@788PDvQg4gx^9{}Dcm(o)2ME84g0$1 zcgpvy(ch3{@wi9V-l*q+QytaRNWq@ya_V$HXT=B(#icTXCFeI%zkEMMb48e@8i delta 432 zcmbQie}h-7+~3WOfsp|S*lsXzgXtay#>tKBViR?Q>n)OV6AKs^7&CwpIUxL~;h}VT zVlhbU3XsnM#RBO$m1#h+I}8j=OF;PT>l~Mi)Wj4929^e(8Z!`<4wVEy_;@s`F#{0~8PfVI~vCpxnd?pv9~LAVpxz$B>ej zn41a|GXPrN2EsGFIDZ%97ncD21F{67jv2^eX6%~G!&q$_0AvdQAxMmoLE!__2Zs;d zAAkJ+`~N>s1gHZ=gz^7l0Ve*V{JZ)1fjSsBpJ6=6xY?0~nNfxLBS&C7zs*+$ZYHq4 zySn+#AO?i~f1B|($7vWi%KkK}ES3L>S~4vKV$TTwx3Xih-1K UFeEXsg5{ZjaUs0 { initHostType(); initContainerType(); initVarInputs(); + initRangeInput(); initIntervalPattern(); initEternalCheckbox(); } @@ -126,6 +127,12 @@ function initContainerType() }) } +function initRangeInput() { + document.querySelector('.range-input-errorlevel').addEventListener('input', event => { + document.querySelector('.range-value-errorlevel').innerHTML = event.target.value + '%'; + }) +} + function initVarInputs() { document.querySelector('.addvar-btn').addEventListener('click', event => { diff --git a/assets/scss/icons.scss b/assets/scss/icons.scss index 9b254d7..da1e2f4 100644 --- a/assets/scss/icons.scss +++ b/assets/scss/icons.scss @@ -1,9 +1,11 @@ +@import "vars"; + @font-face { - font-family: 'icomoon'; + font-family: '#{$icomoon-font-family}'; src: - url('../fonts/icomoon.ttf?ov6ddj') format('truetype'), - url('../fonts/icomoon.woff?ov6ddj') format('woff'), - url('../fonts/icomoon.svg?ov6ddj#icomoon') format('svg'); + url('#{$icomoon-font-path}/#{$icomoon-font-family}.ttf?ef3few') format('truetype'), + url('#{$icomoon-font-path}/#{$icomoon-font-family}.woff?ef3few') format('woff'), + url('#{$icomoon-font-path}/#{$icomoon-font-family}.svg?ef3few##{$icomoon-font-family}') format('svg'); font-weight: normal; font-style: normal; font-display: block; @@ -11,7 +13,7 @@ [class^="icon-"], [class*=" icon-"] { /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'icomoon' !important; + font-family: '#{$icomoon-font-family}' !important; speak: never; font-style: normal; font-weight: normal; @@ -24,15 +26,28 @@ -moz-osx-font-smoothing: grayscale; } -.icon-view:before { - content: "\f002"; +.icon-view { + &:before { + content: $icon-view; + } } -.icon-edit:before { - content: "\f040"; +.icon-edit { + &:before { + content: $icon-edit; + } } -.icon-run:before { - content: "\f04b"; +.icon-run { + &:before { + content: $icon-run; + } } -.icon-delete:before { - content: "\f1f8"; +.icon-warning { + &:before { + content: $icon-warning; + } +} +.icon-delete { + &:before { + content: $icon-delete; + } } diff --git a/assets/scss/job/index.scss b/assets/scss/job/index.scss index 16fe3fe..015c816 100644 --- a/assets/scss/job/index.scss +++ b/assets/scss/job/index.scss @@ -9,9 +9,17 @@ tr.running td { background-color: #d4edda; } +.big-icon { + font-size: 1.5rem; +} + +td.status-col { + width: 54px; +} .blur { filter: blur(3px); } + .runnow-overlay { .runnow-blur { bottom: 0; diff --git a/assets/scss/vars.scss b/assets/scss/vars.scss new file mode 100644 index 0000000..1dd5b4c --- /dev/null +++ b/assets/scss/vars.scss @@ -0,0 +1,8 @@ +$icomoon-font-family: "icomoon" !default; +$icomoon-font-path: "../fonts" !default; + +$icon-view: "\f002"; +$icon-edit: "\f040"; +$icon-run: "\f04b"; +$icon-warning: "\f071"; +$icon-delete: "\f1f8"; diff --git a/lib/Framework/Twig.php b/lib/Framework/Twig.php index 28d6984..f4256ef 100644 --- a/lib/Framework/Twig.php +++ b/lib/Framework/Twig.php @@ -21,9 +21,13 @@ class Twig public function __construct(Kernel $kernel) { $loader = new FilesystemLoader([$kernel->getTemplateDir()]); - $cache = new FilesystemCache($kernel->getCacheDir() . '/twig'); $this->environment = new Environment($loader); - $this->environment->setCache($cache); + + if(!$_ENV['DEBUG']) { + $cache = new FilesystemCache($kernel->getCacheDir() . '/twig'); + $this->environment->setCache($cache); + } + $this->kernel = $kernel; $this->addExtensions(); $this->addFunctions(); diff --git a/src/Repository/Job.php b/src/Repository/Job.php index 73e8934..a25eb78 100644 --- a/src/Repository/Job.php +++ b/src/Repository/Job.php @@ -14,6 +14,8 @@ class Job extends Repository { public function getAllJobs(bool $idiskey = false) { + $runRepo = new Run($this->dbcon); + $jobsSql = "SELECT * FROM job"; $jobsStmt = $this->dbcon->prepare($jobsSql); $jobsRslt = $jobsStmt->executeQuery(); @@ -26,6 +28,9 @@ class Job extends Repository $job['service'] = $job['data']['service'] ?? ''; $job['norun'] = isset($job['lastrun']) && $job['nextrun'] > $job['lastrun']; $job['running'] = $job['running'] != 0; + $failed = count($runRepo->getRunsForJob($job['id'], true)); + $all = count($runRepo->getRunsForJob($job['id'])); + $job['needschecking'] = $all > 0 && (($failed / $all) * 100) > $job['data']['errorlevel']; if(!empty($job['data']['containertype']) && $job['data']['containertype'] != 'none') { $job['host-displayname'] = $job['data']['service'] . ' on ' . $job['data']['host']; } @@ -41,6 +46,18 @@ class Job extends Repository return $jobs; } + public function getErrorRatio(int $jobId): bool + { + $errorSql = "SELECT count(*) as count FROM job WHERE id = :id"; + $errorStmt = $this->dbcon->prepare($errorSql); + $errorRslt = $errorStmt->executeQuery([':timestamp' => time(), ':timestamplastrun' => time(), ':timestamprun' => time()]); + $error = $errorRslt->fetchAllAssociative(); + + $errorSql = "SELECT count(*) as count FROM job WHERE id = :id"; + $errorStmt = $this->dbcon->prepare($errorSql); + $errorRslt = $errorStmt->executeQuery([':timestamp' => time(), ':timestamplastrun' => time(), ':timestamprun' => time()]); + $error = $errorRslt->fetchAllAssociative(); + } public function getJobsDue() { @@ -389,6 +406,7 @@ class Job extends Repository $values['data']['crontype'] = $values['crontype']; $values['data']['hosttype'] = $values['hosttype']; $values['data']['containertype'] = $values['containertype']; + $values['data']['errorlevel'] = $values['errorlevel']; if(empty($values['data']['crontype'])) { throw new \InvalidArgumentException("Crontype cannot be empty"); diff --git a/src/Repository/Run.php b/src/Repository/Run.php index d620256..b085c96 100644 --- a/src/Repository/Run.php +++ b/src/Repository/Run.php @@ -13,7 +13,7 @@ class Run extends Repository const SUCCESS = 'S'; const MANUAL = 'M'; - public function getRunsForJob(int $id, $onlyfailed = false, $ordered = true): array + public function getRunsForJob(int $id, bool $onlyfailed = false, bool $ordered = true): array { $runsSql = "SELECT * FROM run WHERE job_id = :job"; $params = [':job' => $id]; diff --git a/templates/job/add.html.twig b/templates/job/add.html.twig index 2ee342b..1026151 100644 --- a/templates/job/add.html.twig +++ b/templates/job/add.html.twig @@ -50,6 +50,17 @@ How many days (at least) to keep runs of this job in the database +
+ +
+
50%
+
+ +
+
+ Max percentage of errors over all runs +
+

Job details

+
+ +
+
{% if attribute(data, 'errorlevel') is not empty %}{{ attribute(data, 'errorlevel') }}{% else %}50{% endif %}%
+
+ +
+
+ Max percentage of errors over all runs +
+

Job details