lib/dtutils/string - Added categories to substitution functionality.

This makes it feature complete.  Some substitutions
                         aren't implemented since the current use case for
                         this is creating export directories.  The results
                         from the not implemented substitutions wouldn't
                         work well in path/file names (such as comma
                         separated lists or newlines).
This commit is contained in:
Bill Ferguson
2024-05-18 21:59:11 -04:00
parent ad10542b30
commit 3e2afffed8

View File

@@ -477,6 +477,7 @@ function dtutils_string.get_filetype(str)
end
local substitutes = {}
local category_substitutes = {}
-- - - - - - - - - - - - - - - - - - - - - - - -
-- C O N S T A N T S
@@ -488,7 +489,7 @@ local PLACEHOLDERS = {"ROLL.NAME",
"FILE.EXTENSION",
"ID",
"VERSION",
"VERSION.IF.MULTI", -- Not Implemented
"VERSION.IF.MULTI",
"VERSION.NAME",
"DARKTABLE.VERSION",
"DARKTABLE.NAME", -- Not Implemented
@@ -504,14 +505,20 @@ local PLACEHOLDERS = {"ROLL.NAME",
"WIDTH.MAX", -- Not Implemented
"HEIGHT.MAX", -- Not Implemented
"YEAR",
"YEAR.SHORT",
"MONTH",
"MONTH.LONG",
"MONTH.SHORT",
"DAY",
"HOUR",
"MINUTE",
"SECOND",
"MSEC",
"EXIF.YEAR",
"EXIF.YEAR.SHORT",
"EXIF.MONTH",
"EXIF.MONTH.LONG",
"EXIF.MONTH.SHORT",
"EXIF.DAY",
"EXIF.HOUR",
"EXIF.MINUTE",
@@ -523,17 +530,22 @@ local PLACEHOLDERS = {"ROLL.NAME",
"EXIF.EXPOSURE",
"EXIF.EXPOSURE.BIAS",
"EXIF.APERTURE",
"EXIF.CROP.FACTOR",
"EXIF.FOCAL.LENGTH",
"EXIF.FOCAL.LENGTH.EQUIV", -- Not Implemented
"EXIF.FOCUS.DISTANCE",
"IMAGE.EXIF", -- Not Implemented
"LONGITUDE",
"LATITUDE",
"ALTITUDE",
"ELEVATION",
"GPS.LOCATION", -- Not Implemented
"STARS",
"RATING.ICONS", -- Not Implemented
"LABELS",
"LABELS.ICONS", -- Not Implemented
"MAKER",
"MODEL",
"LENS",
"TITLE",
"DESCRIPTION",
"CREATOR",
@@ -547,7 +559,7 @@ local PLACEHOLDERS = {"ROLL.NAME",
"FOLDER.DESKTOP",
"OPENCL.ACTIVATED", -- Not Implemented
"USERNAME",
"NL",
"NL", -- Not Implemented
"JOBCODE" -- Not Implemented
}
@@ -560,14 +572,19 @@ local DESKTOP = HOME .. PS .. "Desktop"
local function get_colorlabels(image)
local old_log_level = log.log_level()
log.log_level(dtutils_string.log_level)
local colorlabels = {}
if image.red then table.insert(colorlabels, "red") end
if image.yellow then table.insert(colorlabels, "yellow") end
if image.green then table.insert(colorlabels, "green") end
if image.blue then table.insert(colorlabels, "blue") end
if image.purple then table.insert(colorlabels, "purple") end
local labels = #colorlabels == 1 and colorlabels[1] or du.join(colorlabels, ",")
local labels = #colorlabels == 1 and colorlabels[1] or du.join(colorlabels, "_")
log.log_level(old_log_level)
return labels
end
@@ -575,9 +592,10 @@ dtutils_string.libdoc.functions["build_substitution_list"] = {
Name = [[build_substitution_list]],
Synopsis = [[build a list of variable substitutions]],
Usage = [[local ds = require "lib/dtutils.string"
ds.build_substitution_list(image, sequence, [username], [pic_folder], [home], [desktop])
ds.build_substitution_list(image, sequence, variable_string, [username], [pic_folder], [home], [desktop])
image - dt_lua_image_t - the image being processed
sequence - integer - the sequence number of the image
variable_string - string - the substitution variable string
[username] - string - optional - user name. Will be determined if not supplied
[pic_folder] - string - optional - pictures folder name. Will be determined if not supplied
[home] - string - optional - home directory. Will be determined if not supplied
@@ -594,7 +612,46 @@ dtutils_string.libdoc.functions["build_substitution_list"] = {
Copyright = [[]],
}
function dtutils_string.build_substition_list(image, sequence, username, pic_folder, home, desktop)
local function build_category_substitution_list(image, variable_string)
-- scan the variable string for CATEGORYn(tag name) entries and build the substitutions
local old_log_level = log.log_level()
-- log.log_level(dtutils_string.log_level)
--log.log_level(log.debug)
for match in string.gmatch(variable_string, "%$%(.-%)?%)") do
log.msg(log.info, "match is " .. match)
local var = string.match(match, "%$%((.-%)?)%)")
log.msg(log.info, "var is " .. var)
if string.match(var, "CATEGORY%d") then
local element, tag = string.match(var, "CATEGORY(%d)%((.-)%)")
element = element + 1
log.msg(log.debug, "element is " .. element .. " and tag is " .. tag)
local tags = image:get_tags()
log.msg(log.debug, "got " .. #tags .. " from image " .. image.filename)
for _, image_tag in ipairs(tags) do
log.msg(log.debug, "checking tag " .. image_tag.name)
if string.match(image_tag.name, tag) then
parts = du.split(image_tag.name, "|")
substitutes[var] = parts[element]
log.msg(log.info, "set substitute for " .. var .. " to " .. parts[element])
log.msg(log.info, "double check substitutes[" .. var .. "] is " .. substitutes[var])
end
end
end
end
-- scan the variable string looking for CATEGORYn
-- retrieve the tag and compute the replacement string
-- add the found string as a PLACEHOLDER
-- add the replacement to the corresponding spot in the replacement table
log.log_level(old_log_level)
end
local function exiftime2systime(exiftime)
local yr,mo,dy,h,m,s = string.match(exiftime, "(%d-):(%d-):(%d-) (%d-):(%d-):(%d+)")
return(os.time{year=yr, month=mo, day=dy, hour=h, min=m, sec=s})
end
function dtutils_string.build_substition_list(image, sequence, variable_string, username, pic_folder, home, desktop)
-- build the argument substitution list from each image
local old_log_level = log.log_level()
@@ -606,6 +663,8 @@ function dtutils_string.build_substition_list(image, sequence, username, pic_fol
end
local datetime = os.date("*t")
local long_month = os.date("%B")
local short_month = os.date("%b")
local user_name = username or USER
local pictures_folder = pic_folder or PICTURES
local home_folder = home or HOME
@@ -629,7 +688,8 @@ function dtutils_string.build_substition_list(image, sequence, username, pic_fol
dtutils_string.get_filetype(image.filename),-- FILE.EXTENSION
image.id, -- ID
image.duplicate_index, -- VERSION
"", -- VERSION.IF_MULTI
-- #image:get_group_members() > 1 and image.duplicate_index or "", -- VERSION.IF_MULTI
"", -- VERSION.IF_MULTI
image.version_name, -- VERSION.NAME
dt.configuration.version, -- DARKTABLE.VERSION
"", -- DARKTABLE.NAME
@@ -645,57 +705,71 @@ function dtutils_string.build_substition_list(image, sequence, username, pic_fol
"", -- WIDTH.MAX
"", -- HEIGHT.MAX
string.format("%4d", datetime.year), -- YEAR
string.sub(datetime.year, 3), -- YEAR.SHORT
string.format("%02d", datetime.month), -- MONTH
long_month, -- MONTH.LONG
short_month, -- MONTH.SHORT
string.format("%02d", datetime.day), -- DAY
string.format("%02d", datetime.hour), -- HOUR
string.format("%02d", datetime.min), -- MINUTE
string.format("%02d", datetime.sec), -- SECOND
"", -- MSEC
0, -- MSEC
eyear, -- EXIF.YEAR
string.sub(eyear, 3), -- EXIF.YEAR.SHORT
emon, -- EXIF.MONTH
os.date("%B", exiftime2systime(image.exif_datetime_taken)), -- EXIF.MONTH.LONG
os.date("%b", exiftime2systime(image.exif_datetime_taken)), -- EXIF.MONTH.SHORT
eday, -- EXIF.DAY
ehour, -- EXIF.HOUR
emin, -- EXIF.MINUTE
esec, -- EXIF.SECOND
emsec, -- EXIF.MSEC
"", -- EXIF.DATE.REGIONAL
"", -- EXIF.TIME.REGIONAL
"", -- EXIF.DATE.REGIONAL - wont be implemented
"", -- EXIF.TIME.REGIONAL - wont be implemented
string.format("%d", image.exif_iso), -- EXIF.ISO
string.format("1/%.0f", 1./image.exif_exposure), -- EXIF.EXPOSURE
string.format("%.0f", 1./image.exif_exposure), -- EXIF.EXPOSURE
image.exif_exposure_bias, -- EXIF.EXPOSURE.BIAS
string.format("%.01f", image.exif_aperture), -- EXIF.APERTURE
string.format("%.01f", image.exif_crop),-- EXIF.CROP_FACTOR
string.format("%.0f", image.exif_focal_length), -- EXIF.FOCAL.LENGTH
string.format("%.0f", image.exif_focal_length * image.exif_crop), -- EXIF.FOCAL.LENGTH.EQUIV
image.exif_focus_distance, -- EXIF.FOCUS.DISTANCE
"", -- IMAGE.EXIF
image.longitude or "", -- LONGITUDE
image.latitude or "", -- LATITUDE
image.elevation or "", -- ALTITUDE
image.elevation or "", -- ELEVATION
"", -- GPS.LOCATION - wont be implemented
image.rating, -- STARS
"", -- RATING.ICONS
"", -- RATING.ICONS - wont be implemented
labels, -- LABELS
"", -- LABELS.ICONS
"", -- LABELS.ICONS - wont be implemented
image.exif_maker, -- MAKER
image.exif_model, -- MODEL
image.exif_lens, -- LENS
image.title, -- TITLE
image.description, -- DESCRIPTION
image.creator, -- CREATOR
image.publisher, -- PUBLISHER
image.rights, -- RIGHTS
"", -- TAGS
"", -- CATEGORYn
"", -- SIDECAR.TXT
"", -- TAGS - wont be implemented
"", -- CATEGORY
"", -- SIDECAR.TXT - wont be implemented
pictures_folder, -- FOLDER.PICTURES
home_folder, -- FOLDER.HOME
desktop_folder, -- FOLDER.DESKTOP
"", -- OPENCL.ACTIVATED
"", -- OPENCL.ACTIVATED - wont be implemented
user_name, -- USERNAME
"\n", -- NL
"" -- JOBCODE
"", -- NL - wont be implemented
"" -- JOBCODE - wont be implemented
}
for i = 1, #PLACEHOLDERS, 1 do
substitutes[PLACEHOLDERS[i]] = replacements[i]
log.msg(log.info, "setting " .. PLACEHOLDERS[i] .. " to " .. tostring(replacements[i]))
end
build_category_substitution_list(image, variable_string)
log.log_level(old_log_level)
end
@@ -712,86 +786,105 @@ end
local function treat(var_string)
local old_log_level = log.log_level()
log.log_level(dtutils_string.log_level)
--log.log_level(dtutils_string.log_level)
--log.log_level(log.info)
local ret_val = ""
-- remove the var from the string
local var = string.match(var_string, "[%a%._]+")
var = check_legacy_vars(var)
log.msg(log.info, "var_string is " .. tostring(var_string) .. " and var is " .. tostring(var))
ret_val = substitutes[var]
if not ret_val then
if string.match(var_string, "CATEGORY%d") then
log.msg(log.info, "substituting for " .. var_string)
ret_val = substitutes[var_string]
log.msg(log.info, "ret_val is " .. ret_val)
else
ret_val = substitutes[var]
end
local valid_var = false
if ret_val then
valid_var = true
--elseif string.match(var, "CATEGORY%d") then
--valid_var = true
end
if not valid_var then
log.msg(log.error, "variable " .. var .. " is not an allowed variable, returning empty value")
log.log_level(old_log_level)
return ""
end
local args = string.gsub(var_string, var, "")
log.msg(log.info, "args is " .. tostring(args))
if string.len(args) > 0 then
if string.match(args, '^%^%^') then
ret_val = string.upper(ret_val)
elseif string.match(args, "^%^") then
ret_val = string.gsub(ret_val, "^%a", string.upper, 1)
elseif string.match(args, "^,,") then
ret_val = string.lower(ret_val)
elseif string.match(args, "^,") then
ret_val = string.gsub(ret_val, "^%a", string.lower, 1)
elseif string.match(args, "^:%-?%d+:%-?%d+") then
local soffset, slen = string.match(args, ":(%-?%d+):(%-?%d+)")
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
if tonumber(soffset) >= 0 then
soffset = soffset + 1
if string.match(var, "CATEGORY%d") then
ret_val = process_category(image, var, var_string)
else
local args = string.gsub(var_string, var, "")
log.msg(log.info, "args is " .. tostring(args))
if string.len(args) > 0 then
if string.match(args, '^%^%^') then
ret_val = string.upper(ret_val)
elseif string.match(args, "^%^") then
ret_val = string.gsub(ret_val, "^%a", string.upper, 1)
elseif string.match(args, "^,,") then
ret_val = string.lower(ret_val)
elseif string.match(args, "^,") then
ret_val = string.gsub(ret_val, "^%a", string.lower, 1)
elseif string.match(args, "^:%-?%d+:%-?%d+") then
local soffset, slen = string.match(args, ":(%-?%d+):(%-?%d+)")
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
if tonumber(soffset) >= 0 then
soffset = soffset + 1
end
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
if tonumber(soffset) < 0 and tonumber(slen) < 0 then
local temp = soffset
soffset = slen
slen = temp
end
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
ret_val = string.sub(ret_val, soffset, slen)
log.msg(log.info, "ret_val is " .. ret_val)
elseif string.match(args, "^:%-?%d+") then
local soffset= string.match(args, ":(%-?%d+)")
if tonumber(soffset) >= 0 then
soffset = soffset + 1
end
ret_val = string.sub(ret_val, soffset, -1)
elseif string.match(args, "^-%$%(.-%)") then
local replacement = string.match(args, "-%$%(([%a%._]+)%)")
replacement = check_legacy_vars(replacement)
if string.len(ret_val) == 0 then
ret_val = substitutes[replacement]
end
elseif string.match(args, "^-.+$") then
local replacement = string.match(args, "-(.+)$")
if string.len(ret_val) == 0 then
ret_val = replacement
end
elseif string.match(args, "^+.+") then
local replacement = string.match(args, "+(.+)")
if string.len(ret_val) > 0 then
ret_val = replacement
end
elseif string.match(args, "^#.+") then
local pattern = string.match(args, "#(.+)")
log.msg(log.info, "pattern to remove is " .. tostring(pattern))
ret_val = string.gsub(ret_val, "^" .. dtutils_string.sanitize_lua(pattern), "")
elseif string.match(args, "^%%.+") then
local pattern = string.match(args, "%%(.+)")
ret_val = string.gsub(ret_val, pattern .. "$", "")
elseif string.match(args, "^//.-/.+") then
local pattern, replacement = string.match(args, "//(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern, replacement)
elseif string.match(args, "^/#.+/.+") then
local pattern, replacement = string.match(args, "/#(.+)/(.+)")
ret_val = string.gsub(ret_val, "^" .. pattern, replacement, 1)
elseif string.match(args, "^/%%.-/.+") then
local pattern, replacement = string.match(args, "/%%(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern .. "$", replacement)
elseif string.match(args, "^/.-/.+") then
log.msg(log.info, "took replacement branch")
local pattern, replacement = string.match(args, "/(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern, replacement, 1)
end
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
if tonumber(soffset) < 0 and tonumber(slen) < 0 then
local temp = soffset
soffset = slen
slen = temp
end
log.msg(log.info, "soffset is " .. soffset .. " and slen is " .. slen)
ret_val = string.sub(ret_val, soffset, slen)
log.msg(log.info, "ret_val is " .. ret_val)
elseif string.match(args, "^:%-?%d+") then
local soffset= string.match(args, ":(%-?%d+)")
if tonumber(soffset) >= 0 then
soffset = soffset + 1
end
ret_val = string.sub(ret_val, soffset, -1)
elseif string.match(args, "^-%$%(.-%)") then
local replacement = string.match(args, "-%$%(([%a%._]+)%)")
replacement = check_legacy_vars(replacement)
if string.len(ret_val) == 0 then
ret_val = substitutes[replacement]
end
elseif string.match(args, "^-.+$") then
local replacement = string.match(args, "-(.+)$")
if string.len(ret_val) == 0 then
ret_val = replacement
end
elseif string.match(args, "^+.+") then
local replacement = string.match(args, "+(.+)")
if string.len(ret_val) > 0 then
ret_val = replacement
end
elseif string.match(args, "^#.+") then
local pattern = string.match(args, "#(.+)")
log.msg(log.info, "pattern to remove is " .. tostring(pattern))
ret_val = string.gsub(ret_val, "^" .. dtutils_string.sanitize_lua(pattern), "")
elseif string.match(args, "^%%.+") then
local pattern = string.match(args, "%%(.+)")
ret_val = string.gsub(ret_val, pattern .. "$", "")
elseif string.match(args, "^//.-/.+") then
local pattern, replacement = string.match(args, "//(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern, replacement)
elseif string.match(args, "^/#.+/.+") then
local pattern, replacement = string.match(args, "/#(.+)/(.+)")
ret_val = string.gsub(ret_val, "^" .. pattern, replacement, 1)
elseif string.match(args, "^/%%.-/.+") then
local pattern, replacement = string.match(args, "/%%(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern .. "$", replacement)
elseif string.match(args, "^/.-/.+") then
log.msg(log.info, "took replacement branch")
local pattern, replacement = string.match(args, "/(.-)/(.+)")
ret_val = string.gsub(ret_val, pattern, replacement, 1)
end
end
log.log_level(old_log_level)
@@ -848,9 +941,10 @@ dtutils_string.libdoc.functions["clear_substitute_list"] = {
function dtutils_string.clear_substitute_list()
local old_log_level = log.log_level()
log.log_level(dtutils_string.log_level)
for i = 1, #PLACEHOLDERS, 1 do
substitutes[PLACEHOLDERS[i]] = nil
end
substitutes = {}
category_substitutes = {}
log.log_level(old_log_level)
end
@@ -858,9 +952,10 @@ dtutils_string.libdoc.functions["substitute"] = {
Name = [[substitute]],
Synopsis = [[Check if a string has been sanitized]],
Usage = [[local ds = require "lib/dtutils.string"
ds.substitute(image, sequence, [username], [pic_folder], [home], [desktop])
ds.substitute(image, sequence, variable_string, [username], [pic_folder], [home], [desktop])
image - dt_lua_image_t - the image being processed
sequence - integer - the sequence number of the image
variable_string - string - the substitution variable string
[username] - string - optional - user name. Will be determined if not supplied
[pic_folder] - string - optional - pictures folder name. Will be determined if not supplied
[home] - string - optional - home directory. Will be determined if not supplied
@@ -880,13 +975,17 @@ dtutils_string.libdoc.functions["substitute"] = {
function dtutils_string.substitute(image, sequence, variable_string, username, pic_folder, home, desktop)
local old_log_level = log.log_level()
log.log_level(dtutils_string.log_level)
dtutils_string.clear_substitute_list()
dtutils_string.build_substition_list(image, sequence, username, pic_folder, home, desktop)
dtutils_string.build_substition_list(image, sequence, variable_string, username, pic_folder, home, desktop)
local str = dtutils_string.substitute_list(variable_string)
log.log_level(old_log_level)
return str
end
return dtutils_string