diff options
author | Thomas Voss <mail@thomasvoss.com> | 2025-06-06 01:32:41 +0200 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2025-06-06 01:32:41 +0200 |
commit | 2c726e551d90d20bcd2d78545c369864cc9038e5 (patch) | |
tree | 520615a33c966c729e70e549e859a2c01dc4ba40 | |
parent | 9379ea42a9b0afd1abf705bf3300f03d8bbe79f9 (diff) |
Use CSV’s for mintages
-rw-r--r-- | .dir-locals.el | 9 | ||||
-rw-r--r-- | .exrc | 9 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | GNUmakefile | 9 | ||||
-rw-r--r-- | cmd/mfmt/main.go | 189 | ||||
-rw-r--r-- | cmd/mfmt/main_test.go | 140 | ||||
-rw-r--r-- | cmd/mfmt/util_test.go | 35 | ||||
-rw-r--r-- | contrib/emacs/mintage-mode.el | 26 | ||||
-rw-r--r-- | contrib/vim/syntax/mintage.vim | 13 | ||||
-rw-r--r-- | data/mintages/ad | 119 | ||||
-rw-r--r-- | data/mintages/ad-c-circ.csv | 28 | ||||
-rw-r--r-- | data/mintages/ad-c-nifc.csv | 24 | ||||
-rw-r--r-- | data/mintages/ad-c-proof.csv | 24 | ||||
-rw-r--r-- | data/mintages/ad-s-circ.csv | 13 | ||||
-rw-r--r-- | data/mintages/ad-s-nifc.csv | 13 | ||||
-rw-r--r-- | data/mintages/ad-s-proof.csv | 13 | ||||
-rw-r--r-- | src/http.go | 13 | ||||
-rw-r--r-- | src/mintage/mintage.go | 40 | ||||
-rw-r--r-- | src/mintage/parser.go | 368 | ||||
-rw-r--r-- | src/mintage/parser_test.go | 233 | ||||
-rw-r--r-- | src/templates.go | 4 | ||||
-rw-r--r-- | src/templates/coins-mintages.html.tmpl | 15 | ||||
-rw-r--r-- | static/style.css | 4 |
23 files changed, 288 insertions, 1054 deletions
diff --git a/.dir-locals.el b/.dir-locals.el index d8584cc..8895f93 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -1,11 +1,4 @@ -((nil . ((eval . (progn - (add-to-list - 'load-path - (expand-file-name - "contrib/emacs" - (locate-dominating-file default-directory "contrib/emacs"))) - (require 'mintage-mode))))) - (go-ts-mode . ((comment-start . "/* ") +((go-ts-mode . ((comment-start . "/* ") (comment-end . " */") (comment-continue . " ") (require-final-newline . t))) @@ -1,23 +1,14 @@ -set runtimepath+=contrib/vim - function s:SaveExcursion(cmd) let l:win = winsaveview() silent execute 'keepjumps %!' .. a:cmd call winrestview(l:win) endfunction -autocmd BufNewFile,BufRead */data/mintages/* - \ setfiletype mintage - \ | setlocal nowrap autocmd BufNewFile,BufRead */cmd/exttmpl/* \ setlocal makeprg=go\ build\ ./cmd/exttmpl -autocmd BufNewFile,BufRead */cmd/mfmt/* - \ setlocal makeprg=go\ build\ ./cmd/mfmt autocmd FileType go autocmd BufWritePre <buffer> \ call s:SaveExcursion('gofmt -s') -autocmd FileType mintage autocmd BufWritePre <buffer> - \ call s:SaveExcursion('./mfmt') nnoremap <silent> gM :wall \| make all-i18n<CR> nnoremap <silent> <LocalLeader>t :vimgrep /\CTODO/ **/*<CR> @@ -1,7 +1,6 @@ # Binaries euro-cash.eu exttmpl -mfmt !cmd/* diff --git a/GNUmakefile b/GNUmakefile index 3c48127..62402b8 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -4,9 +4,8 @@ gofiles := $(shell find main.go src -name '*.go') templates := $(shell find src/templates -name '*.tmpl') exttmpl := $(wildcard cmd/exttmpl/*.go) -mfmt := $(wildcard cmd/mfmt/*.go) -all: euro-cash.eu exttmpl mfmt +all: euro-cash.eu exttmpl euro-cash.eu: $(cssfiles) $(templates) $(gofiles) go build @@ -19,9 +18,6 @@ all-i18n: exttmpl exttmpl: $(exttmpl) go build ./cmd/exttmpl -mfmt: $(mfmt) - go build ./cmd/mfmt - %.min.css: %.css lightningcss -m $< -o $@ @@ -39,7 +35,6 @@ clean: find . -type f \( \ -name euro-cash.eu \ -or -name exttmpl \ - -or -name mfmt \ -or -name '*.min.css' \ -or -name '*.tar.gz' \ - \) -delete + \) -delete
\ No newline at end of file diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go deleted file mode 100644 index c4e2277..0000000 --- a/cmd/mfmt/main.go +++ /dev/null @@ -1,189 +0,0 @@ -/* Simple formatter for mintage files. This is not perfect and doesn’t - check for syntactic correctness, it’s just to get stuff aligned - nicely. Maybe in the future I will construct a military-grade - formatter. */ - -package main - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "strings" -) - -const ws = " \t" - -var ( - rv int - - reMintageYear = regexp.MustCompile(`^\d{4}(-[^ \t]+)?`) - reMintageRowC = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){2}([0-9.]+|\?)$`) - reMintageRowS = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){7}([0-9.]+|\?)$`) -) - -func main() { - if len(os.Args) == 1 { - mfmt("-", os.Stdin, os.Stdout) - } - for _, arg := range os.Args[1:] { - f, err := os.OpenFile(arg, os.O_RDWR, 0) - if err != nil { - warn(err) - continue - } - defer f.Close() - - buf := bytes.NewBuffer(make([]byte, 0, 8192)) - mfmt(arg, f, buf) - - if _, err = f.Seek(0, io.SeekStart); err != nil { - warn(err) - continue - } - - if _, err = f.Write(buf.Bytes()); err != nil { - warn(err) - continue - } - - if err = f.Truncate(int64(buf.Len())); err != nil { - warn(err) - } - } - os.Exit(rv) -} - -func mfmt(file string, r io.Reader, w io.Writer) { - var ( - buf [3]string - bufsz int - longestNums [8]int - ) - - scanner := bufio.NewScanner(r) - for linenr := 1; scanner.Scan(); linenr++ { - line := strings.Trim(scanner.Text(), ws) - - switch { - case len(line) == 0, line[0] == '#': - fmt.Fprintln(w, line) - case reMintageRowS.MatchString(line): - switch bufsz { - case len(buf): - bufsz = 0 - clear(longestNums[:]) - fallthrough - default: - setLongestNum(longestNums[:], line) - buf[bufsz] = line - if bufsz++; bufsz == len(buf) { - fmtMintageRow(buf[0], longestNums[:], w) - fmtMintageRow(buf[1], longestNums[:], w) - fmtMintageRow(buf[2], longestNums[:], w) - } - } - case reMintageRowC.MatchString(line): - var ns [3]int - setLongestNum(ns[:], line) - fmtMintageRow(line, ns[:], w) - case reMintageYear.MatchString(line): - fmtMintageYear(line, w) - default: - warn(fmt.Sprintf("%s:%d: potential syntax error", file, linenr)) - fmt.Fprintln(w, line) - } - } -} - -func setLongestNum(longest []int, line string) { - for i, x := range strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) { - n := len(strings.ReplaceAll(x, ".", "")) - n += (n - 1) / 3 // Thousands separators - longest[i] = max(longest[i], n) - } -} - -func fmtMintageYear(line string, w io.Writer) { - switch i := strings.IndexAny(line, ws); i { - case -1: - fmt.Fprintln(w, line) - default: - fmt.Fprintf(w, "%s %s\n", line[:i], strings.TrimLeft(line[i:], ws)) - } -} - -func fmtMintageRow(line string, longest []int, w io.Writer) { - tokens := strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) - - for i, tok := range tokens { - s := formatMintage(tok) - - if i == 0 { - fmt.Fprintf(w, "\t%*s", longest[i], s) - } else { - fmt.Fprintf(w, "%*s", longest[i]+1, s) - } - } - - fmt.Fprintln(w) -} - -func formatMintage(s string) string { - if s == "?" { - return s - } - - n := atoiWithDots(s) - digits := intlen(n) - dots := (digits - 1) / 3 - out := make([]byte, digits+dots) - - for i, j := len(out)-1, 0; ; i-- { - out[i] = byte(n%10) + 48 - if n /= 10; n == 0 { - return string(out) - } - if j++; j == 3 { - i, j = i-1, 0 - out[i] = '.' - } - } -} - -func intlen(v int) int { - switch { - case v == 0: - return 1 - default: - n := 0 - for x := v; x != 0; x /= 10 { - n++ - } - return n - } -} - -func atoiWithDots(s string) int { - n := 0 - for _, ch := range s { - if ch == '.' { - continue - } - n = n*10 + int(ch) - '0' - } - return n -} - -func warn(e any) { - fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), e) - rv = 1 -} diff --git a/cmd/mfmt/main_test.go b/cmd/mfmt/main_test.go deleted file mode 100644 index ef91473..0000000 --- a/cmd/mfmt/main_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package main - -import ( - "bytes" - "testing" -) - -func runTest(t *testing.T, in, out string, nilErr bool) { -} - -func TestComplete(t *testing.T) { - in := ` -2014 -60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 -70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 -3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 - -# Ref: https://example.com -2015 - 0 0 0 0 0 0 0 1.072.400 -40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 - ? ? ? ? ? ? ? ? - -2016 - 0 0 0 0 0 0 2.339.200 0 -35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 - ? ? ? ? ? ? ? ? - -2017 -2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 -22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 - ? ? ? ? ? ? ? ? - -2018 -2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 -20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 - ? ? ? ? ? ? ? ? - -2019 -2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 -15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 - ? ? ? ? ? ? ? ? - -2020 - 0 0 0 860.000 175.000 740.000 0 1.500.000 -12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 - ? ? ? ? ? ? ? ? - -2021 - 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2022 - 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2023 - 0 0 0 0 0 0 0 2.075.250 -10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2024 - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - - 2014 "Council of Europe" - ? ? ? -` - out := ` -2014 - 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 - 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 - 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 - -# Ref: https://example.com -2015 - 0 0 0 0 0 0 0 1.072.400 - 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 - ? ? ? ? ? ? ? ? - -2016 - 0 0 0 0 0 0 2.339.200 0 - 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 - ? ? ? ? ? ? ? ? - -2017 - 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 - 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 - ? ? ? ? ? ? ? ? - -2018 - 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 - 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 - ? ? ? ? ? ? ? ? - -2019 - 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 - 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 - ? ? ? ? ? ? ? ? - -2020 - 0 0 0 860.000 175.000 740.000 0 1.500.000 - 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 - ? ? ? ? ? ? ? ? - -2021 - 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2022 - 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2023 - 0 0 0 0 0 0 0 2.075.250 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2024 - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - -2014 "Council of Europe" - ? ? ? -` - - r := bytes.NewReader([]byte(in)) - w := new(bytes.Buffer) - - mfmt("-", r, w) - if w.String() != out { - t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String()) - } -} diff --git a/cmd/mfmt/util_test.go b/cmd/mfmt/util_test.go deleted file mode 100644 index 8366a50..0000000 --- a/cmd/mfmt/util_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* This file contains tests for utility functions used in the mfmt - binary. Tests of the actual application are in main_test.go. */ - -package main - -import "testing" - -func TestFormatInt(t *testing.T) { - for _, x := range [...]struct{ x, y string }{ - {"?", "?"}, - {"0", "0"}, - {"123", "123"}, - {"81758", "81.758"}, - {"752759237528", "752.759.237.528"}, - } { - s := formatMintage(x.x) - if s != x.y { - t.Fatalf(`Expected s="%s"; got "%s"`, x.y, s) - } - } -} - -func TestIntLen(t *testing.T) { - for _, x := range [...]struct{ x, y int }{ - {0, len("0")}, - {123, len("123")}, - {81758, len("81758")}, - {752759237528, len("752759237528")}, - } { - n := intlen(x.x) - if n != x.y { - t.Fatalf("Expected n=%d; got %d", x.y, n) - } - } -} diff --git a/contrib/emacs/mintage-mode.el b/contrib/emacs/mintage-mode.el deleted file mode 100644 index 8cfe036..0000000 --- a/contrib/emacs/mintage-mode.el +++ /dev/null @@ -1,26 +0,0 @@ -;;; mintage-mode.el --- Major mode for mintage data files -*- lexical-binding: t; -*- - -(defconst mintage--number (rx (or digit ?.))) -(defconst mintage--string (rx "\"" (*? (not "\"")) "\"")) -(defconst mintage--unknown (rx ??)) -(defconst mintage--comment (rx bol (* space) ?# (* any))) -(defconst mintage--year (rx bol (= 4 digit) (? ?- (+ (not space))))) - -(defun mintage--font-lock-keywords () - `((,mintage--year . font-lock-constant-face) - (,mintage--number . 'font-lock-number-face) - (,mintage--string . font-lock-string-face) - (,mintage--comment . font-lock-comment-face) - (,mintage--unknown . font-lock-warning-face))) - -;;;###autoload -(add-to-list 'auto-mode-alist (cons "mintages/[a-z]\\{2\\}\\'" 'mintage-mode)) - -;;;###autoload -(define-derived-mode mintage-mode conf-mode "Mintage" - "Major mode for editing euro-cash.eu mintage files" - (setq comment-start "#" - comment-end "") - (setq font-lock-defaults '(mintage--font-lock-keywords))) - -(provide 'mintage-mode) diff --git a/contrib/vim/syntax/mintage.vim b/contrib/vim/syntax/mintage.vim deleted file mode 100644 index 2b03b41..0000000 --- a/contrib/vim/syntax/mintage.vim +++ /dev/null @@ -1,13 +0,0 @@ -if exists('b:current_syntax') - finish -endif -let b:current_syntax = 'mintage' - -syntax match Comment /^\s*#.*/ -syntax match Number /[0-9.]\+/ -syntax match Identifier /\v\d{4}(-\S+)?/ -syntax match String /"[^"]\{-}"/ - - -" ‘Todo’ is semantically a better syntax group, but it looks bad -syntax match Error /?/ diff --git a/data/mintages/ad b/data/mintages/ad deleted file mode 100644 index ee08991..0000000 --- a/data/mintages/ad +++ /dev/null @@ -1,119 +0,0 @@ -2014 - 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 - 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 - 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 - -2015 - 0 0 0 0 0 0 0 1.072.400 - 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 - ? ? ? ? ? ? ? ? - -2016 - 0 0 0 0 0 0 2.339.200 0 - 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 - ? ? ? ? ? ? ? ? - -2017 - 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 - 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 - ? ? ? ? ? ? ? ? - -2018 - 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 - 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 - ? ? ? ? ? ? ? ? - -2019 - 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 - 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 - ? ? ? ? ? ? ? ? - -2020 - 0 0 0 860.000 175.000 740.000 0 1.500.000 - 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 - ? ? ? ? ? ? ? ? - -2021 - 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2022 - 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2023 - 0 0 0 0 0 0 0 2.075.250 - 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 - ? ? ? ? ? ? ? ? - -2024 - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - ? ? ? ? ? ? ? ? - -2014 "20 Years in the Council of Europe" - ? ? ? - -2015 "30 Years of Political Rights" - ? ? ? - -2015 "25th Anniversary of the Customs Agreement" - ? ? ? - -2016 "25th Anniversary of the Radio and Television of Andorra" - ? ? ? - -2016 "150th Anniversary of the New Reform of 1866" - ? ? ? - -2017 "100th Anniversary of the National Anthem of Andorra" - ? ? ? - -2017 "The Pyrenean Country" - ? ? ? - -2018 "70th Anniversary of the Universal Declaration of Human Rights" - ? ? ? - -2018 "25th Anniversary of the Andorran Constitution" - ? ? ? - -2019 "600th Anniversary of the Council of the Land" - ? ? ? - -2019 "2019 Alpine Ski World Cup Finals" - ? ? ? - -2020 "50 Years of Universal Female Suffrage" - ? ? ? - -2020 "The 27th Ibero-American Summit" - ? ? ? - -2021 "Taking Care of Our Seniors" - ? ? ? - -2021 "Our Lady of Meritxell" - ? ? ? - -2022 "The Legend of Charlemagne" - ? ? ? - -2022 "10th Anniversary of the Monetary Agreement with the EU" - ? ? ? - -2023 "Summer Solstice Fire Festivals in the Pyrenees" - ? ? ? - -2023 "30 Years of United Nations Membership" - ? ? ? - -# TODO: Find the official name of this coin -2024 "100 Years of Skiing in Andorra" - ? ? ? - -# TODO: Find the official name of this coin -2024 "UCI Mountain Bike World Cup" - ? ? ? diff --git a/data/mintages/ad-c-circ.csv b/data/mintages/ad-c-circ.csv new file mode 100644 index 0000000..3482b8e --- /dev/null +++ b/data/mintages/ad-c-circ.csv @@ -0,0 +1,28 @@ +Year,Name,Mintmark,Mintage +2014,20 Years in the Council of Europe,, +2015,30 Years of Political Rights,, +2015,25th Anniversary of the Customs Agreement,, +2016,25th Anniversary of the Radio and Television of Andorra,, +2016,150th Anniversary of the New Reform of 1866,, +2017,100th Anniversary of the National Anthem of Andorra,, +2017,The Pyrenean Country,, +2018,70th Anniversary of the Universal Declaration of Human Rights,, +2018,25th Anniversary of the Andorran Constitution,, +2019,600th Anniversary of the Council of the Land,, +2019,2019 Alpine Ski World Cup Finals,, +2020,50 Years of Universal Female Suffrage,, +2020,The 27th Ibero-American Summit,, +2021,Taking Care of Our Seniors,, +2021,Our Lady of Meritxell,, +2022,The Legend of Charlemagne,, +2022,10th Anniversary of the Monetary Agreement with the EU,, +2023,Summer Solstice Fire Festivals in the Pyrenees,, +2023,30 Years of United Nations Membership,, +# TODO: Find the official name of this coin +2024,100 Years of Skiing in Andorra,, +# TODO: Find the official name of this coin +2024,UCI Mountain Bike World Cup,, +# TODO: Find the official name of this coin +2025,Bearded Vulture,, +# TODO: Find the official name of this coin +2025,Games of the Small States of Europe,,
\ No newline at end of file diff --git a/data/mintages/ad-c-nifc.csv b/data/mintages/ad-c-nifc.csv new file mode 100644 index 0000000..f230855 --- /dev/null +++ b/data/mintages/ad-c-nifc.csv @@ -0,0 +1,24 @@ +Year,Name,Mintmark,Mintage +2014,20 Years in the Council of Europe,, +2015,30 Years of Political Rights,, +2015,25th Anniversary of the Customs Agreement,, +2016,25th Anniversary of the Radio and Television of Andorra,, +2016,150th Anniversary of the New Reform of 1866,, +2017,100th Anniversary of the National Anthem of Andorra,, +2017,The Pyrenean Country,, +2018,70th Anniversary of the Universal Declaration of Human Rights,, +2018,25th Anniversary of the Andorran Constitution,, +2019,600th Anniversary of the Council of the Land,, +2019,2019 Alpine Ski World Cup Finals,, +2020,50 Years of Universal Female Suffrage,, +2020,The 27th Ibero-American Summit,, +2021,Taking Care of Our Seniors,, +2021,Our Lady of Meritxell,, +2022,The Legend of Charlemagne,, +2022,10th Anniversary of the Monetary Agreement with the EU,, +2023,Summer Solstice Fire Festivals in the Pyrenees,, +2023,30 Years of United Nations Membership,, +2024,100 Years of Skiing in Andorra,, +2024,UCI Mountain Bike World Cup,, +2025,Bearded Vulture,, +2025,Games of the Small States of Europe,,
\ No newline at end of file diff --git a/data/mintages/ad-c-proof.csv b/data/mintages/ad-c-proof.csv new file mode 100644 index 0000000..f230855 --- /dev/null +++ b/data/mintages/ad-c-proof.csv @@ -0,0 +1,24 @@ +Year,Name,Mintmark,Mintage +2014,20 Years in the Council of Europe,, +2015,30 Years of Political Rights,, +2015,25th Anniversary of the Customs Agreement,, +2016,25th Anniversary of the Radio and Television of Andorra,, +2016,150th Anniversary of the New Reform of 1866,, +2017,100th Anniversary of the National Anthem of Andorra,, +2017,The Pyrenean Country,, +2018,70th Anniversary of the Universal Declaration of Human Rights,, +2018,25th Anniversary of the Andorran Constitution,, +2019,600th Anniversary of the Council of the Land,, +2019,2019 Alpine Ski World Cup Finals,, +2020,50 Years of Universal Female Suffrage,, +2020,The 27th Ibero-American Summit,, +2021,Taking Care of Our Seniors,, +2021,Our Lady of Meritxell,, +2022,The Legend of Charlemagne,, +2022,10th Anniversary of the Monetary Agreement with the EU,, +2023,Summer Solstice Fire Festivals in the Pyrenees,, +2023,30 Years of United Nations Membership,, +2024,100 Years of Skiing in Andorra,, +2024,UCI Mountain Bike World Cup,, +2025,Bearded Vulture,, +2025,Games of the Small States of Europe,,
\ No newline at end of file diff --git a/data/mintages/ad-s-circ.csv b/data/mintages/ad-s-circ.csv new file mode 100644 index 0000000..fd05b7c --- /dev/null +++ b/data/mintages/ad-s-circ.csv @@ -0,0 +1,13 @@ +Year,Mintmark,"€0,01","€0,02","€0,05","€0,10","€0,20","€0,50","€1,00","€2,00" +2014,,60000,60000,860000,860000,860000,340000,511843,360000 +2015,,0,0,0,0,0,0,0,1072400 +2016,,0,0,0,0,0,0,2339200,0 +2017,,2582395,1515000,2191421,1103000,1213000,968800,17000,794588 +2018,,2430000,2550000,1800000,980000,1014000,890000,0,868000 +2019,,2447000,1727000,2100000,1610000,1570000,930000,0,1058310 +2020,,0,0,0,860000,175000,740000,0,1500000 +2021,,200000,700000,0,1400000,1420000,600000,50000,1474500 +2022,,700000,450000,400000,700000,700000,380000,0,1708000 +2023,,0,0,0,0,0,0,0,2075250 +2024,,0,900300,1950000,1000000,700000,500000,1050000,1601200 +2025,,,,,,,,,
\ No newline at end of file diff --git a/data/mintages/ad-s-nifc.csv b/data/mintages/ad-s-nifc.csv new file mode 100644 index 0000000..dc8b452 --- /dev/null +++ b/data/mintages/ad-s-nifc.csv @@ -0,0 +1,13 @@ +Year,Mintmark,"€0,01","€0,02","€0,05","€0,10","€0,20","€0,50","€1,00","€2,00" +2014,,70000,70000,70000,70000,70000,70000,70000,70000 +2015,,40000,40000,40000,40000,40000,40000,40000,40000 +2016,,35000,35000,35000,35000,35000,35000,35000,35000 +2017,,22000,22000,22000,22000,22000,22000,22000,22000 +2018,,20000,20000,20000,20000,20000,20000,20000,20000 +2019,,15000,15000,15000,15000,15000,15000,15000,15000 +2020,,12000,12000,12000,12000,12000,12000,12000,12000 +2021,,10500,10500,10500,10500,10500,10500,10500,10500 +2022,,10500,10500,10500,10500,10500,10500,10500,10500 +2023,,10500,10500,10500,10500,10500,10500,10500,10500 +2024,,,,,,,,, +2025,,,,,,,,,
\ No newline at end of file diff --git a/data/mintages/ad-s-proof.csv b/data/mintages/ad-s-proof.csv new file mode 100644 index 0000000..c8ed593 --- /dev/null +++ b/data/mintages/ad-s-proof.csv @@ -0,0 +1,13 @@ +Year,Mintmark,"€0,01","€0,02","€0,05","€0,10","€0,20","€0,50","€1,00","€2,00" +2014,,,,,,,,, +2015,,,,,,,,, +2016,,,,,,,,, +2017,,,,,,,,, +2018,,,,,,,,, +2019,,,,,,,,, +2020,,,,,,,,, +2021,,,,,,,,, +2022,,,,,,,,, +2023,,,,,,,,, +2024,,,,,,,,, +2025,,,,,,,,,
\ No newline at end of file diff --git a/src/http.go b/src/http.go index 04c17de..eaaeb71 100644 --- a/src/http.go +++ b/src/http.go @@ -8,8 +8,6 @@ import ( "log" "math" "net/http" - "os" - "path/filepath" "slices" "strconv" "strings" @@ -139,15 +137,8 @@ func mintageHandler(next http.Handler) http.Handler { td.Type = "circ" } - path := filepath.Join("data", "mintages", td.Code) - f, err := os.Open(path) - if err != nil { - throwError(http.StatusInternalServerError, err, w, r) - return - } - defer f.Close() - - td.Mintages, err = mintage.Parse(f, path) + var err error + td.Mintages, err = mintage.Parse(td.Code) if err != nil { throwError(http.StatusInternalServerError, err, w, r) return diff --git a/src/mintage/mintage.go b/src/mintage/mintage.go new file mode 100644 index 0000000..cc94de7 --- /dev/null +++ b/src/mintage/mintage.go @@ -0,0 +1,40 @@ +package mintage + +type Data struct { + Standard []SRow + Commemorative []CRow +} + +type SRow struct { + Year int + Mintmark string + Mintages [denoms]int +} + +type CRow struct { + Year int + Name string + Mintmark string + Mintage int +} + +const ( + TypeCirc = iota + TypeNifc + TypeProof +) + +const ( + Unknown = -iota - 1 + Invalid +) + +const denoms = 8 + +var cache map[string][3]Data = make(map[string][3]Data) + +func ClearCache(country string) { + if _, ok := cache[country]; ok { + delete(cache, country) + } +} diff --git a/src/mintage/parser.go b/src/mintage/parser.go index 364b6e8..daeb05d 100644 --- a/src/mintage/parser.go +++ b/src/mintage/parser.go @@ -1,297 +1,153 @@ package mintage import ( - "bufio" - "fmt" + "encoding/csv" "io" + "os" + "path/filepath" "strconv" - "strings" - "time" ) -type SyntaxError struct { - expected, got string - file string - linenr int -} - -func (e SyntaxError) Error() string { - return fmt.Sprintf("%s:%d: syntax error: expected %s but got %s", - e.file, e.linenr, e.expected, e.got) -} - -type Data struct { - Standard []SRow - Commemorative []CRow -} +func Parse(country string) ([3]Data, error) { + if data, ok := cache[country]; ok { + return data, nil + } -type SRow struct { - Year int - Mintmark string - Mintages [typeCount][denoms]int -} + var ( + data [3]Data + err error + path = filepath.Join("data", "mintages", country) + ) -type CRow struct { - Year int - Mintmark string - Name string - Mintage [typeCount]int + data[TypeCirc].Standard, err = parseS(path + "-s-circ.csv") + if err != nil { + return data, err + } + data[TypeNifc].Standard, err = parseS(path + "-s-nifc.csv") + if err != nil { + return data, err + } + data[TypeProof].Standard, err = parseS(path + "-s-proof.csv") + if err != nil { + return data, err + } + data[TypeCirc].Commemorative, err = parseC(path + "-c-circ.csv") + if err != nil { + return data, err + } + data[TypeNifc].Commemorative, err = parseC(path + "-c-nifc.csv") + if err != nil { + return data, err + } + data[TypeProof].Commemorative, err = parseC(path + "-c-proof.csv") + if err == nil { + cache[country] = data + } + return data, err } -const ( - TypeCirc = iota - TypeNIFC - TypeProof - typeCount -) +func parseS(path string) ([]SRow, error) { + rows := make([]SRow, 0, 69) /* TODO: Compute number of rows */ -const ( - Unknown = -iota - 1 - Invalid -) + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() -const ( - denoms = 8 - ws = " \t" -) + r := csv.NewReader(f) + r.Comment = '#' + r.FieldsPerRecord = 10 + r.ReuseRecord = true -func Parse(r io.Reader, file string) (Data, error) { - yearsSince := time.Now().Year() - 1999 + 1 - data := Data{ - Standard: make([]SRow, 0, yearsSince), - Commemorative: make([]CRow, 0, yearsSince), + /* Skip header */ + if _, err := r.Read(); err != nil { + return nil, err } - scanner := bufio.NewScanner(r) - for linenr := 1; scanner.Scan(); linenr++ { - line := strings.Trim(scanner.Text(), ws) - if isBlankOrComment(line) { - continue + for { + record, err := r.Read() + if err == io.EOF { + break } - - if len(line) < 4 || !isNumeric(line[:4], false) { - return Data{}, SyntaxError{ - expected: "4-digit year", - got: line, - linenr: linenr, - file: file, - } + if err != nil { + return nil, err } - var ( - commem bool - mintmark string - ) - year, _ := strconv.Atoi(line[:4]) - line = line[4:] - - if len(line) != 0 { - if strings.ContainsRune(ws, rune(line[0])) { - commem = true - goto out - } - if line[0] != '-' { - return Data{}, SyntaxError{ - expected: "end-of-line or mintmark", - got: line, - linenr: linenr, - file: file, - } - } + data := SRow{Mintmark: record[1]} - if line = line[1:]; len(line) == 0 { - return Data{}, SyntaxError{ - expected: "mintmark", - got: "end-of-line", - linenr: linenr, - file: file, - } - } + data.Year, err = strconv.Atoi(record[0]) + if err != nil { + return nil, err + } - switch i := strings.IndexAny(line, ws); i { - case 0: - return Data{}, SyntaxError{ - expected: "mintmark", - got: "whitespace", - linenr: linenr, - file: file, + for i, s := range record[2:] { + if s == "" { + data.Mintages[i] = Unknown + } else { + data.Mintages[i], err = strconv.Atoi(s) + if err != nil { + data.Mintages[i] = Invalid } - case -1: - mintmark = line - default: - mintmark, line = line[:i], line[i:] - commem = true } } - out: - - if !commem { - row := SRow{ - Year: year, - Mintmark: mintmark, - } - for i := range row.Mintages { - line = "" - for isBlankOrComment(line) { - if !scanner.Scan() { - return Data{}, SyntaxError{ - expected: "mintage row", - got: "end-of-file", - linenr: linenr, - file: file, - } - } - line = strings.Trim(scanner.Text(), ws) - linenr++ - } - - tokens := strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) - if tokcnt := len(tokens); tokcnt != denoms { - word := "entries" - if tokcnt == 1 { - word = "entry" - } - return Data{}, SyntaxError{ - expected: fmt.Sprintf("%d mintage entries", denoms), - got: fmt.Sprintf("%d %s", tokcnt, word), - linenr: linenr, - file: file, - } - } - - for j, tok := range tokens { - if tok != "?" && !isNumeric(tok, true) { - return Data{}, SyntaxError{ - expected: "numeric mintage figure or ‘?’", - got: tok, - linenr: linenr, - file: file, - } - } - if tok == "?" { - row.Mintages[i][j] = Unknown - } else { - row.Mintages[i][j] = atoiWithDots(tok) - } - } - } + rows = append(rows, data) + } - data.Standard = append(data.Standard, row) - } else { - row := CRow{ - Year: year, - Mintmark: mintmark, - } - line = strings.TrimLeft(line, ws) - if line[0] != '"' { - return Data{}, SyntaxError{ - expected: "string", - got: line, - linenr: linenr, - file: file, - } - } + return rows, nil +} - line = line[1:] - switch i := strings.IndexByte(line, '"'); i { - case -1: - return Data{}, SyntaxError{ - expected: "closing quote", - got: "end-of-line", - linenr: linenr, - file: file, - } - case 0: - return Data{}, SyntaxError{ - expected: "commemorated event", - got: "empty string", - linenr: linenr, - file: file, - } - default: - row.Name, line = line[:i], line[i+1:] - } +func parseC(path string) ([]CRow, error) { + rows := make([]CRow, 0, 69) /* TODO: Compute number of rows */ - if len(line) != 0 { - return Data{}, SyntaxError{ - expected: "end-of-line", - got: line, - linenr: linenr, - file: file, - } - } + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() - for isBlankOrComment(line) { - if !scanner.Scan() { - return Data{}, SyntaxError{ - expected: "mintage row", - got: "end-of-file", - linenr: linenr, - file: file, - } - } - line = strings.Trim(scanner.Text(), ws) - linenr++ - } + r := csv.NewReader(f) + r.Comment = '#' + r.FieldsPerRecord = 4 + r.ReuseRecord = true - tokens := strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) - if tokcnt := len(tokens); tokcnt != typeCount { - word := "entries" - if tokcnt == 1 { - word = "entry" - } - return Data{}, SyntaxError{ - expected: fmt.Sprintf("%d mintage entries", typeCount), - got: fmt.Sprintf("%d %s", tokcnt, word), - linenr: linenr, - file: file, - } - } + /* Skip header */ + if _, err := r.Read(); err != nil { + return nil, err + } - for i, tok := range tokens { - if tok == "?" { - row.Mintage[i] = Unknown - } else { - row.Mintage[i] = atoiWithDots(tok) - } - } + for { + record, err := r.Read() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } - data.Commemorative = append(data.Commemorative, row) + data := CRow{ + Name: record[1], + Mintmark: record[2], } - } - return data, nil -} + data.Year, err = strconv.Atoi(record[0]) + if err != nil { + return nil, err + } -func isNumeric(s string, dot bool) bool { - for _, ch := range s { - switch ch { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - default: - if ch != '.' || !dot { - return false + s := record[3] + if s == "" { + data.Mintage = Unknown + } else { + data.Mintage, err = strconv.Atoi(s) + if err != nil { + data.Mintage = Invalid } } - } - return true -} -func atoiWithDots(s string) int { - n := 0 - for _, ch := range s { - if ch == '.' { - continue - } - n = n*10 + int(ch) - '0' + rows = append(rows, data) } - return n -} -func isBlankOrComment(s string) bool { - return len(s) == 0 || s[0] == '#' + return rows, nil } diff --git a/src/mintage/parser_test.go b/src/mintage/parser_test.go deleted file mode 100644 index 76e0f01..0000000 --- a/src/mintage/parser_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package mintage - -import ( - "bytes" - "errors" - "testing" -) - -func TestParserComplete(t *testing.T) { - data, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1000 1001 1002 1003 1004 1005 1006 1007 - 1100 1101 1102 1103 1104 1105 1106 1107 - 1200 1201 1202 1203 1204 1205 1206 1207 - 2021-KNM - 2.000 ? 2002 2003 2004 2005 2006 2007 - 2.100 ? 2102 2103 2104 2105 2106 2107 - 2.200 ? 2202 2203 2204 2205 2206 2207 - 2021-MdP - 3000 3001 3002 3003 3004 3005 3006 3007 - 3100 3101 3102 3103 3104 3105 3106 3107 - 3200 3201 3202 3203 3204 3205 3206 3207 - 2022 - 4000 4001 4.002 4003 4004 4005 4006 4007 - 4100 4101 4.102 4103 4104 4105 4106 4107 - 4200 4201 4.202 4203 4204 4205 4206 4207 - - 2009 "10th Anniversary of Economic and Monetary Union" - 1000 2000 3000 - 2022-⋆ "35th Anniversary of the Erasmus Programme" - 1001 ? 3001 - `)), "-") - - if err != nil { - t.Fatalf(`Expected err=nil; got "%s"`, err) - } - - for i, row := range data.Standard { - for k := TypeCirc; k <= TypeProof; k++ { - for j, col := range row.Mintages[k] { - n := 1000*(i+1) + 100*k + j - if i == 1 && j == 1 { - n = Unknown - } - if col != n { - t.Fatalf("Expected data.Standard[%d].Mintages[%d][%d]=%d; got %d", - i, k, j, col, n) - } - } - } - } - - for i, row := range data.Commemorative { - for k := TypeCirc; k <= TypeProof; k++ { - n := 1000*(k+1) + i - if i == 1 && k == 1 { - n = Unknown - } - if row.Mintage[k] != n { - t.Fatalf("Expected row.Mintage[%d]=%d; got %d", - k, n, row.Mintage[k]) - } - } - } - - if len(data.Standard) != 4 { - t.Fatalf("Expected len(data.Standard)=2; got %d", len(data.Standard)) - } - if len(data.Commemorative) != 2 { - t.Fatalf("Expected len(data.Commemorative)=2; got %d", len(data.Commemorative)) - } - - for i, x := range [...]struct { - year int - mintmark, name string - }{ - {2009, "", "10th Anniversary of Economic and Monetary Union"}, - {2022, "⋆", "35th Anniversary of the Erasmus Programme"}, - } { - if data.Commemorative[i].Year != x.year { - t.Fatalf("Expected data.Commemorative[%d].Year=%d; got %d", - i, x.year, data.Commemorative[i].Year) - } - if data.Commemorative[i].Mintmark != x.mintmark { - t.Fatalf(`Expected data.Commemorative[%d].Mintmark="%s"; got "%s"`, - i, x.mintmark, data.Commemorative[i].Mintmark) - } - if data.Commemorative[i].Name != x.name { - t.Fatalf(`Expected data.Commemorative[%d].Name="%s"; got "%s"`, - i, x.name, data.Commemorative[i].Name) - } - } -} - -func TestParserMintmarks(t *testing.T) { - data, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1000 1001 1002 1003 1004 1005 1006 1007 - 1100 1101 1102 1103 1104 1105 1106 1107 - 1200 1201 1202 1203 1204 1205 1206 1207 - 2021-KNM - 2.000 ? 2002 2003 2004 2005 2006 2007 - 2.100 ? 2102 2103 2104 2105 2106 2107 - 2.200 ? 2202 2203 2204 2205 2206 2207 - 2021-MdP - 3000 3001 3002 3003 3004 3005 3006 3007 - 3100 3101 3102 3103 3104 3105 3106 3107 - 3200 3201 3202 3203 3204 3205 3206 3207 - 2022 - 4000 4001 4.002 4003 4004 4005 4006 4007 - 4100 4101 4.102 4103 4104 4105 4106 4107 - 4200 4201 4.202 4203 4204 4205 4206 4207 - `)), "-") - - if err != nil { - t.Fatalf(`Expected err=nil; got "%s"`, err) - } - - for i, row := range data.Standard { - for j, col := range row.Mintages[TypeCirc] { - n := 1000*(i+1) + j - if i == 1 && j == 1 { - n = Unknown - } - if col != n { - t.Fatalf("Expected data.Standard[%d].Mintages[TypeCirc][%d]=%d; got %d", - i, j, col, n) - } - } - } - - for i, y := range [...]int{2020, 2021, 2021, 2022} { - if data.Standard[i].Year != y { - t.Fatalf("Expected data.Standard[%d].Year=%d; got %d", - i, y, data.Standard[i].Year) - } - } - - for i, m := range [...]string{"", "KNM", "MdP", ""} { - if data.Standard[i].Mintmark != m { - t.Fatalf(`Expected data.Standard[%d].Mintmark="%s"; got "%s"`, - i, m, data.Standard[i].Mintmark) - } - } -} - -func TestParserNoYear(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 1.000 1001 1002 1003 1004 1005 1006 1007 - 1.100 1101 1102 1103 1104 1105 1106 1107 - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2021 - 2000 ? 2002 2003 2004 2005 2006 2007 - 2100 ? 2102 2103 2104 2105 2106 2107 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserBadToken(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1.000 1001 1002 1003 1004 1005 1006 1007 - 1.100 1101 1102 1103 1104 1105 1106 1107 - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2021 Naughty! - 2000 ? 2002 2003 2004 2005 2006 2007 - 2100 ? 2102 2103 2104 2105 2106 2107 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserShortRow(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1.000 1001 1002 1003 1004 1005 1006 1007 - 1.100 1101 1102 1103 1104 1105 1106 1107 - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2021 - 2000 ? 2002 2003 2004 2005 2006 2007 - 2100 ? 2102 2103 2104 2105 2106 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserLongRow(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1.000 1001 1002 1003 1004 1005 1006 1007 - 1.100 1101 1102 1103 1104 1105 1106 1107 - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2021 - 2000 ? 2002 2003 2004 2005 2006 2007 - 2100 ? 2102 2103 2104 2105 2106 2107 2108 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} - -func TestParserMissingRow(t *testing.T) { - _, err := Parse(bytes.NewBuffer([]byte(` - 2020 - 1.000 1001 1002 1003 1004 1005 1006 1007 - 1.100 1101 1102 1103 1104 1105 1106 1107 - 1.200 1201 1202 1203 1204 1205 1206 1207 - 2021 - 2000 ? 2002 2003 2004 2005 2006 2007 - 2200 ? 2202 2203 2204 2205 2206 2207 - `)), "-") - - var sErr SyntaxError - if !errors.As(err, &sErr) { - t.Fatalf("Expected err=SyntaxError; got %s", err) - } -} diff --git a/src/templates.go b/src/templates.go index 2cee22e..f6413d9 100644 --- a/src/templates.go +++ b/src/templates.go @@ -13,7 +13,7 @@ import ( type templateData struct { Printer Printer Code, Type string - Mintages mintage.Data + Mintages [3]mintage.Data Countries []country } @@ -86,7 +86,7 @@ func templateMakeTuple(args ...any) []any { func strToCtype(s string) int { switch s { case "nifc": - return mintage.TypeNIFC + return mintage.TypeNifc case "proof": return mintage.TypeProof default: diff --git a/src/templates/coins-mintages.html.tmpl b/src/templates/coins-mintages.html.tmpl index 8c60248..8554c3f 100644 --- a/src/templates/coins-mintages.html.tmpl +++ b/src/templates/coins-mintages.html.tmpl @@ -66,6 +66,7 @@ </div> <button type="submit">{{ .T "Filter" }}</button> </form> + {{ $data := (index .Mintages (strToCtype .Type)) }} <figure> <figcaption>{{ .T "Standard Issue Coins" }}</figcaption> <table class="mintage-table" role="grid"> @@ -80,7 +81,7 @@ <tbody> {{ $p := .Printer }} {{ $type := .Type }} - {{ range .Mintages.Standard }} + {{ range $data.Standard }} <tr> <th scope="col"> {{- .Year -}} @@ -88,9 +89,11 @@ <sub><small>{{ .Mintmark }}</small></sub> {{- end -}} </th> - {{ range (index .Mintages (strToCtype $type)) }} + {{ range .Mintages }} {{ if eq . -1 }} <td>{{ $p.T "Unknown" }}</td> + {{ else if eq . -2 }} + <td class="error">{{ $p.T "Error" }}</td> {{ else if eq . 0 }} <td>—</td> {{ else }} @@ -103,7 +106,7 @@ </tbody> </table> </figure> - {{ if ne (len .Mintages.Commemorative) 0 }} + {{ if ne (len $data.Commemorative) 0 }} <figure> <figcaption>{{ .T "Commemorative Coins" }}</figcaption> <table class="mintage-table-cc" role="grid"> @@ -115,7 +118,7 @@ <tbody> {{ $p := .Printer }} {{ $type := .Type }} - {{ range .Mintages.Commemorative }} + {{ range $data.Commemorative }} <tr> <th scope="col"> {{- .Year -}} @@ -125,9 +128,11 @@ </th> <!-- TODO: Translate commemorative names --> <td>{{ .Name }}</td> - {{ with (index .Mintage (strToCtype $type)) }} + {{ with .Mintage }} {{ if eq . -1 }} <td>{{ $p.T "Unknown" }}</td> + {{ else if eq . -2 }} + <td class="error">{{ $p.T "Error" }}</td> {{ else if eq . 0 }} <td>—</td> {{ else }} diff --git a/static/style.css b/static/style.css index 9ce0d82..023317c 100644 --- a/static/style.css +++ b/static/style.css @@ -2377,3 +2377,7 @@ dl.unix-style dd { dl dt:not(:first-child) { margin-top: var(--block-spacing-vertical); } + +.error { + color: var(--del-color); +}
\ No newline at end of file |