Browse Source

Add collections.

scossu 16 hours ago
parent
commit
a97a8479de

BIN
bin/darkhttpd


+ 35 - 0
config/model/typedef/brick.lua

@@ -0,0 +1,35 @@
+--[[
+Brick content type: a building block for structures.
+
+Resources of this type may be used to build logical structures implemented
+as linked lists.
+
+The `pas:first` property points to the first child in a",
+linked list nested inside the current resource. There may be any number of
+linked list heads under the same Part, or none.
+
+The `pas:next` property points to the next sibling in a linked list. The",
+last item in a list is identified by the lack of this property.
+
+In a more complex hierarchy, any given Part may have both "first" and
+"next" properties.
+--]]
+
+return {
+    id = "pas:Brick",
+    label = "Brick",
+
+    broader = "pas:Anything",
+
+    properties = {
+        ["pas:first"] = {
+            label = "First child",
+            type = "resource",
+        },
+        ["pas:next"] = {
+            label = "Next sibling",
+            type = "resource",
+            max_cardinality = 1,
+        }
+    }
+}

+ 17 - 0
config/model/typedef/collection.lua

@@ -0,0 +1,17 @@
+return {
+    id = "pas:Collection",
+    label = "Collection",
+
+    broader = "pas:Brick",
+
+    properties = {
+        ["pas:first"] = {
+            range = {["pas:Collection"] = true, ["pas:Artifact"] = true},
+        },
+        ["pas:next"] = {
+            range = {["pas:Collection"] = true, ["pas:Artifact"] = true},
+        }
+    }
+}
+
+

+ 2 - 22
config/model/typedef/part.lua

@@ -1,36 +1,16 @@
---[[
-Part content type = "a logical subdivision within an artifact.",
-Resources of this type may be used to build logical structures implemented
-as linked lists.
-
-The `pas:first` property points to the first child in a",
-linked list nested inside the current resource. There may be any number of
-linked list heads under the same Part, or none.
-
-The `pas:next` property points to the next sibling in a linked list. The",
-last item in a list is identified by the lack of this property.
-
-In a more complex hierarchy, any given Part may have both "first" and
-"next" properties.
---]]
-
 return {
     id = "pas:Part",
     label = "Part",
 
-    broader = "pas:Anything",
+    broader = "pas:Brick",
 
     properties = {
         ["pas:first"] = {
-            label = "First child",
-            type = "resource",
             range = {["pas:Part"] = true, ["pas:File"] = true},
         },
         ["pas:next"] = {
-            label = "Next sibling",
-            type = "resource",
             range = {["pas:Part"] = true},
-            max_cardinality = 1,
         }
     }
 }
+

+ 2 - 0
pocket_archive-scm-1.rockspec

@@ -20,6 +20,7 @@ description = {
 dependencies = {
    "lua >= 5.4",
 
+   "cli",
    "csv",
    "datafile",
    "etlua",
@@ -43,5 +44,6 @@ build = {
             "ext/monocypher/lua_monocypher.c",
         },
     },
+    install = {bin = {pkar = "pkar.lua"}},
     copy_directories = {"config", "doc", "templates"},
 }

+ 1 - 1
scratch.lua

@@ -14,7 +14,7 @@ local hgen = require "pocket_archive.html_generator"
 _ = pkar.reset_store
 
 sip = sub.generate_sip(
-    "test/sample_submission/postcard-bag/data/pkar_submission.csv")
+    "test/sample_submission/demo01/pkar_submission.csv")
 
 sub.deposit(sip)
 --]]

+ 0 - 6
src/core.lua

@@ -25,12 +25,6 @@ local store_id = "file://" .. (os.getenv("PKAR_BASE") or config.fs.dres_path)
 for pfx, ns in pairs(config.namespace) do nsm.add(pfx, ns) end
 
 
