yl_survey/internal.lua
2024-09-17 11:49:07 +02:00

284 lines
7.5 KiB
Lua

-- The functions and variables in this file are only for use in the mod itself.
-- Those that do real work should be local and wrapped in public functions
local function log(text)
local logmessage = yl_survey.t("log_prefix", yl_survey.modname, text)
if yl_survey.settings.debug then minetest.log("action", logmessage) end
return logmessage
end
function yl_survey.log(text) return log(text) end
-- Helpers
local function filename2uuid(filename) return filename:sub(1, -6) end
local function is_visible(filename) return (string.sub(filename, 1, 1) ~= ".") end
local function is_json(filename) return (filename:match("%.json$")) end
local function ends_with(str, suffix) return str:sub(-suffix:len()) == suffix end
local function split(str)
local parts = {}
for part in str:gmatch("[^,%s]+") do table.insert(parts, part) end
return parts
end
local function priv_exists(priv)
return (minetest.registered_privileges[priv] ~= nil) or false
end
function yl_survey.priv_exists(priv) return priv_exists(priv) end
-- table.contains
local function table_contains(t, m)
if ((type(m) ~= "number") and (type(m) ~= "string") and (type(m) ~= "boolean")) then
return nil, yl_survey.t("member must be primitive")
end
for _, v in pairs(t) do
if (v == m) then
return true
end
end
return false
end
function yl_survey.table_contains(t, m)
return table_contains(t, m)
end
-- Hash name
local function hash_name(name)
if ((type(name) ~= "string") or (name == "")) then
return nil
end
return minetest.sha256(name, false)
end
function yl_survey.hash_name(name) return hash_name(name) end
-- UUID
local function generate_uuid()
local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
return string.gsub(template, '[xy]', function(c)
local v = (c == 'x') and math.random(0, 15) or math.random(8, 11)
return string.format('%x', v)
end)
end
local function is_uuid_duplicate(UUID)
for i, record in pairs(yl_survey.data) do
local message = "is_uuid_duplicate record.id=%s, UUID=%s"
local formatted = string.format(message, dump(record.id), dump(UUID))
log(formatted)
if record.id == UUID then return true end
end
return false
end
local function create_uuid()
local max_attempts = 10
local UUID
repeat
UUID = generate_uuid()
max_attempts = max_attempts - 1
if max_attempts < 0 then
return false, "Cannot find non-duplicate UUID"
end
until (is_uuid_duplicate(UUID) == false)
if UUID == "" then return false, "UUID empty" end
return true, UUID
end
function yl_survey.create_uuid() return create_uuid() end
-- delete responses
local function delete_responses(record)
local no = 0
for key, question in pairs(record) do
if (tonumber(key) ~= nil) then
if question.responses ~= nil then
question.responses = nil
no = no + 1
end
end
end
return record, no
end
function yl_survey.delete_responses(record)
return delete_responses(record)
end
-- yl_survey.find_next_free_sort_number(record)
local function find_next_free_number(record, target)
local highest_no = 1
local duplicates = {}
for key, question in pairs(record) do
if (tonumber(key) ~= nil) then
table.insert(duplicates, question[target])
if (question[target] >= highest_no) then
highest_no = question[target] + 1
end
end
end
local is_duplicate = false
for _, dupe in ipairs(duplicates) do
if (highest_no == dupe) then
is_duplicate = true
end
end
return highest_no, is_duplicate
end
function yl_survey.find_next_free_number(record, target)
return find_next_free_number(record, target)
end
-- yl_survey.is_duplicate(record, sort)
local function is_duplicate(record, sort)
for key, question in pairs(record) do
if (tonumber(key) ~= nil) then
if (question[sort] == sort) then
return true
end
end
end
return false
end
function yl_survey.is_duplicate(record, sort)
return is_duplicate(record, sort)
end
-- Load and save
local function get_savepath()
local savepath = yl_survey.worldpath .. yl_survey.settings.save_path
log(savepath or "")
return savepath
end
local function get_filepath(filename)
local path_to_file = get_savepath() .. DIR_DELIM .. filename .. ".json"
log(yl_survey.t("get_filepath", dump(path_to_file)))
return path_to_file
end
local function save_json(filename, content)
if ((type(filename) ~= "string") or (type(content) ~= "table")) then
return false
end
local savepath = get_filepath(filename)
local savecontent = minetest.write_json(content)
return minetest.safe_file_write(savepath, savecontent)
end
local function load_json(path)
local file = io.open(path, "r")
if not file then
return false, yl_survey.t("error_cannot_open_file", dump(path))
end
local content = file:read("*all")
file:close()
if not content then
return false, yl_survey.t("error_cannot_read_file", dump(path))
end
return true, minetest.parse_json(content)
end
-- Public functions wrap the private ones, so they can be exchanged easily
function yl_survey.load(filename, ...) return load_json(filename, ...) end
function yl_survey.save(filename, content, ...)
return save_json(filename, content, ...)
end
-- Remove file
local function remove_file(UUID)
local path = get_filepath(UUID)
return os.remove(path)
end
function yl_survey.remove_file(UUID) return remove_file(UUID) end
-- Load all data
--
local function load_all_data()
-- Get all json files from savepath
-- Excluding invisible
-- Excluding non-json files
local save_path = get_savepath()
local files = minetest.get_dir_list(save_path, false) or {}
local data = {}
local total = 0
local good = 0
local bad = 0
for key, filename in ipairs(files) do
if is_visible(filename) and is_json(filename) then
total = total + 1
local UUID = filename2uuid(filename)
local filepath = get_filepath(UUID)
local success, content = load_json(filepath)
if ((success == true) and (content ~= nil) and (content["metadata"].id == UUID)) then
good = good + 1
data[UUID] = content
else
bad = bad + 1
end
end
end
yl_survey.data = data
local message = "[MOD] %s : bad = %s, good = %s, total = %s"
local formatted = string.format(message, yl_survey.information.name, tostring(bad), tostring(good), tostring(total))
if bad == 0 then
minetest.log("action", formatted)
return true, good, bad
else
minetest.log("warning", formatted)
return false, good, bad
end
end
function yl_survey.load_all_data() return load_all_data() end
-- Check privs
--
local function check_privs(settings)
for key, value in pairs(settings) do
if ends_with(key, "_privs") then
local parts = split(value)
for _, part in ipairs(parts) do
local message = "[MOD] %s : configured priv %s does not exist."
local formatted = string.format(message, yl_survey.information.name, dump(part))
assert(priv_exists(part), formatted)
end
end
end
log("PASS priv check")
end
function yl_survey.check_privs(settings) return check_privs(settings) end