aboutsummaryrefslogtreecommitdiff
path: root/lib/render/view.ml
blob: 9f67705c3ba70269ac056775420f6c7667fd145d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
module K = Colitur_kernel
module T = Template

let str s = T.Str s
let bool b = T.Bool b

let month_names =
  [| ("Ianuarius", "January"); ("Februarius", "February"); ("Martius", "March");
     ("Aprilis", "April"); ("Maius", "May"); ("Iunius", "June");
     ("Iulius", "July"); ("Augustus", "August"); ("September", "September");
     ("October", "October"); ("November", "November"); ("December", "December") |]

let names_value (n : K.Names.t) =
  T.Obj (List.map (fun (l, s) -> (K.Lang.to_string l, str s)) (K.Names.to_list n))

let dow_int = function
  | K.Date.Sun -> 0 | K.Date.Mon -> 1 | K.Date.Tue -> 2 | K.Date.Wed -> 3
  | K.Date.Thu -> 4 | K.Date.Fri -> 5 | K.Date.Sat -> 6

let citation_ref cits part =
  match
    List.find_opt (fun (c : K.Citation.t) -> c.K.Citation.part = part) cits
  with
  | Some c -> c.K.Citation.reference
  | None -> ""

let comm_value (c, priv) =
  T.Obj
    [ ("slug", str (K.Slug.to_string c.K.Celebration.slug));
      ("name", names_value c.K.Celebration.names);
      ("privileged", bool (priv = K.Precedence.Privileged)) ]

(* A padding cell: present so a grid row always has seven entries, and flagged
   so a template can render it blank. Every field a real day has is present and
   empty, so a template never hits a missing key on a padding cell. *)
let padding_cell dow =
  T.Obj
    [ ("iso", str ""); ("dom", str ""); ("dow", str (string_of_int dow));
      ("in_month", bool false);
      ("season", str ""); ("week", str ""); ("slug", str "");
      ("name", T.Obj []); ("rank", str ""); ("rank_label", T.Obj []);
      ("colour", str "");
      ("is_white", bool false); ("is_red", bool false); ("is_green", bool false);
      ("is_violet", bool false); ("is_rose", bool false); ("is_black", bool false);
      ("subject", str ""); ("comms", T.List []);
      ("transferred_in", T.List []); ("transferred_out", T.List []);
      ("first", str ""); ("gospel", str ""); ("last", bool false) ]

let day_value ~vocab (d : ('s, 'r) K.Liturgical_day.t) =
  let date = d.K.Liturgical_day.date in
  let tmp = d.K.Liturgical_day.temporal in
  let cel = d.K.Liturgical_day.observed in
  let colour = cel.K.Celebration.colour in
  let is c = bool (colour = c) in
  T.Obj
    [ ("iso", str (K.Date.to_iso8601 date));
      ("dom", str (string_of_int (K.Date.day date)));
      ("dow", str (string_of_int (dow_int (K.Date.weekday date))));
      ("in_month", bool true);
      ("season", str (vocab.K.Vocab.season_to_string tmp.K.Temporal.season));
      ("week", str (match tmp.K.Temporal.week with Some w -> string_of_int w | None -> ""));
      ("slug", str (K.Slug.to_string cel.K.Celebration.slug));
      ("name", names_value cel.K.Celebration.names);
      ("rank", str (vocab.K.Vocab.rank_to_string cel.K.Celebration.rank));
      ("rank_label", names_value cel.K.Celebration.names);
      ("colour", str (K.Colour.to_string colour));
      ("is_white", is K.Colour.White); ("is_red", is K.Colour.Red);
      ("is_green", is K.Colour.Green); ("is_violet", is K.Colour.Violet);
      ("is_rose", is K.Colour.Rose); ("is_black", is K.Colour.Black);
      ("subject", str (K.Subject.to_string cel.K.Celebration.subject));
      ("comms", T.List (List.map comm_value d.K.Liturgical_day.commemorations));
      ( "transferred_in",
        T.List
          (match d.K.Liturgical_day.transferred_in with
           | None -> []
           | Some c -> [ T.Obj [ ("slug", str (K.Slug.to_string c.K.Celebration.slug)) ] ]) );
      ( "transferred_out",
        T.List
          (List.map
             (fun (c, dest) ->
               T.Obj
                 [ ("slug", str (K.Slug.to_string c.K.Celebration.slug));
                   ("to", str (K.Date.to_iso8601 dest)) ])
             d.K.Liturgical_day.transferred_out) );
      ("first", str (citation_ref d.K.Liturgical_day.citations K.Citation.First));
      ("gospel", str (citation_ref d.K.Liturgical_day.citations K.Citation.Gospel));
      (* overwritten per grid row by [set_last]; false in the flat [days] list *)
      ("last", bool false) ]

(* Bucket a month's day values into Sunday-started weeks of exactly seven cells,
   padding both ends. This is the computation the template cannot do. *)
(* [last] is true on the seventh cell of a row. A table row needs its separator
   BETWEEN cells and the engine has no "unless last" construct, so the flag is
   data -- the same rule as [in_month]. Without it the LaTeX grid emits eight
   columns for seven cells and pdflatex rejects the file. *)
let set_last cells =
  List.mapi
    (fun i c -> match c with T.Obj kvs -> T.Obj (("last", bool (i = 6)) :: List.remove_assoc "last" kvs) | v -> v)
    cells

let weeks_of_month ~first_dow day_values =
  let lead = List.init first_dow (fun i -> padding_cell i) in
  let cells = lead @ day_values in
  let rec chunk acc = function
    | [] -> List.rev acc
    | rest ->
        let take = min 7 (List.length rest) in
        let week = List.filteri (fun i _ -> i < take) rest in
        let tl = List.filteri (fun i _ -> i >= take) rest in
        let week =
          if take = 7 then week
          else week @ List.init (7 - take) (fun i -> padding_cell (take + i))
        in
        chunk (week :: acc) tl
  in
  List.mapi
    (fun i w -> T.Obj [ ("num", str (string_of_int (i + 1))); ("days", T.List (set_last w)) ])
    (chunk [] cells)

let of_days ~vocab ~rite ~year days =
  let dvs = List.map (fun d -> (d, day_value ~vocab d)) days in
  let months =
    List.init 12 (fun i ->
        let m = i + 1 in
        let own =
          List.filter (fun (d, _) -> K.Date.month d.K.Liturgical_day.date = m) dvs
        in
        let day_values = List.map snd own in
        let first_dow =
          match own with
          | (d, _) :: _ -> dow_int (K.Date.weekday d.K.Liturgical_day.date)
          | [] -> 0
        in
        let la, en = month_names.(i) in
        T.Obj
          [ ("num", str (string_of_int m));
            ("name", T.Obj [ ("la", str la); ("en", str en) ]);
            ("days", T.List day_values);
            ("weeks", T.List (weeks_of_month ~first_dow day_values)) ])
  in
  T.Obj
    [ ("rite", str rite);
      ("year", str (string_of_int year));
      ("months", T.List months);
      ("days", T.List (List.map snd dvs)) ]