-- -- Surveys -- -- yl_survey.get_data -- local function get_data() if next(yl_survey.data) then return true, yl_survey.data else return false, {} end end function yl_survey.get_data() return get_data() end -- yl_survey.get_survey -- local function get_record(id) local success, data = yl_survey.get_data() if (success == false) then return nil end if (type(data[id]) == "table") then return table.copy(data[id]) -- {data} or nil else return nil end end function yl_survey.get_record(id) return get_record(id) end -- validations -- local function validate_nil_or_positive(value) if (value == nil) then return true end if ((type(value) == "number") and (value >= 0)) then return true end return false end local function validate(value, expect_type, allow_nil) if ((allow_nil == true) and (value == nil)) then return true end if ((allow_nil == false) and (value == nil)) then return false end if (type(value) == expect_type) then return true end return false end local function validate_questiontype(allowed_types) local valid_types = { singlechoice = true, multiplechoice = true, freetext = true } local sm_count = 0 local ft_count = 0 for _, t in ipairs(allowed_types) do -- Invalid question type if (valid_types[t] == nil) then return false end -- Either singlechoice OR multiplechoice if ((t == "singlechoice") or (t == "multiplechoice")) then sm_count = sm_count + 1 end if sm_count > 1 then return false end -- Only one freetext allowed if (t == "freetext") then ft_count = ft_count + 1 end if ft_count > 1 then return false end end if ((ft_count + sm_count) == 0) then return false end return true end local function validate_responses(t_answers, responses) -- Defense if (validate(t_answers, "table", false) == false) then return false end if (validate(responses, "table", false) == false) then return false end -- If the responses are not JSON-able, return false if (minetest.write_json(responses) == nil) then return false end -- If there is a response not matched in t_answers, return false for k, v in pairs(responses) do local r_id = tonumber(k) if r_id and (r_id > 0) then local found = false for kk, vv in pairs(t_answers) do if (v == vv) then found = true end end if (found == false) then return false end end end return true end -- yl_survey.create_survey -- local function create_survey(owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end) -- Defense if ((type(owner) ~= "string") or (owner == "")) then return false, yl_survey.t("owner must be string") end if (title == nil) then title = "" end -- set default if (description == nil) then description = "" end -- set default if (allow_names == nil) then allow_names = true end -- set default if (type(allow_names) ~= "boolean") then return false, yl_survey.t("allow_names must be boolean") end if (allow_anonymous == nil) then allow_anonymous = true end -- set default if (type(allow_anonymous) ~= "boolean") then return false, yl_survey.t("allow_anonymous must be boolean") end if (allow_changes == nil) then allow_changes = false end -- set default if (type(allow_changes) ~= "boolean") then return false, yl_survey.t("allow_changes must be boolean") end if (validate_nil_or_positive(timestamp_start) == false) then return false, yl_survey.t( "timestamp_start if given then must be number and not negative") end if (validate_nil_or_positive(timestamp_end) == false) then return false, yl_survey.t( "timestamp_end if given then must be number and not negative") end -- Specialcase: We don't accept surveys NOONE can take part in --[[ if ((allow_names == false) and (allow_anonymous == false)) then return false, yl_survey.t("Noone can take part in that survey. " .. "Neither named nor anonymous participants are allowed") end ]] -- if ((allow_names == false) and (allow_anonymous == false) and (allow_changes == false)) then return false, yl_survey.t("Noone can take part in that survey. " .. "Neither named nor anonymous participants are allowed. " .. " You can't change it later") end -- Specialcase: We don't accept surveys where the enddate is smaller than the startdate if (timestamp_start and timestamp_end and (timestamp_start >= timestamp_end) and (allow_changes == false)) then return false, yl_survey.t("Noone can take part in that survey. " .. "It ends before it starts and you can't change it later") end -- UUID local get_uuid_success, UUID = yl_survey.create_uuid() if (get_uuid_success == false) then return false, UUID end -- Add to list local metadata = { id = UUID, owner = owner, title = title, description = description, allow_names = allow_names, allow_anonymous = allow_anonymous, allow_changes = allow_changes, timestamp_start = timestamp_start, timestamp_end = timestamp_end, creation_date = os.time(), modification_date = os.time() } local record = {} record["metadata"] = metadata -- Store local success = yl_survey.save(UUID, record) if (success == true) then yl_survey.data[UUID] = record return true, UUID else return false, yl_survey.t("Could not store") end end function yl_survey.create_survey(owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end) return create_survey(owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end) end -- yl_survey.edit_survey -- local function edit_survey(id, owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end, delete_responses) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(owner, "string", true) == false) then return false, yl_survey.t("owner must be string") end if (validate(title, "string", true) == false) then return false, yl_survey.t("title must be string") end if (validate(description, "string", true) == false) then return false, yl_survey.t("description must be string") end if (validate(allow_names, "boolean", true) == false) then return false, yl_survey.t("allow_names must be boolean") end if (validate(allow_anonymous, "boolean", true) == false) then return false, yl_survey.t("allow_anonymous must be boolean") end if (validate(allow_changes, "boolean", true) == false) then return false, yl_survey.t("allow_changes must be boolean") end if (validate(timestamp_start, "number", true) == false) then return false, yl_survey.t("timestamp_start must be number") end if (validate(timestamp_end, "number", true) == false) then return false, yl_survey.t("timestamp_end must be number") end if (validate(delete_responses, "boolean", true) == false) then return false, yl_survey.t("delete_responses must be boolean") end if (validate_nil_or_positive(timestamp_start) == false) then return false, yl_survey.t( "timestamp_start if given then must be number and not negative") end if (validate_nil_or_positive(timestamp_end) == false) then return false, yl_survey.t( "timestamp_end if given then must be number and not negative") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end if (record["metadata"].allow_changes == false) then return false, yl_survey.t("no changes allowed") end local metadata = { id = record["metadata"].id, -- This value cannot change owner = owner or record["metadata"].owner, title = title or record["metadata"].title, description = description or record["metadata"].description, allow_names = allow_names or record["metadata"].allow_names, allow_anonymous = allow_anonymous or record["metadata"].allow_anonymous, allow_changes = allow_changes or record["metadata"].allow_changes, timestamp_start = timestamp_start or record["metadata"].timestamp_start, timestamp_end = timestamp_end or record["metadata"].timestamp_end, creation_date = record["metadata"].creation_date, -- This value cannot change modification_date = os.time() -- This value must change } record["metadata"] = metadata -- Delete responses local no if (delete_responses == true) then record, no = yl_survey.delete_responses(record) end -- Store local success = yl_survey.save(id, record) if (success == true) then yl_survey.data[id] = record return true, id, no or 0 else return false, yl_survey.t("Could not store") end end function yl_survey.edit_survey(id, owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end, delete_responses) return edit_survey(id, owner, title, description, allow_names, allow_anonymous, allow_changes, timestamp_start, timestamp_end, delete_responses) end -- yl_survey.remove_record -- local function remove_record(id) local data_success, data = yl_survey.get_data() if (data_success ~= true) then return false, "Cannot get data" end local remove_success = yl_survey.remove_file(id) if (remove_success ~= true) then return false, yl_survey.t("Could not remove") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record not found, cannot remove") end data[id] = nil return true, record end function yl_survey.remove_record(id) return remove_record(id) end -- -- Questions -- -- create_question -- local function create_question(id, question, category, sort, allowed_types, answers, enabled) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(question, "string", false) == false) then return false, yl_survey.t("question must be string") end if (category == nil) then category = "" end if (validate(category, "string", false) == false) then return false, yl_survey.t("category must be string") end if (validate_nil_or_positive(sort) == false) then return false, yl_survey.t("sort must be positive number") end if (validate(allowed_types, "table", false) == false) then return false, yl_survey.t("allowed_types must be table") end if (validate(answers, "table", true) == false) then return false, yl_survey.t("answers must be table") end if (enabled == nil) then enabled = true end if (validate(enabled, "boolean", false) == false) then return false, yl_survey.t("enabled must be boolean") end -- Specialcase : allowed_types must be a combination of -- "singlechoice" or "multiplechoice" or "freetext" if (validate_questiontype(allowed_types) == false) then return false, yl_survey.t("wrong allowed_types") end -- Specialcase : answers cannot be nil or empty if allowedtypes has either multiplechoice or singlechoice if (((answers == nil) or (next(answers) == nil)) and ((yl_survey.table_contains(allowed_types, "multiplechoice") == true) or (yl_survey.table_contains(allowed_types, "singlechoice") == true))) then return false, yl_survey.t( "answers cannot be nil or empty if allowedtypes has either multiplechoice or singlechoice") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Specialcase : given sort is a duplicate if ((type(sort) == "number") and (yl_survey.is_duplicate(record, sort) == true)) then return false, yl_survey.t("sort is duplicate") end if (record["metadata"].allow_changes == false) then return false, yl_survey.t("no changes allowed") end -- Payload -- if sort is nil, then find next sort local next_sort = sort or yl_survey.find_next_free_number(record, "sort") -- Find next free q_id local next_q_id = yl_survey.find_next_free_number(record, "id") local t_question = { id = next_q_id, category = category, sort = next_sort, question = question, enabled = true, allowed_types = allowed_types, answers = answers, responses = {} } record[tostring(next_q_id)] = t_question -- Store local success = yl_survey.save(id, record) if (success == true) then yl_survey.data[id] = record return true, next_q_id else return false, yl_survey.t("Could not store") end end function yl_survey.create_question(id, question, category, sort, allowed_types, answers, enabled) return create_question(id, question, category, sort, allowed_types, answers, enabled) end -- edit_question -- local function edit_question(id, q_id, question, sort, category, allowed_types, answers, enabled, delete_responses) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(q_id, "number", false) == false) then return false, "q_id must be number" end if (q_id <= 0) then return false, "q_id must be positive number" end if (validate(question, "string", true) == false) then return false, yl_survey.t("question must be string") end if (validate(category, "string", true) == false) then return false, yl_survey.t("category must be string") end if (validate_nil_or_positive(sort) == false) then return false, yl_survey.t("sort must be positive number") end if (validate(allowed_types, "table", true) == false) then return false, yl_survey.t("allowed_types must be table") end if (validate(answers, "table", true) == false) then return false, yl_survey.t("answers must be table") end if (validate(enabled, "boolean", true) == false) then return false, yl_survey.t("enabled must be boolean") end if (validate(delete_responses, "boolean", true) == false) then return false, yl_survey.t("delete_responses must be boolean") end -- Specialcase : allowed_types must be a combination of -- "singlechoice" or "multiplechoice" or "freetext" if ((allowed_types ~= nil) and (validate_questiontype(allowed_types) == false)) then return false, yl_survey.t("wrong allowed_types") end -- Specialcase : answers cannot be nil or empty if allowedtypes has either multiplechoice or singlechoice if ((answers ~= nil) and (next(answers) ~= nil) and ((yl_survey.table_contains(allowed_types, "multiplechoice") == true) or (yl_survey.table_contains(allowed_types, "singlechoice") == true))) then return false, yl_survey.t( "answers cannot be nil or empty if allowedtypes has either multiplechoice or singlechoice") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Specialcase : given sort is a duplicate if ((type(sort) == "number") and (yl_survey.is_duplicate(record, sort) == true)) then return false, yl_survey.t("sort is duplicate") end if (record["metadata"].allow_changes == false) then return false, yl_survey.t("no changes allowed") end if (record[tostring(q_id)] == nil) then return false, yl_survey.t("question does not exist") end local t_question = table.copy(record[tostring(q_id)]) -- Delete responses local no = 0 local n_responses = t_question["responses"] or {} if (delete_responses == true) then for _, _ in pairs(n_responses) do no = no + 1 end n_responses = {} end -- Payload local s_question = { id = t_question["id"], category = category or t_question["category"], sort = sort or t_question["sort"], question = question or t_question["question"], enabled = enabled or t_question["enabled"], allowed_types = allowed_types or t_question["allowed_types"], answers = answers or t_question["answers"], responses = n_responses } record[tostring(q_id)] = s_question -- Store local success = yl_survey.save(id, record) if (success == true) then yl_survey.data[id] = record return true, q_id else return false, yl_survey.t("Could not store") end end function yl_survey.edit_question(s_id, q_id, question, sort, category, allowed_types, answers, enabled, delete_responses) return edit_question(s_id, q_id, question, sort, category, allowed_types, answers, enabled, delete_responses) end -- remove_question -- local function remove_question(id, q_id, delete_responses) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(q_id, "number", false) == false) then return false, "q_id must be number" end if (q_id <= 0) then return false, "q_id must be positive number" end if (validate(delete_responses, "boolean", true) == false) then return false, yl_survey.t("delete_responses must be boolean") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end if (record["metadata"].allow_changes == false) then return false, yl_survey.t("no changes allowed") end if (record[tostring(q_id)] == nil) then return false, yl_survey.t("question does not exist") end local t_question = table.copy(record[tostring(q_id)]) -- Delete responses local no = 0 local n_responses = t_question["responses"] or {} if (delete_responses == true) then for _, _ in pairs(n_responses) do no = no + 1 end record[tostring(q_id)] = nil else record[tostring(q_id)] = {responses = n_responses} end -- Store local success = yl_survey.save(id, record) if (success == true) then yl_survey.data[id] = record return true, t_question, no else return false, yl_survey.t("Could not store") end end function yl_survey.remove_question(s_id, q_id, delete_responses) return remove_question(s_id, q_id, delete_responses) end -- list_questions -- local function list_questions(id, include_responses) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(include_responses, "boolean", true) == false) then return false, yl_survey.t("include_responses must be boolean") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Payload local questions = {} for key, value in pairs(record) do if (tonumber(key) ~= nil) then if (include_responses ~= true) then value["responses"] = nil end questions[key] = value end end if (next(questions) ~= nil) then return true, questions else return false, {} end end function yl_survey.list_questions(s_id, include_responses) return list_questions(s_id, include_responses) end -- yl_survey.answer_question -- local function answer_question(id, q_id, playername, responses, overwrite) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(q_id, "number", false) == false) then return false, "q_id must be number" end if (q_id <= 0) then return false, "q_id must be positive number" end if (validate(playername, "string", false) == false) then return false, yl_survey.t("playername must be string") end if (validate(responses, "table", false) == false) then return false, yl_survey.t("responses must be table") end if (validate(overwrite, "boolean", true) == false) then return false, yl_survey.t("overwrite must be boolean") end -- Get survey local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Get question if (record[tostring(q_id)] == nil) then return false, yl_survey.t("question does not exist") end local t_question = table.copy(record[tostring(q_id)]) -- Get responses local all_responses = t_question["responses"] local overwritten_responses = {} if (all_responses and next(all_responses[playername]) ~= nil) then overwritten_responses = all_responses[playername] or {} end if ((overwrite ~= true) and (overwritten_responses ~= nil)) then return false, "previous responses cannot be written over without overwrite true" end responses["q"] = t_question["question"] if (record[tostring(q_id)]["responses"] == nil) then record[tostring(q_id)]["responses"] = {} end if (validate_responses(t_question["answers"], responses) == false) then return false, "responses do not fit the questions" end record[tostring(q_id)]["responses"][playername] = responses -- Store local success = yl_survey.save(id, record) if (success == true) then yl_survey.data[id] = record return true, overwritten_responses or {} else return false, yl_survey.t("Could not store") end end function yl_survey.answer_question(id, q_id, playername, responses, overwrite) return answer_question(id, q_id, playername, responses, overwrite) end -- get_question -- local function get_question(id, q_id, include_responses, playername) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(q_id, "number", false) == false) then return false, yl_survey.t("q_id must be number") end if (q_id <= 0) then return false, "q_id must be positive number" end if (validate(playername, "string", true) == false) then return false, yl_survey.t("playername must be string") end if (validate(include_responses, "boolean", true) == false) then return false, yl_survey.t("include_responses must be boolean") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Payload local question = record[tostring(q_id)] or {} if (playername ~= nil) then local responses = {} responses[playername] = question["responses"] and question["responses"][playername] or {} question["responses"] = responses end if (include_responses ~= true) then question["responses"] = nil end if (next(question) ~= nil) then return true, question else return false, {} end end function yl_survey.get_question(s_id, q_id, include_responses, playername) return get_question(s_id, q_id, include_responses, playername) end -- get_response -- local function get_response(id, q_id, playername, include_question, include_freetext) -- Defense if (validate(id, "string", false) == false) then return false, yl_survey.t("id must be string") end if (validate(q_id, "number", false) == false) then return false, yl_survey.t("q_id must be number") end if (q_id <= 0) then return false, "q_id must be positive number" end if (validate(playername, "string", false) == false) then return false, yl_survey.t("playername must be string") end if (validate(include_question, "boolean", true) == false) then return false, yl_survey.t("include_question must be boolean") end if (validate(include_freetext, "boolean", true) == false) then return false, yl_survey.t("include_freetext must be boolean") end local record = yl_survey.get_record(id) if (record == nil) then return false, yl_survey.t("record does not exist") end -- Payload local question = record[tostring(q_id)] or {} local response = question["responses"] and question["responses"][playername] or {} if (include_question ~= true) then response["q"] = nil end if (include_freetext ~= true) then response["0"] = nil end if (next(question) ~= nil) then return true, response else return false, yl_survey.t("question does not exist") end end function yl_survey.get_response(s_id, q_id, playername, include_question) return get_response(s_id, q_id, playername, include_question) end