Mercurial > sci
changeset 41:00b9af607524
scid: implement /jobresults/id page
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 09 Aug 2022 14:52:34 +0200 |
parents | 752bb1cd2dd8 |
children | 4076b07c7a6f |
files | Makefile examples/irccd.sh scid/db.h scid/http.c scid/page-jobresults.c scid/page-jobresults.d scid/page-jobresults.h scid/page-jobresults.o scid/theme.c scid/theme.h themes/bulma/header.mustache themes/bulma/index.mustache themes/bulma/jobresults.mustache themes/bulma/static/scid.css themes/bulma/theme.js |
diffstat | 15 files changed, 292 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sun Aug 07 08:43:32 2022 +0200 +++ b/Makefile Tue Aug 09 14:52:34 2022 +0200 @@ -84,6 +84,7 @@ scid/page-api-todo.c \ scid/page-api-workers.c \ scid/page-index.c \ + scid/page-jobresults.c \ scid/page-static.c \ scid/pageutil.c \ scid/scid.c \
--- a/examples/irccd.sh Sun Aug 07 08:43:32 2022 +0200 +++ b/examples/irccd.sh Tue Aug 09 14:52:34 2022 +0200 @@ -23,7 +23,7 @@ ;; esac -echo "=> Cloning repository $repo (revision $1) into $wkrdir" +echo "=> Cloning repository $repo (revision $1) into $wrkdir" hg clone -r "$1" "$repo" "$wrkdir" cd "$wrkdir"
--- a/scid/db.h Sun Aug 07 08:43:32 2022 +0200 +++ b/scid/db.h Tue Aug 09 14:52:34 2022 +0200 @@ -113,7 +113,7 @@ * \return the JSON model or NULL on failure */ json_t * -db_jobresult_list_by_job_group(intmax_t); +db_jobresult_list_by_job_group(intmax_t job_id); /** * Get a list of job results realized by this worker.
--- a/scid/http.c Sun Aug 07 08:43:32 2022 +0200 +++ b/scid/http.c Tue Aug 09 14:52:34 2022 +0200 @@ -34,14 +34,16 @@ #include "page-api-todo.h" #include "page-api-workers.h" #include "page-index.h" +#include "page-jobresults.h" #include "page-static.h" #include "pageutil.h" enum page { - PAGE_INDEX, /* Job results at index. */ + PAGE_INDEX, /* Job results at index. */ + PAGE_JOBRESULTS, /* List of jobresult for one job. */ PAGE_API, PAGE_STATIC, - PAGE_LAST /* Not used. */ + PAGE_LAST /* Not used. */ }; static void @@ -68,12 +70,14 @@ static const char *pages[] = { [PAGE_INDEX] = "", + [PAGE_JOBRESULTS] = "jobresults", [PAGE_API] = "api", [PAGE_STATIC] = "static" }; static void (*handlers[])(struct kreq *req) = { [PAGE_INDEX] = page_index, + [PAGE_JOBRESULTS] = page_jobresults, [PAGE_API] = dispatch_api, [PAGE_STATIC] = page_static };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scid/page-jobresults.c Tue Aug 09 14:52:34 2022 +0200 @@ -0,0 +1,72 @@ +/* + * page-jobresults.c -- page /jobresults/<id> route + * + * Copyright (c) 2021-2022 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <stdio.h> + +#include "db.h" +#include "page-jobresults.h" +#include "pageutil.h" +#include "theme.h" +#include "util.h" + +static void +list(struct kreq *r, intmax_t id) +{ + json_t *results, *doc; + char *data; + + if (!(results = db_jobresult_list_by_job(id))) + pageutil_status(r, KHTTP_404); + else { + doc = util_json_pack("{sI so}", + "job_id", (json_int_t)id, + "jobresults", results + ); + data = theme_page_jobresults(doc); + pageutil_render(r, KHTTP_200, KMIME_TEXT_HTML, data); + json_decref(doc); + free(data); + } +} + +static void +get(struct kreq *r) +{ + intmax_t id; + + if (sscanf(r->fullpath, "/jobresults/%jd", &id) != 1) + pageutil_status(r, KHTTP_400); + else + list(r, id); +} + +void +page_jobresults(struct kreq *r) +{ + (void)r; + + switch (r->method) { + case KMETHOD_GET: + get(r); + break; + default: + pageutil_status(r, KHTTP_400); + break; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scid/page-jobresults.d Tue Aug 09 14:52:34 2022 +0200 @@ -0,0 +1,6 @@ +scid/page-jobresults.o: scid/page-jobresults.c scid/db.h \ + /usr/local/Cellar/jansson/2.14/include/jansson.h \ + /usr/local/Cellar/jansson/2.14/include/jansson_config.h \ + scid/page-jobresults.h scid/pageutil.h \ + /usr/local/Cellar/kcgi/0.13.0/include/kcgi.h scid/theme.h \ + libsci/util.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scid/page-jobresults.h Tue Aug 09 14:52:34 2022 +0200 @@ -0,0 +1,40 @@ +/* + * page-jobresults.h -- page /jobresults/<id> route + * + * Copyright (c) 2021-2022 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SCI_PAGE_JOBRESULTS_H +#define SCI_PAGE_JOBRESULTS_H + +/** + * \file page-jobresults.h + * \brief Page /jobresults/id route. + * + * This module does not log messages. + */ + +struct kreq; + +/** + * Run the page. + * + * \pre r != NULL + * \param r the request + */ +void +page_jobresults(struct kreq *r); + +#endif /* !SCI_PAGE_JOBRESULTS_H */
--- a/scid/theme.c Sun Aug 07 08:43:32 2022 +0200 +++ b/scid/theme.c Tue Aug 09 14:52:34 2022 +0200 @@ -360,6 +360,14 @@ } char * +theme_page_jobresults(const json_t *json) +{ + assert(json); + + return call(json, "onPageJobresults"); +} + +char * theme_page_status(int status, const char *message) { json_t *doc;
--- a/scid/theme.h Sun Aug 07 08:43:32 2022 +0200 +++ b/scid/theme.h Tue Aug 09 14:52:34 2022 +0200 @@ -99,6 +99,35 @@ theme_page_index(const json_t *doc); /** + * Render the jobresults page. + * + * The document requires the following properties: + * + * ``` + * { + * "jobresults": [ + * { + * "id": jobresult id, + * "job_id": parent job id, + * "worker_name": "worker which realized the task", + * "console": "stdout and stderr merge", + * "exitcode": process exit code, + * "sigcode": process termination code (0 means success), + * "date": job result insertion date + * } + * ] + * } + * ``` + * + * \pre doc != NULL + * \param doc the page document + * \return a newly allocated rendered string + * \note You must free the return value + */ +char * +theme_page_jobresults(const json_t *doc); + +/** * Render the status page (for error code). * * The document requires the following properties:
--- a/themes/bulma/header.mustache Sun Aug 07 08:43:32 2022 +0200 +++ b/themes/bulma/header.mustache Tue Aug 09 14:52:34 2022 +0200 @@ -5,7 +5,22 @@ <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>{{title}}</title> <link rel="stylesheet" href="/static/bulma.min.css" /> + <link rel="stylesheet" href="/static/scid.css" /> </head> <body> + <nav class="navbar" role="navigation" aria-label="main navigation"> + <div class="navbar-brand"> + <a class="navbar-item" href="/"><strong>sci</strong></a> + </div> + + <div class="navbar-menu"> + <div class="navbar-start"> + <a class="navbar-item">projects</a> + <a class="navbar-item">workers</a> + </div> + </div> + </div> + </nav> + <section class="section"> <div class="container">
--- a/themes/bulma/index.mustache Sun Aug 07 08:43:32 2022 +0200 +++ b/themes/bulma/index.mustache Tue Aug 09 14:52:34 2022 +0200 @@ -12,7 +12,7 @@ <div class="content"> {{#jobs}} - <table class="table"> + <table class="table jobresult-table"> <thead> <tr> <th>job</th> @@ -22,7 +22,7 @@ </thead> {{#jobs}} <tr> - <td><a href="jobs/{{job}}">{{id}}</a></td> + <td><a href="jobresults/{{id}}">{{id}}</a></td> <td>{{tag}}</td> <td><span class="tag {{classname}} is-light">{{status}}</span></td> </tr>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/bulma/jobresults.mustache Tue Aug 09 14:52:34 2022 +0200 @@ -0,0 +1,85 @@ + <h1 class="title">Results for job {{job_id}}</h1> + <table class="table"> + <thead> + <tr> + <th>Worker</th> + <th>Exit code</th> + <th>Termination code</th> + <th>Status</th> + <th>View log</th> + </tr> + </thead> + <tbody> + {{#jobresults}} + <tr> + <td><a href="/workers/{{worker_name}}">{{worker_name}}</a></td> + <td>{{exitcode}}</td> + <td>{{sigcode}}</td> + <td class="{{classname}}">{{status}}</td> + <td><button class="button is-light is-info is-small js-modal-trigger" data-target="console-{{job_id}}">view log</button></td> + </tr> + {{/jobresults}} + </tbody> + </table> + + <!-- Modal dialogs for every console output. --> +{{#jobresults}} + <div id="console-{{job_id}}" class="modal"> + <div class="modal-background"></div> + + <div class="modal-content console"> + <div class="box"> + <pre>{{console}}</pre> + </div> + </div> + + <button class="modal-close is-large" aria-label="close"></button> + </div> +{{/jobresults}} + + <script type="text/javascript"> + document.addEventListener('DOMContentLoaded', () => { + // Functions to open and close a modal + function openModal($el) { + $el.classList.add('is-active'); + } + + function closeModal($el) { + $el.classList.remove('is-active'); + } + + function closeAllModals() { + (document.querySelectorAll('.modal') || []).forEach(($modal) => { + closeModal($modal); + }); + } + + // Add a click event on buttons to open a specific modal + (document.querySelectorAll('.js-modal-trigger') || []).forEach(($trigger) => { + const modal = $trigger.dataset.target; + const $target = document.getElementById(modal); + + $trigger.addEventListener('click', () => { + openModal($target); + }); + }); + + // Add a click event on various child elements to close the parent modal + (document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => { + const $target = $close.closest('.modal'); + + $close.addEventListener('click', () => { + closeModal($target); + }); + }); + + // Add a keyboard event to close all modals + document.addEventListener('keydown', (event) => { + const e = event || window.event; + + if (e.keyCode === 27) { // Escape key + closeAllModals(); + } + }); + }); + </script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/themes/bulma/static/scid.css Tue Aug 09 14:52:34 2022 +0200 @@ -0,0 +1,8 @@ +.jobresult-table { + text-align: left; +} + +/* Console log jobresults page. */ +.console { + width: 80%; +}
--- a/themes/bulma/theme.js Sun Aug 07 08:43:32 2022 +0200 +++ b/themes/bulma/theme.js Tue Aug 09 14:52:34 2022 +0200 @@ -44,8 +44,25 @@ render(rdr, "index.mustache", "sci -- index page", data); } +function onPageJobresults(rdr, data) +{ + /* + * Add a status on failed/successful tasks. + */ + for (var i = 0; i < data.jobresults.length; ++i) { + if (data.jobresults[i].exitcode !== 0 || data.jobresults[i].sigcode !== 0) { + data.jobresults[i].status = "failed"; + data.jobresults[i].classname = "has-text-danger"; + } else { + data.jobresults[i].status = "passed"; + data.jobresults[i].classname = "has-text-success"; + } + } + + render(rdr, "jobresults.mustache", "sci -- job results", data); +} + function onPageStatus(rdr, data) { - Scid.print(JSON.stringify(data)); render(rdr, "status.mustache", "sci -- " + data.status, data); }