Clément Lassieur <clem...@lassieur.org> ezt írta (időpont: 2018. júl. 19.,
Cs, 23:23):

> Hello Tatiana,
>
> While your commits aren't pushed to master, they can be modified as much
> as you want.  It's good to take advantage of this to make them very
> consistent.  For example, they shouldn't correct one another.  The
> naming, also, should describe what they do.  It would be inconvenient to
> add the huge static files in the commits where you do the actual work,
> so maybe we can set them appart.  It's also good that after each commit,
> Cuirass is still in a working and consistent state.
>
> What do you think of:
>
>   1. One commit to add all the static files.
>   2. One commit to add the whole web interface.
>
> I'm not sure about it, though.  Usually it's better if the commits are
> short, but it's also difficult to split the work into small parts
> consistenly.  Don't hesitate to tell me if you find a better alternative
> :-).
>
>
What I usually do here is that I work on two branches, on one branch I keep
small separate commits to aid development, and on the other, which is the
one I actually push
I squash the commits belonging together. Also I usually have my branch with
small commits in a repo, so
it is easier to review.

I'll be reviewing the "second commit": everything except the static
> files.  But just one note about the static files: they need to be
> declared in the Makefile, so that they are copied to their right
> location.  That would look like:
>
> --8<---------------cut here---------------start------------->8---
> diff --git a/Makefile.am b/Makefile.am
> index f519f51..549713a 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -34,6 +34,10 @@ pkgobjectdir = $(guileobjectdir)/$(PACKAGE)
>  webmoduledir = $(guilesitedir)/web/server
>  webobjectdir = $(guileobjectdir)/web/server
>  sqldir = $(pkgdatadir)/sql
> +staticdir = $(pkgdatadir)/static
> +cssdir = $(staticdir)/css
> +fontsdir = $(staticdir)/fonts
> +imagesdir = $(staticdir)/images
>
>  dist_pkgmodule_DATA =                          \
>    src/cuirass/base.scm                         \
> @@ -62,6 +66,18 @@ dist_pkgdata_DATA = src/schema.sql
>  dist_sql_DATA =                                \
>    src/sql/upgrade-1.sql
>
> +dist_css_DATA =                                        \
> +  src/static/css/bootstrap.css                 \
> +  src/static/css/open-iconic-bootstrap.css
> +dist_fonts_DATA =                              \
> +  src/static/fonts/open-iconic.eot             \
> +  src/static/fonts/open-iconic.otf             \
> +  src/static/fonts/open-iconic.svg             \
> +  src/static/fonts/open-iconic.ttf             \
> +  src/static/fonts/open-iconic.woff
> +dist_images_DATA =                             \
> +  src/static/images/logo.png
> +
>  TEST_EXTENSIONS = .scm .sh
>  AM_TESTS_ENVIRONMENT = \
>    env GUILE_AUTO_COMPILE='0' \
> --8<---------------cut here---------------end--------------->8---
>
> Could you add a copyright header at the beginning of database.scm?
>
> >    ;; XXX Change caller and remove
> >    (define (assqx-ref filters key)
> > @@ -533,17 +539,27 @@ Assumes that if group id stays the same the group
> headers stay the same."
> >           (collect-outputs x-builds-id x-repeated-row '() rows)))))
> >
> >    (let* ((order (match (assq 'order filters)
> > -                  (('order 'build-id) "Builds.id ASC")
> > -                  (('order 'decreasing-build-id) "Builds.id DESC")
> > -                  (('order 'finish-time) "Builds.stoptime DESC")
> > -                  (('order 'start-time) "Builds.starttime DESC")
> > -                  (('order 'submission-time) "Builds.timestamp DESC")
> > +                  ;(('order 'build-id) "Builds.id ASC")
> > +                  ;(('order 'decreasing-build-id) "Builds.id DESC")
> > +                  ;(('order 'finish-time) "Builds.stoptime DESC")
> > +                  ;(('order 'start-time) "Builds.starttime DESC")
> > +                  ;(('order 'submission-time) "Builds.timestamp DESC")
> > +                  ;(('order 'status+submission-time)
>                      ^
> Could you please remove the code comments?  (And in other places too :-))
>
> > +                   (('order 'build-id) "id ASC")
>                      ^
> And fix the indentation :-)
>
> About the indentation, I've seen it at several places in the code.
> Maybe you could try to setup your text editor so that it indents Scheme
> correctly.  Don't hesitate to ask for help about it.  You can also join
> us on IRC (#guix), we'd happily share our tricks.
>
> > +                  (('order 'decreasing-build-id) "id DESC")
> > +                  (('order 'finish-time) "stoptime DESC")
> > +                  (('order 'start-time) "starttime DESC")
> > +                  (('order 'submission-time) "timestamp DESC")
>
> [...]
>
> > @@ -624,3 +648,67 @@ FROM Evaluations ORDER BY id DESC LIMIT " limit
> ";"))
> >                       (#:specification . ,specification)
> >                       (#:commits . ,(string-tokenize commits)))
> >                     evaluations))))))
> > +
>   ^
> extra line :-)
>
> > +(define (db-get-evaluations-build-summary db spec limit border-low
> border-high)
> > +  (let loop ((rows  (sqlite-exec db
>                                        ^
> The string needs to start here, otherwise you'll have an indentation
> issue.
>
> > +"SELECT E.id, E.revision, B.succeeded, B.failed, B.scheduled FROM
>                                                                      ^
> Please end each line with '\', so that new lines aren't interpreted by
> SQLite.
>
> > +  (SELECT id, evaluation, SUM(status=0) as succeeded, SUM(status>0) as
> failed, SUM(status<0) as scheduled
> > +  FROM Builds
> > +  GROUP BY evaluation) B
> > +  JOIN
> > +  (SELECT id, revision
> > +  FROM Evaluations
> > +  WHERE (specification=" spec ")\
> > +  AND (" border-low "IS NULL OR (id >" border-low "))\
> > +  AND (" border-high "IS NULL OR (id <" border-high "))\
> > +  ORDER BY CASE WHEN " border-low "IS NULL THEN id ELSE -id END DESC
> > +  LIMIT " limit ") E
> > +ON B.evaluation=E.id
> > +ORDER BY E.id ASC;"))
> > +             (evaluations '()))
> > +    (match rows
> > +      (() evaluations)
> > +      ((#(id revision succeeded failed scheduled)
> > +        . rest)
> > +       (loop rest
> > +             (cons `((#:id . ,id)
> > +                     (#:revision . ,revision)
> > +                     (#:succeeded . ,succeeded)
> > +                     (#:failed . ,failed)
> > +                     (#:scheduled . ,scheduled))
> > +                   evaluations))))))
> > +
> > +(define (db-get-evaluations-count db spec)
> > +  "Return the number of evaluations of the given specification SPEC"
>
> Our convention is that we end our sentences with a '.' in docstrings :-)
>
> > +  (let ((rows (sqlite-exec db
> > +"SELECT COUNT(id) FROM Evaluations
> > +WHERE specification=" spec)))
> > +    (array-ref (list-ref rows 0) 0)))
> > +
> > +(define (db-get-evaluations-id-max db spec)
> > +  "Return the max id of evaluations of the given specification SPEC"
> > +  (let ((rows (sqlite-exec db
> > +"SELECT MAX(id) FROM Evaluations
> > +WHERE specification=" spec)))
> > +    (array-ref (list-ref rows 0) 0)))
> > +
> > +(define (db-get-evaluations-id-min db spec)
> > +  "Return the min id of evaluations of the given specification SPEC"
> > +  (let ((rows (sqlite-exec db
> > +"SELECT MIN(id) FROM Evaluations
> > +WHERE specification=" spec)))
> > +    (array-ref (list-ref rows 0) 0)))
> > +
>  ^ extra line
> > +
> > +(define (db-get-builds-id-max db eval)
> > +  (let ((rows (sqlite-exec db
> > +"SELECT MAX(stoptime) FROM Builds
> > +WHERE evaluation=" eval)))
> > +    (array-ref (list-ref rows 0) 0)))
> > +
>  ^ extra line
> > +
> > +(define (db-get-builds-id-min db eval)
> > +  (let ((rows (sqlite-exec db
> > +"SELECT MIN(stoptime) FROM Builds
> > +WHERE evaluation=" eval)))
> > +    (array-ref (list-ref rows 0) 0)))
> > diff --git a/src/cuirass/http.scm b/src/cuirass/http.scm
> > index a45e6b1..1995abf 100644
> > --- a/src/cuirass/http.scm
> > +++ b/src/cuirass/http.scm
> > @@ -1,8 +1,10 @@
> > +
> >  ;;;; http.scm -- HTTP API
> >  ;;; Copyright © 2016 Mathieu Lirzin <m...@gnu.org>
> >  ;;; Copyright © 2017 Mathieu Othacehe <m.othac...@gmail.com>
> >  ;;; Copyright © 2018 Ludovic Courtès <l...@gnu.org>
> >  ;;; Copyright © 2018 Clément Lassieur <clem...@lassieur.org>
> > +;;; Copyright © 2018 Tatiana Sholokhova <tanja201...@gmail.com>
> >  ;;;
> >  ;;; This file is part of Cuirass.
> >  ;;;
> > @@ -23,8 +25,10 @@
> >    #:use-module (cuirass database)
> >    #:use-module (cuirass utils)
> >    #:use-module (cuirass logging)
> > +  #:use-module (srfi srfi-1)
> >    #:use-module (srfi srfi-11)
> >    #:use-module (srfi srfi-26)
> > +  #:use-module (ice-9 binary-ports)
> >    #:use-module (ice-9 match)
> >    #:use-module (json)
> >    #:use-module (web request)
> > @@ -33,8 +37,39 @@
> >    #:use-module (web uri)
> >    #:use-module (fibers)
> >    #:use-module (fibers channels)
> > +  #:use-module (sxml simple)
> > +  #:use-module (cuirass templates)
> >    #:export (run-cuirass-server))
> >
> > +(define %static-directory
> > +  ;; Define to the static file directory.
> > +  (string-append (or (getenv "CUIRASS_DATADIR")
> > +                     (string-append %datadir "/" %package))
> > +                 "/static"))
>
> Here you should use a parameter[1], it will allow %static-directory to
> be "dynamically bound"[2], which makes it easy to change the setting for
> a particular bit of code, restoring to its original value when done.
> Plus, they are per-thread, unlike global variables.  There are examples
> of paremeters in database.scm.  Then to access the value of the
> parameter, you need to use (%static-directory) instead of
> %static-directory.
>
> [1]: https://www.gnu.org/software/guile/manual/html_node/Parameters.html
> [2]:
> https://www.gnu.org/software/guile/manual/html_node/Lexical-Scope.html
>
> > +(define file-mime-types
> > +  '(("css" . (text/css))
> > +    ("otf" . (font/otf))
> > +    ("woff" . (font/woff))
> > +    ("js"  . (text/javascript))
> > +    ("png" . (image/png))
> > +    ("gif" . (image/gif))
> > +    ("html" . (text/html))))
> > +
> > +(define file-white-list
> > +  '("css/bootstrap.css"
> > +    "css/open-iconic-bootstrap.css"
> > +    "fonts/open-iconic.otf"
> > +    "fonts/open-iconic.woff"
> > +    "images/logo.png"))
> > +
>  ^ extra line :-)
> > +
> > +(define (file-extension file-name)
> > +  (last (string-split file-name #\.)))
>
> This would behave inconsistenly if there is no extension.  Please use
> 'file-extension' from (guix utils).
>
> > +(define (directory? filename)
> > +  (string=? filename (dirname filename)))
>
> This doesn't work, it seems to always return #f.  Instead, you can use
> 'file-is-directory?' from the module (guix build union).
>
> As a general rule, don't hesitate to use stuff from Guix, because it's
> already a dependency, it's very close to Cuirass, and it's good to avoid
> copying code.  Also, don't hesitate to 'grep' for stuff like
> "is-directory" in the Guix repo, to find ideas.  It's big and there are
> many examples that you can use.
>
> >  (define (build->hydra-build build)
> >    "Convert BUILD to an assoc list matching hydra API format."
> >    (define (bool->int bool)
> > @@ -99,11 +134,12 @@ Hydra format."
> >                            (match key-symbol
> >                              ('id (string->number param))
> >                              ('nr (string->number param))
> > +                            ('page (string->number param))
> >                              (_   param)))))))
> >               (string-split query #\&))
> >          '())))
> >
> > -
> > +
>    ^
> Here you removed the page break (^L) :-)
>
> >  ;;;
> >  ;;; Web server.
> >  ;;;
> > @@ -112,9 +148,15 @@ Hydra format."
> >  ;;; https://github.com/NixOS/hydra/blob/master/doc/manual/api.xml
> >  ;;;
> >
> > +
>   ^
> Here you added a line.
>
> >  (define (request-path-components request)
> >    (split-and-decode-uri-path (uri-path (request-uri request))))
> >
> > +(define (normalize-parameter parameter)
> > + (if parameter
> > +    (list-ref parameter 0)
> > +    #f))
>
> You don't need this function, see below.
>
> >  (define (url-handler request body db-channel)
> >
> >    (define* (respond response #:key body (db-channel db-channel))
> > @@ -136,6 +178,24 @@ Hydra format."
> >       (object->json-string
> >        `((error . ,message)))))
>
> [...]
>
> > +    (("jobset" name)
> > +     (let* ((evaluation-id-max (with-critical-section db-channel (db)
> (db-get-evaluations-id-max db name)))
> > +      (evaluation-id-min (with-critical-section db-channel (db)
> (db-get-evaluations-id-min db name)))
>
> Could you please stick to 80 columns?  I've noticed this at other places
> too.
>
> > +      (params (request-parameters request))
> > +      (border-high (normalize-parameter (assq-ref params 'border-high)))
> > +      (border-low (normalize-parameter (assq-ref params 'border-low))))
>
> We need to rewrite request-parameters so that it returns an assoc-list,
> but in the meantime it's better to use the 'assqx-ref' function from
> database.scm.  You probably need to get it out of 'db-get-builds'
> though, and to export it.
>
> > +      (respond-html
> > +       (html-page
> > +         name
> > +         (evaluation-info-table
> > +          name
> > +          (with-critical-section db-channel (db)
> > +            (db-get-evaluations-build-summary
> > +              db
> > +              name
> > +              PAGESIZE
> > +              border-low
> > +              border-high
> > +              ))
>
> Please avoid dangling parentheses :-)
>
> > +          evaluation-id-min
> > +          evaluation-id-max)))))
> > +
> > +    (("eval" id)
> > +     (let* ((builds-id-max (with-critical-section db-channel (db)
> (db-get-builds-id-max db id)))
> > +      (builds-id-min (with-critical-section db-channel (db)
> (db-get-builds-id-min db id)))
>
> Here I think you can put the '(with-critical-section db-channel (db)'
> around the 'let*', so that you don't need to call it twice.  Same for
> "jobset".
>
> > +      (params (request-parameters request))
> > +      (border-high (normalize-parameter (assq-ref params 'border-high)))
> > +      (border-low (normalize-parameter (assq-ref params 'border-low))))
> > +       (respond-html
> > +         (html-page
> > +          "Evaluations"
> > +          (build-eval-table
> > +            (handle-builds-request db-channel
> > +                                  `((evaluation ,id)
> > +                                   (nr ,PAGESIZE)
>
> See below, if it's a parameter, it would be (nr ,(%pagesize)).
>
> > +                                   (order finish-time)
> > +                                   (border-high ,border-high)
> > +                                   (border-low ,border-low)))
> > +            builds-id-min
> > +            builds-id-max)))))
> > +
> > +    (("static" path ...)
> > +     ;(display (request-uri request))
> > +     (respond-static-file path))
> >      ('method-not-allowed
> >       ;; 405 "Method Not Allowed"
> >       (values (build-response #:code 405) #f db-channel))
> >      (_
> > -     (respond (build-response #:code 404)
> > -              #:body (string-append "Resource not found: "
> > -                                    (uri->string (request-uri
> request)))))))
> > +     (respond-not-found  (uri->string (request-uri request))))))
> >
> >  (define* (run-cuirass-server db #:key (host "localhost") (port 8080))
> >    (let* ((host-info  (gethostbyname host))
> > diff --git a/src/cuirass/templates.scm b/src/cuirass/templates.scm
> > new file mode 100644
> > index 0000000..0e72981
> > --- /dev/null
> > +++ b/src/cuirass/templates.scm
> > @@ -0,0 +1,207 @@
> > +
> > +;;;; http.scm -- HTTP API
> > +;;; Copyright © 2018 Tatiana Sholokhova <tanja201...@gmail.com>
> > +;;;
> > +;;; This file is part of Cuirass.
> > +;;;
> > +;;; Cuirass is free software: you can redistribute it and/or modify
> > +;;; it under the terms of the GNU General Public License as published by
> > +;;; the Free Software Foundation, either version 3 of the License, or
> > +;;; (at your option) any later version.
> > +;;;
> > +;;; Cuirass is distributed in the hope that it will be useful,
> > +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +;;; GNU General Public License for more details.
> > +;;;
> > +;;; You should have received a copy of the GNU General Public License
> > +;;; along with Cuirass.  If not, see <http://www.gnu.org/licenses/>.
> > +
> > +(define-module (cuirass templates)
> > +  #:export (html-page
> > +            specifications-table
> > +            build-table
> > +            evaluation-info-table
> > +            build-eval-table
> > +            PAGESIZE))
> > +
> > +(define PAGESIZE 10)
>
> It's better to use Guile parameters[1] for the reasons explained above.
> Also, our convention is to start global variables' names with a '%'.
> That would give:
>
> (define %pagesize
>   ;; description
>   (make-parameter 10))
>
> [1]: https://www.gnu.org/software/guile/manual/html_node/Parameters.html
>
> > +(define (html-page title body)
> > +  "Return html page with given title and body"
> > +  `(html (@ (xmlns "http://www.w3.org/1999/xhtml";) (xml:lang "en")
> (lang "en"))
> > +    (head
> > +      (meta (@ (charset "utf-8")))
> > +      (meta (@ (name "viewport")
> > +               (content "width=device-width, initial-scale=1,
> shrink-to-fit=no")))
> > +      (link (@ (rel "stylesheet")
> > +               (href "/static/css/bootstrap.css")))
> > +      (link (@ (rel "stylesheet")
> > +               (href "/static/css/open-iconic-bootstrap.css")))
> > +      (title ,title))
> > +    (body
> > +      (nav (@ (class "navbar navbar-expand-lg navbar-light bg-light"))
> > +           (a (@ (class "navbar-brand") (href "/"))
> > +              (img (@ (src "/static/images/logo.png")
> > +                      (alt "logo")
> > +                      (height "25")))))
> > +      (main (@ (role "main") (class "container pt-4 px-1"))
> > +            ,body
> > +            (hr)))))
> > +
> > +
> > +(define (specifications-table specs)
> > +  "Return body for main (Projects) html-page"
>
> I think it's good to refer to the arguments while describing the
> function.  I'd use: "Return HTML for the SPECS table." or something
> similar.  I think this function could be used in another context than a
> "body" (please correct me if I'm wrong), so there is no need to put
> "body" in the docstring.
>
> > +  `((p (@ (class "lead")) "Projects")
> > +    (table
> > +      (@ (class "table table-sm table-hover"))
> > +       ,@(if (null? specs)
> > +          `((th (@ (scope "col")) "No elements here."))
> > +          `((thead
> > +              (tr
> > +               (th (@ (scope "col")) Name)
> > +               (th (@ (scope "col")) Branch)))
> > +             (tbody
> > +              ,@(map
> > +               (lambda (spec)
> > +                `(tr
> > +                  (td (a (@ (href "/jobset/" ,(assq-ref spec #:name)))
> ,(assq-ref spec #:name)))
> > +                  (td ,(assq-ref spec #:branch))))
> > +              specs)))))))
> > +
> > +(define (pagination page-id-min page-id-max id-min id-max)
> > +  "Return page navigation buttons"
> > +    `(div (@ (class row))
> > +      (nav
> > +        (@ (class "mx-auto") (aria-label "Page navigation"))
> > +        (ul (@ (class "pagination"))
> > +            (li (@ (class "page-item"))
> > +                (a (@ (class "page-link")
> > +                   (href "?border-high=" ,(number->string (+ id-max
> 1))))
> > +                   "<< First"))
> > +            (li (@ (class "page-item" ,(if (= page-id-max id-max) "
> disabled" "")))
> > +                (a (@ (class "page-link")
> > +                   (href "?border-low=" ,(number->string page-id-max)))
> > +                   "< Previous"))
> > +            (li (@ (class "page-item" ,(if (= page-id-min id-min) "
> disabled" "")))
> > +                (a (@ (class "page-link")
> > +                   (href "?border-high=" ,(number->string page-id-min)))
> > +                   "Next >"))
> > +            (li (@ (class "page-item"))
> > +                (a (@ (class "page-link")
> > +                   (href "?border-low=" ,(number->string (- id-min 1))))
> > +                   "Last >>"))
> > +            ))))
>
> Could you give a status about the pagination?  I have seen several
> discussions about it.  The goal for this commit is to have something
> that we can use, but it doesn't need to be perfect.  We can improve it
> later.
>
> > +(define (minimum lst cur-min)
>
> I have no idea what cur-min means :-)  Could you find a more descriptive
> name?
>
> > +  (cond ((null? lst) cur-min)
> > +      ((< (car lst) cur-min) (minimum (cdr lst) (car lst)))
> > +      (else (minimum (cdr lst) cur-min))))
> > +
>  ^ extra line :-)
> > +
> > +(define (maximum lst cur-max)
> > +  (cond ((null? lst) cur-max)
> > +      ((> (car lst) cur-max) (maximum (cdr lst) (car lst)))
> > +      (else (maximum (cdr lst) cur-max))))
> > +
>  ^ too
> > +
> > +(define (evaluation-info-table name data evaluation-id-min
> evaluation-id-max)
> > +  "Return body for (Evaluation) html-page"
>
> "Return HTML for the EVALUATION table NAME from EVALUATION-ID-MIN to
> EVALUATION-ID-MAX." (and please, replace 'data' with 'evaluations', it's
> difficult to know what 'data' means :-)).
>
> > +  (let ((id-min (minimum (map (lambda (row) (assq-ref row #:id)) data)
> evaluation-id-max))
> > +       (id-max (maximum (map (lambda (row) (assq-ref row #:id)) data)
> evaluation-id-min)))
> > +    `((p (@ (class "lead")) "Evaluations of " ,name)
> > +      ;(p (@ (class "text-muted")) "Showing evaluations ",id-min
> "-",id-max " out of ",evaluation-id-max)
> > +      (table
> > +        (@ (class "table table-sm table-hover table-striped"))
> > +         ,@(if (null? data)
> > +            `((th (@ (scope "col")) "No elements here."))
> > +            `((thead
> > +                (tr
> > +                 (th (@ (scope "col")) "#")
> > +                 (th (@ (scope "col")) Revision)
> > +                 (th (@ (scope "col")) Success)))
> > +               (tbody
> > +                ,@(map
> > +                 (lambda (row)
> > +                  `(tr
> > +                    (th (@ (scope "row")) (a (@ (href "/eval/"
> ,(assq-ref row #:id))) ,(assq-ref row #:id)))
> > +                    (td ,(assq-ref row #:revision))
> > +                    (td
> > +                      (a (@ (href "#") (class "badge badge-success"))
> ,(assq-ref row #:succeeded))
> > +                      (a (@ (href "#") (class "badge badge-danger"))
> ,(assq-ref row #:failed))
> > +                      (a (@ (href "#") (class "badge badge-secondary"))
> ,(assq-ref row #:scheduled)))))
> > +                 data)))))
> > +      ,(pagination id-min id-max evaluation-id-min evaluation-id-max))))
> > +
> > +(define (build-eval-table data build-id-min build-id-max)
> > +
>  ^ extra line
> > +  (define (table-header)
> > +    `(thead
> > +      (tr
> > +       (th (@ (scope "col")) '())
> > +       (th (@ (scope "col")) ID)
> > +       (th (@ (scope "col")) Project)
> > +       (th (@ (scope "col")) "Finished at")
> > +       (th (@ (scope "col")) Job)
> > +       (th (@ (scope "col")) Nixname)
> > +       (th (@ (scope "col")) System)
>
> [...]
>
> > +(define (build-table done pending)
> > +  "Return body for project's html-page"
> > +  (define (table-row build)
> > +    `(tr
> > +      (td ,(assq-ref build #:project))
> > +      (td ,(assq-ref build #:jobset))
> > +      (td ,(assq-ref build #:job))
> > +      (td ,(assq-ref build #:nixname))
> > +      (td ,(assq-ref build #:buildstatus))))
> > +  (define (table-header)
> > +    `(thead
> > +      (tr
> > +       (th (@ (scope "col")) Project)
> > +       (th (@ (scope "col")) Jobset)
> > +       (th (@ (scope "col")) Job)
> > +       (th (@ (scope "col")) Nixname)
> > +       (th (@ (scope "col")) Buildstatus))))
> > +  `((table
> > +     (@ (class "table table-sm table-hover table-striped"))
> > +     (caption  "Latest builds")
> > +     ,@(if (null? done)
> > +       `((th (@ (scope "col")) "No elements here."))
> > +       `(,(table-header)
> > +         (tbody
> > +          (@ (class "table table-sm table-hover table-striped"))
> > +          ,@(map table-row done)))))
> > +    (table
> > +     (@ (class "table table-sm table-hover table-striped"))
> > +     (caption "Queue")
> > +     ,@(if (null? pending)
> > +       `((th (@ (scope "col")) "No elements here."))
> > +       `(,(table-header)
> > +         (tbody
> > +          (@ (class "table table-sm table-hover table-striped"))
> > +          ,@(map table-row pending)))))))
>
> Here I would do a procedure 'build-table' of one argument, that I would
> call twice: once with the builds that are done, and once with the builds
> that are pending.  That would avoid the code repetition.
>
> Also, if you need help with git (to put the commits together), don't
> hesistate to ask, either here or on IRC (#guix).
>
> And please, make sure 'make check' works :-).  I had to replace (3
> "/baz.drv") with (1 "/foo.drv") in the "db-get-builds" test, but I
> didn't look closely at it.
>
> And that's all!
>
>
Thanks for all of you! Great work.
As far as I can see most of the suggestions are on style. Do you think that
we need some improvement in the documentation regarding style, both coding
and
documentation?



> Thank you!
> Clément
>
>

Reply via email to