--- Initialize the store. `clear` wipes all data. YOU HAVE BEEN WARNED!
-local store_init = function(clear)
-    return store.new(store.MDB, store_id, clear)
-end
-
-
 local M = {
     -- Project root path.
     root = root_path,

+ 8 - 2
src/html_generator.lua

@@ -166,7 +166,7 @@ local function generate_dres(s, mconf)
     logger:debug("Breadcrumbs:", pp.write(get_breadcrumbs(mconf)))
 
     out_html = templates.dres.data({
-        webroot = M.webroot,
+        --webroot = M.webroot,
         site_title = pkar.config.site.title or pkar.default_title,
         title = title or s.data,
         head_tpl = templates.head.data,
@@ -260,7 +260,7 @@ local function generate_ores(s, mconf)
     end
 
     out_html = templates.ores.data({
-        webroot = M.webroot,
+        --webroot = M.webroot,
         site_title = pkar.config.site.title or pkar.default_title,
         fname = plpath.basename(techmd["pas:sourcePath"][1]),
         head_tpl = templates.head.data,
@@ -338,6 +338,11 @@ M.generate_res_idx = function(s, mconf)
 end
 
 
+M.generate_ll = function(s)
+
+end
+
+
 M.generate_resource = function(s)
     local res_type
     _, res_type = next(gr:attr(s, pkar.CONTENT_TYPE_P))
@@ -422,6 +427,7 @@ M.generate_idx = function()
     out_html = templates.idx.data({
         webroot = M.webroot,
         title = pkar.config.site.title or pkar.default_title,
+        site_title = pkar.config.site.title or pkar.default_title,
         head_tpl = templates.head.data,
         header_tpl = templates.header.data,
         nsm = nsm,

+ 4 - 3
src/submission.lua

@@ -113,7 +113,7 @@ M.generate_sip = function(path)
     for row in sub_data:lines() do
         logger:debug("Row path: ", row["pas:sourcePath"])
         logger:debug("Parsing row:", pp.write(row))
-        if row["pas:sourcePath"] ~= "" then
+        if #row["pas:sourcePath"] > 0 then
             i = i + 1
             logger:info(
                     ("Processing LL resource #%d at row #%d.")
@@ -121,10 +121,11 @@ M.generate_sip = function(path)
             prev_path = row["pas:sourcePath"]
             -- New row.
             local id
-            if row.id then
+            if #row.id > 0 then
                 id = "par:" .. row.id
                 row.id = nil
             else id = "par:" .. M.idgen() end
+
             sip[i] = {id = id}
             for k, v in pairs(row) do
                 if v == "" then goto cont1 end  -- skip empty strings.
@@ -322,7 +323,7 @@ M.deposit = function(sip)
 
             -- Copy thumbnail if existing.
             if rsrc["pas:thumbnail"] then
-                src_path = rsrc["pas:thumbnail"] 
+                src_path = rsrc["pas:thumbnail"]
                 out_path = plpath.join(
                         out_dir, plpath.basename(src_path))
                 logger:debug(("Moving file %s to %s"):format(src_path, out_path))

+ 4 - 1
src/validator.lua

@@ -20,7 +20,10 @@ M.validate = function(gr, s)
     local rmod = model.parse_model(ctype.data)
     if not rmod then error("No type definition for ", ctype.data) end
 
-    local report = {notices = {}, warnings = {}, errors = {}}
+    local report = {
+        id = s.data, ctype = ctype.data,
+        notices = {}, warnings = {}, errors = {}
+    }
 
     for fname, rules in pairs(rmod.properties or NT) do
         local values

+ 0 - 0
test/sample_submission/postcard-bag/data/0001/0001-back/567890.jpg → test/sample_submission/demo01/demo_collection/demo_postcard/back/567890.jpg


+ 0 - 0
test/sample_submission/postcard-bag/data/0001/0001-front/54321.jpg → test/sample_submission/demo01/demo_collection/demo_postcard/front/54321.jpg


+ 0 - 0
test/sample_submission/postcard-bag/data/0006/0685_04.jpg → test/sample_submission/demo01/demo_collection/single_image/0685_04.jpg


+ 15 - 0
test/sample_submission/demo01/pkar_submission.csv

@@ -0,0 +1,15 @@
+"pas:sourcePath","id","pas:contentType","dc:title","dc:alternative","dc:description","dc:coverage","dc:date"
+"demo_collection",,"Collection","My Demo Collection","My Beautiful  Collection","Some random stuff from my hard drive.",,2025-07-28
+,,,,"My Aunt's Beautiful Collection","Old B/W photos.",,
+,,,,,"More description to demonstrate how multi-valued fields are filled.",,
+,,,,,"""id"" fields have been left blank to let the system auto-generate them.",,
+"demo_collection/demo_postcard",,"Postcard","Example Postcard","This is an alternative label","Note that recto and verso representations have been named front and back, to emphasize that the ordering is not alphabetical.",,2025-06-10
+"demo_collection/demo_postcard/front",,"Part","Recto",,"An idle, windy day in Capo Falcone, Sardinia","Capo Falcone (SS) Italy",2004-04-12
+,,,,,,"https://www.openstreetmap.org/#map=18/40.9696884/8.2020324",
+"demo_collection/demo_postcard/front/54321.jpg",,"StillImageFile",,,,,
+"demo_collection/demo_postcard/back",,"Part","Verso",,"Wandering around somewhere in Tirana, 2006.","Tirana, Albania",2006-05-05
+,,,,,,"https://geohack.toolforge.org/geohack.php?params=41.32888888888889_N_19.817777777777778_E_globe:earth&language=en",
+"demo_collection/demo_postcard/back/567890.jpg",,"StillImageFile",,,,,
+"demo_collection/single_image",,"StillImage","Preparing kebab at Aqil's during curfew.",,"Nothing much to do under curfew but cooking, eating, singing, dancing, playing cards, smoking water pipe, and occasionally playing soccer in the street when the Merkava didn't get in the way.","Nablus, Palestine",2002-08-16
+,,,,,,"https://www.openstreetmap.org/#map=19/32.221597/35.260929",
+"demo_collection/single_image/0685_04.jpg",,"StillImageFile",,,,,

+ 0 - 2
test/sample_submission/postcard-bag/bagit.txt

@@ -1,2 +0,0 @@
-BagIt-Version: 1.0
-Tag-File-Character-Encoding: UTF-8

+ 0 - 10
test/sample_submission/postcard-bag/data/pkar_submission.csv

@@ -1,10 +0,0 @@
-"pas:sourcePath","id","pas:contentType","dc:title","dc:alternative","dc:description"
-0001,00001,"Postcard","Example Postcard","This is an alternative label","Note that recto and verso representations have been named front and back, to emphasize that the ordering is not alphabetical."
-,,,,"And this is another alternative label","Second description."
-,,,,"Yet another alt label.",
-"0001/0001-front",00002,"Part","Recto",,
-"0001/0001-front/54321.jpg",00003,"StillImageFile",,,
-"0001/0001-back",00004,"Part","Verso",,
-"0001/0001-back/567890.jpg",00005,"StillImageFile",,,
-0006,00006,"StillImage","Single Image",,"Preparing kebab at Aqil's during curfew."
-0006/0685_04.jpg,00007,"StillImageFile","B/W scan of physical photo",,

+ 0 - 3
test/sample_submission/postcard-bag/manifest-md5.txt

@@ -1,3 +0,0 @@
-6d3e96779489ec928bd54b716ee7b9d3  data/submission.csv
-9db0de0b0c6b40be9cad3828946e8fd0  data/12345/12345-front/54321.jpg
-ccc13ac50e926995da704b51ee4948c0  data/12345/12345-back/567890.jpg

+ 0 - 5
test/sample_submission/postcard-bag/manifest-sha512.txt

@@ -1,5 +0,0 @@
-2b6263572b781ba987778d362969d5e62729fe7cb94694a379ba7af4ea8311d0b5dac499dd0da7a7c5162979ccbf5f943cbb3ee91c2794ab09924969b08ec66c  data/submission.csv
-da8e36bb2fc58834acc535db182b1b1350317a15440e22238a0ba050c2860fb2b4fc2ce7f1c1fa95758d9cdb365913948b7ab26083f1e8325540159b24c6973d  data/DRS_12345/12345-front/54321.TIF
-c6a2dfbafcf8157f76dfca5e65f7bc24e14c52125b8e7b73df0995c9c714bd3f762e4d6692e953bac4aec7ada4ff92ecf5a0317812b036b701843597fb6ac7c6  data/DRS_12345/12345-front/54321.jpg
-ccedf8f20a05a0d34f67965e0858622867bc442a543f3183b7a12c466159805ddd0a3e6484a465344d2c52049ed68312401c62bfa8c14aaeace40cff8a803a4c  data/DRS_12345/12345-back/567890.jpg
-8d663fcd34934399dfb2df28194d9795c97e9c02de4eef709d9b77b572690f04a74a2f88175a35d24e89a08a7839eb2ddb595e0f09804ebe5e523add935ec3db  data/DRS_12345/12345-back/567890.TIF