123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- local datafile = require "datafile"
- local dir = require "pl.dir"
- local etlua = require "etlua"
- local plpath = require "pl.path"
- local pp = require "pl.pretty"
- local nsm = require "lsup.namespace"
- local term = require "lsup.term"
- local triple = require "lsup.triple"
- local graph = require "lsup.graph"
- local pkar = require "pocket_archive"
- local logger = pkar.logger
- local model = require "pocket_archive.model"
- local transformers = require "pocket_archive.transformers"
- -- "nil" table - for missing key fallback in chaining.
- local NT = {}
- -- Compile all templates once.
- -- TODO Add override for user-maintained templates.
- local fh, idx_tpl, dres_tpl, ores_tpl
- fh = datafile.open("templates/index.html")
- idx_tpl = assert(etlua.compile(fh:read("a")))
- fh:close()
- fh = datafile.open("templates/dres.html")
- dres_tpl = assert(etlua.compile(fh:read("a")))
- fh:close()
- fh = datafile.open("templates/ores.html")
- ores_tpl = assert(etlua.compile(fh:read("a")))
- fh:close()
- -- HTML generator module.
- local M = {
- res_dir = pkar.config.htmlgen.out_dir .. "/res",
- asset_dir = pkar.config.htmlgen.out_dir .. "/assets",
- media_dir = pkar.config.htmlgen.out_dir .. "/media",
- }
- local function get_breadcrumbs(mconf)
- -- Breadcrumbs, from top class to current class.
- -- Also verify if it's a File subclass.
- local breadcrumbs = {}
- for i = 1, #mconf.lineage do
- breadcrumbs[i] = {
- mconf.lineage[i],
- model.models[mconf.lineage[i]].label
- }
- end
- return breadcrumbs
- end
- local function generate_dres(s, mconf)
- local gr = graph.new(pkar.store, term.DEFAULT_CTX)
- local dmd = {}
- local rel = {}
- local children = {}
- -- Metadata
- local attrs = gr:connections(s, term.LINK_OUTBOUND)
- --require "debugger"()
- for p, ots in pairs(attrs) do
- local fname = nsm.denormalize_uri(p.data)
- p_label = ((mconf.properties or NT)[fname] or NT).label
- -- RDF types are shown in in breadcrumbs.
- if fname == "rdf:type" then goto skip
- elseif ((mconf.properties or NT)[fname] or NT).type == "rel" then
- -- Relationship.
- rel[fname] = {label = p_label, uri = fname}
- for o in pairs(ots) do table.insert(dmd[fname], o.data) end
- elseif fname == "pas:first" then
- -- Build a linked list for every first found.
- local p = term.new_iriref_ns("pas:next")
- local dc_title = term.new_iriref_ns("dc:title")
- local tn_p = term.new_iriref_ns("pas:thumbnail")
- for o in pairs(ots) do
- -- Loop through all first children.
- local node_uri = o
- logger:debug("local node_uri", node_uri.data)
- local ll = {}
- while node_uri do
- -- Loop trough all next nodes for each first child.
- local el_gr = graph.get(node_uri, pkar.store)
- table.insert(ll, {
- uri = node_uri,
- label = gr:attr(node_uri, dc_title)[1],
- tn = gr:attr(node_uri, tn_p)[1],
- })
- local next_attr = gr:attr(node_uri, p)
- node_uri = next(next_attr) -- There can only be one "next"
- end
- table.insert(children, ll)
- end
- elseif fname == "pas:next" then
- -- Sibling.
- for o in pairs(ots) do ls_next = o.data break end
- else
- -- Descriptive metadata.
- dmd[fname] = {label = p_label, uri = fname}
- -- TODO differentiate term types
- for o in pairs(ots) do table.insert(dmd[fname], o.data) end
- table.sort(dmd[fname])
- end
- ::skip::
- end
- table.sort(dmd)
- table.sort(rel)
- table.sort(children)
- logger:debug("Lineage:", pp.write(mconf.lineage))
- logger:debug("DMD:", pp.write(dmd))
- logger:debug("REL:", pp.write(rel))
- logger:debug("Children:", pp.write(children))
- logger:debug("Breadcrumbs:", pp.write(get_breadcrumbs(mconf)))
- out_html = dres_tpl({
- site_title = pkar.config.site.title or pkar.default_title,
- title = ((dmd["dc:title"] or NT)[1] or NT) or s.data,
- mconf = mconf,
- uri = s,
- dmd = dmd,
- rel = rel,
- children = children,
- ls_next = ls_next,
- breadcrumbs = get_breadcrumbs(mconf),
- deliverable = deliverable,
- })
- local res_id = s.data:gsub(nsm.get_ns("par"), "")
- local ofh = assert(io.open(string.format(
- "%s/%s.html", M.res_dir, res_id), "w"))
- ofh:write(out_html)
- ofh:close()
- end
- local function generate_ores(s, mconf)
- local gr = graph.new(pkar.store, term.DEFAULT_CTX)
- local techmd = {}
- local rel = {}
- -- Metadata
- local attrs = gr:connections(s, term.LINK_OUTBOUND)
- for p, ots in pairs(attrs) do
- local fname = nsm.denormalize_uri(p.data)
- p_label = ((mconf.properties or NT)[fname] or NT).label
- -- RDF types are shown in in breadcrumbs.
- if fname == "rdf:type" then goto skip
- elseif ((mconf.properties or NT)[fname] or NT).type == "rel" then
- -- Relationship.
- rel[fname] = {label = p_label, uri = fname}
- for o in pairs(ots) do table.insert(techmd[fname], o.data) end
- elseif fname == "pas:next" then
- -- Sibling.
- for o in pairs(ots) do ls_next = o.data break end
- else
- -- Descriptive metadata.
- techmd[fname] = {label = p_label, uri = fname}
- -- TODO differentiate term types
- for o in pairs(ots) do table.insert(techmd[fname], o.data) end
- table.sort(techmd[fname])
- end
- ::skip::
- end
- table.sort(techmd)
- table.sort(rel)
- logger:debug("Lineage:", pp.write(mconf.lineage))
- logger:debug("Breadcrumbs:", pp.write(get_breadcrumbs(mconf)))
- logger:debug("techmd:", pp.write(techmd))
- logger:debug("REL:", pp.write(rel))
- --require "debugger"()
- -- Transform and move media assets.
- logger:info("Transforming resource file.")
- local res_path = techmd["pas:path"]
- if not res_path then error("No file path for File resource!") end
- local txconf = (mconf.transformers or NT).deliverable or {fn = "copy"}
- local dest_fname = plpath.basename(res_path[1])
- if txconf.ext then
- dest_fname = plpath.splitext(dest_fname) .. txconf.ext
- end
- dest = M.asset_dir .. "/" .. dest_fname
- assert(transformers[txconf.fn](
- res_path[1], dest, table.unpack(txconf or NT)))
- local deliverable = dest:gsub(pkar.config.htmlgen.out_dir, "..")
- logger:info("Deliverable: ", dest)
- out_html = ores_tpl({
- site_title = pkar.config.site.title or pkar.default_title,
- fname = plpath.basename(techmd["pas:sourcePath"][1]),
- mconf = mconf,
- uri = s,
- techmd = techmd,
- rel = rel,
- ls_next = ls_next,
- breadcrumbs = get_breadcrumbs(mconf),
- deliverable = deliverable,
- })
- local res_id = s.data:gsub(nsm.get_ns("par"), "")
- local ofh = assert(io.open(string.format(
- "%s/%s.html", M.res_dir, res_id), "w"))
- ofh:write(out_html)
- ofh:close()
- end
- M.generate_resource = function(s)
- local gr = graph.new(pkar.store, term.DEFAULT_CTX)
- local res_type
- local type_attr = gr:attr(
- s, term.new_iriref_ns("pas:contentType"))
- res_type = next(type_attr).data
- local mconf = model.models[res_type]
- if mconf.types["pas:File"] then return generate_ores(s, mconf)
- else return generate_dres(s, mconf) end
- end
- M.generate_resources = function()
- dir.rmtree(M.res_dir)
- dir.makepath(M.res_dir)
- dir.rmtree(M.asset_dir)
- dir.makepath(M.asset_dir)
- dir.rmtree(M.media_dir)
- dir.makepath(M.media_dir)
- local gr = graph.new(pkar.store, term.DEFAULT_CTX)
- local subjects = gr:unique_terms(triple.POS_S)
- -- TODO parallelize
- for s in pairs(subjects) do M.generate_resource(s) end
- end
- M.generate_idx = function()
- local obj_idx = {}
- local gr = graph.new(pkar.store, term.DEFAULT_CTX)
- -- Get all subject of type: Artifact.
- s_ts = gr:term_set(
- pkar.RDF_TYPE, triple.POS_P,
- term.new_iriref_ns("pas:Artifact"), triple.POS_O
- )
- for s in pairs(s_ts) do
- local s_label = nsm.denormalize_uri(s.data)
- local titles = gr:attr(s, term.new_iriref_ns("dc:title"))
- local obj = {title = next(titles)}
- if obj.title then obj_idx[s_label] = obj end
- end
- logger:debug(pp.write(obj_idx))
- out_html = idx_tpl({
- title = pkar.config.site.title or pkar.default_title,
- nsm = nsm,
- obj_idx = obj_idx,
- })
- local idx_path = pkar.config.htmlgen.out_dir .. "/index.html"
- local ofh = assert(io.open(idx_path, "w"))
- logger:debug("Writing info at ", idx_path)
- ofh:write(out_html)
- ofh:close()
- end
- return M
|