Module:AchievementSystem: Difference between revisions
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode |
||
| Line 106: | Line 106: | ||
-- Try using the specialized JSON data loader - this is the correct MediaWiki way | -- Try using the specialized JSON data loader - this is the correct MediaWiki way | ||
-- to load JSON from wiki pages | -- to load JSON from wiki pages | ||
mw.log("JSON-DEBUG: ===== LOAD JSON DATA ATTEMPT START =====") | |||
mw.log("JSON-DEBUG: Attempting to use mw.loadJsonData for " .. ACHIEVEMENT_DATA_PAGE) | |||
-- Check if mw.loadJsonData is available | |||
if not mw.loadJsonData then | |||
mw.log("JSON-DEBUG: CRITICAL ERROR: mw.loadJsonData function does not exist!") | |||
mw.log("JSON-DEBUG: This indicates the MediaWiki version may be too old or misconfigured") | |||
else | |||
mw.log("JSON-DEBUG: mw.loadJsonData function exists, attempting to use it") | |||
end | |||
local loadJsonSuccess, jsonData = pcall(function() | local loadJsonSuccess, jsonData = pcall(function() | ||
return mw.loadJsonData(ACHIEVEMENT_DATA_PAGE) | return mw.loadJsonData(ACHIEVEMENT_DATA_PAGE) | ||
end) | end) | ||
| Line 113: | Line 123: | ||
if loadJsonSuccess and jsonData then | if loadJsonSuccess and jsonData then | ||
mw.log("JSON-DEBUG: Successfully loaded data with mw.loadJsonData") | mw.log("JSON-DEBUG: Successfully loaded data with mw.loadJsonData") | ||
mw.log("JSON-DEBUG: JSON data type: " .. type(jsonData)) | |||
-- Check if we got a table as expected | |||
if type(jsonData) == 'table' then | |||
-- Check some expected fields to confirm it's valid | |||
if jsonData.achievement_types then | |||
mw.log("JSON-DEBUG: ✓ Found achievement_types array with " .. | |||
#(jsonData.achievement_types or {}) .. " entries") | |||
else | |||
mw.log("JSON-DEBUG: ✗ Missing achievement_types array in loaded data!") | |||
end | |||
if jsonData.user_achievements then | |||
local userCount = 0 | |||
for _, _ in pairs(jsonData.user_achievements) do | |||
userCount = userCount + 1 | |||
end | |||
mw.log("JSON-DEBUG: ✓ Found user_achievements for " .. userCount .. " users") | |||
else | |||
mw.log("JSON-DEBUG: ✗ Missing user_achievements in loaded data!") | |||
end | |||
end | |||
return jsonData | return jsonData | ||
else | else | ||
mw.log("JSON-DEBUG: mw.loadJsonData failed: " .. tostring(jsonData or 'unknown error')) | mw.log("JSON-DEBUG: mw.loadJsonData failed: " .. tostring(jsonData or 'unknown error')) | ||
-- Try to categorize the error | |||
local errorMsg = tostring(jsonData or '') | |||
if errorMsg:match("content model") then | |||
mw.log("JSON-DEBUG: ERROR CAUSE: Content model issue - page must be set to 'json' model") | |||
elseif errorMsg:match("JSON decode") or errorMsg:match("syntax") then | |||
mw.log("JSON-DEBUG: ERROR CAUSE: JSON syntax error - check for invalid JSON formatting") | |||
elseif errorMsg:match("permission") or errorMsg:match("access") then | |||
mw.log("JSON-DEBUG: ERROR CAUSE: Permission/access error - check page permissions") | |||
end | |||
end | end | ||
-- Try | -- Try to fetch the raw content to see what's being received | ||
local | local rawContent = nil | ||
mw.log("JSON-DEBUG: | pcall(function() | ||
local title = mw.title.new(ACHIEVEMENT_DATA_PAGE) | |||
if title and title.exists then | |||
rawContent = title:getContent() | |||
if rawContent and #rawContent > 0 then | |||
mw.log("JSON-DEBUG: Raw content first 100 chars: " .. | |||
rawContent:sub(1, 100):gsub("\n", "\\n"):gsub("\t", "\\t")) | |||
-- Check if it starts with a curly brace | |||
if rawContent:match("^%s*{") then | |||
mw.log("JSON-DEBUG: ✓ Content starts with { (possibly with whitespace)") | |||
else | |||
mw.log("JSON-DEBUG: ✗ Content does NOT start with {") | |||
end | |||
-- Check for common issues | |||
if rawContent:match("^<!DOCTYPE") or rawContent:match("^<[Hh][Tt][Mm][Ll]") then | |||
mw.log("JSON-DEBUG: ERROR: Content appears to be HTML, not JSON!") | |||
elseif rawContent:match("^%s*<") then | |||
mw.log("JSON-DEBUG: ERROR: Content appears to have XML/HTML markup!") | |||
end | |||
else | |||
mw.log("JSON-DEBUG: Could not get raw content or content is empty") | |||
end | |||
else | |||
mw.log("JSON-DEBUG: Could not create title object or page doesn't exist") | |||
end | |||
end) | end) | ||
mw.log("JSON-DEBUG: ===== LOAD JSON DATA ATTEMPT END =====") | |||
-- GET RAW CONTENT DIRECTLY APPROACH - likely the most reliable method | |||
mw.log("JSON-DEBUG: ===== DIRECT RAW CONTENT METHOD START =====") | |||
-- Try to fetch the raw content directly with proper error handling | |||
-- Try | |||
local pageTitle = mw.title.new(ACHIEVEMENT_DATA_PAGE) | local pageTitle = mw.title.new(ACHIEVEMENT_DATA_PAGE) | ||
if not pageTitle or not pageTitle.exists then | if not pageTitle or not pageTitle.exists then | ||
| Line 137: | Line 203: | ||
return DEFAULT_DATA | return DEFAULT_DATA | ||
end | end | ||
-- | -- Get raw content from the wiki page | ||
local content = nil | |||
local contentSuccess, contentResult = pcall(function() | |||
return pageTitle:getContent() | |||
end) | |||
if contentSuccess and contentResult and contentResult ~= "" then | |||
content = contentResult | |||
mw.log("JSON-DEBUG: Successfully retrieved raw content, length: " .. #content) | |||
if jsonDecodeSuccess and jsonData then | -- Log the first part of content for debugging | ||
local contentPreview = content:sub(1, 100):gsub("\n", "\\n"):gsub("\t", "\\t") | |||
return | mw.log("JSON-DEBUG: Content preview: " .. contentPreview) | ||
-- Remove any BOM or leading whitespace that might cause issues | |||
content = content:gsub("^%s+", "") | |||
if content:byte(1) == 239 and content:byte(2) == 187 and content:byte(3) == 191 then | |||
mw.log("JSON-DEBUG: Removing UTF-8 BOM from content") | |||
content = content:sub(4) | |||
end | |||
-- Check if content starts with { or [ | |||
if content:match("^%s*[{%[]") then | |||
mw.log("JSON-DEBUG: Content appears to be valid JSON") | |||
-- Use mw.text.jsonDecode for parsing | |||
if mw.text and mw.text.jsonDecode then | |||
local jsonDecodeSuccess, jsonData = pcall(function() | |||
mw.log("JSON-DEBUG: Using mw.text.jsonDecode on raw content") | |||
return mw.text.jsonDecode(content) | |||
end) | |||
if jsonDecodeSuccess and jsonData then | |||
mw.log("JSON-DEBUG: ✓ Successfully decoded content with mw.text.jsonDecode") | |||
-- Validate the structure | |||
if jsonData.achievement_types and jsonData.user_achievements then | |||
mw.log("JSON-DEBUG: ✓ Parsed JSON has required fields") | |||
mw.log("JSON-DEBUG: ===== DIRECT RAW CONTENT METHOD SUCCESS =====") | |||
return jsonData | |||
else | |||
mw.log("JSON-DEBUG: ✗ Parsed JSON missing critical fields") | |||
end | |||
else | |||
mw.log("JSON-DEBUG: ✗ mw.text.jsonDecode failed: " .. tostring(jsonData or 'unknown error')) | |||
end | |||
else | |||
mw.log("JSON-DEBUG: ✗ mw.text.jsonDecode not available") | |||
end | |||
-- Fallback to Module:JSON if available | |||
if json and json.decode then | |||
local parseSuccess, parsedData = pcall(function() | |||
mw.log("JSON-DEBUG: Using Module:JSON's json.decode as fallback") | |||
return json.decode(content) | |||
end) | |||
if parseSuccess and parsedData then | |||
mw.log("JSON-DEBUG: ✓ Successfully decoded with Module:JSON") | |||
mw.log("JSON-DEBUG: ===== DIRECT RAW CONTENT METHOD SUCCESS =====") | |||
return parsedData | |||
else | |||
mw.log("JSON-DEBUG: ✗ Module:JSON decode failed: " .. tostring(parsedData or 'unknown error')) | |||
end | |||
end | |||
else | else | ||
mw.log("JSON-DEBUG: mw. | mw.log("JSON-DEBUG: ✗ Content does not appear to be valid JSON") | ||
mw.log("JSON-DEBUG: First 10 characters: " .. content:sub(1, 10):gsub("\n", "\\n"):gsub("\t", "\\t")) | |||
end | end | ||
else | |||
mw.log("JSON-DEBUG: ✗ Failed to get content: " .. tostring(contentResult or 'unknown error')) | |||
end | end | ||
-- | mw.log("JSON-DEBUG: ===== DIRECT RAW CONTENT METHOD FAILED =====") | ||
local | |||
mw.log("JSON-DEBUG: | -- FALLBACK SAFETY APPROACHES | ||
return | |||
-- Try parser cache as fallback | |||
local loadDataSuccess, cachedData = pcall(function() | |||
mw.log("JSON-DEBUG: Attempting mw.loadData fallback") | |||
return mw.loadData('Module:AchievementSystem') | |||
end) | end) | ||
if | if loadDataSuccess and cachedData then | ||
mw.log("JSON-DEBUG: | mw.log("JSON-DEBUG: Using mw.loadData cached data") | ||
return | return cachedData | ||
else | |||
mw.log("JSON-DEBUG: mw.loadData failed, using default data") | |||
end | end | ||
mw.log("JSON-DEBUG: | -- As absolute last resort, use local default data | ||
mw.log("JSON-DEBUG: All JSON loading approaches failed, using default data") | |||
end) | end) | ||
| Line 695: | Line 817: | ||
return cssClass, displayName, achievementId | return cssClass, displayName, achievementId | ||
end | |||
-------------------------------------------------------------------------------- | |||
-- Diagnostic function that can be directly called to troubleshoot JSON loading | |||
-------------------------------------------------------------------------------- | |||
function Achievements.diagnoseJsonLoading() | |||
local output = {} | |||
table.insert(output, "===== ACHIEVEMENT SYSTEM JSON DIAGNOSTICS =====") | |||
-- Check MediaWiki version and capabilities | |||
table.insert(output, "\n== MEDIAWIKI CAPABILITIES ==") | |||
if mw.loadJsonData then | |||
table.insert(output, "✓ mw.loadJsonData: Available") | |||
else | |||
table.insert(output, "❌ mw.loadJsonData: Not available! MediaWiki may be too old or misconfigured") | |||
end | |||
if mw.text and mw.text.jsonDecode then | |||
table.insert(output, "✓ mw.text.jsonDecode: Available") | |||
else | |||
table.insert(output, "❌ mw.text.jsonDecode: Not available! This is a critical function") | |||
end | |||
if json and json.decode then | |||
table.insert(output, "✓ Module:JSON: Available") | |||
else | |||
table.insert(output, "❌ Module:JSON: Not available or failed to load") | |||
end | |||
-- Check the page configuration | |||
table.insert(output, "\n== JSON PAGE STATUS ==") | |||
local pageTitle = mw.title.new(ACHIEVEMENT_DATA_PAGE) | |||
if not pageTitle then | |||
table.insert(output, "❌ Could not create title object for: " .. ACHIEVEMENT_DATA_PAGE) | |||
return table.concat(output, "\n") | |||
end | |||
if not pageTitle.exists then | |||
table.insert(output, "❌ Page does not exist: " .. ACHIEVEMENT_DATA_PAGE) | |||
return table.concat(output, "\n") | |||
end | |||
table.insert(output, "✓ Page exists: " .. ACHIEVEMENT_DATA_PAGE) | |||
-- Check the content model | |||
if pageTitle.contentModel then | |||
table.insert(output, "Content model: " .. pageTitle.contentModel) | |||
if pageTitle.contentModel == "json" then | |||
table.insert(output, "✓ Page has correct content model: 'json'") | |||
else | |||
table.insert(output, "❌ Page has INCORRECT content model: '" .. pageTitle.contentModel .. "' (should be 'json')") | |||
end | |||
else | |||
table.insert(output, "⚠ Could not determine content model") | |||
end | |||
-- Try to fetch page content | |||
local content = nil | |||
local contentSuccess, contentResult = pcall(function() | |||
return pageTitle:getContent() | |||
end) | |||
if not contentSuccess or not contentResult or contentResult == "" then | |||
table.insert(output, "❌ Failed to get page content: " .. tostring(contentResult or "Unknown error")) | |||
return table.concat(output, "\n") | |||
end | |||
table.insert(output, "✓ Got page content, length: " .. #contentResult) | |||
-- Check page content | |||
content = contentResult | |||
local contentPreview = content:sub(1, 50):gsub("\n", "\\n"):gsub("\t", "\\t") | |||
table.insert(output, "Content preview: \"" .. contentPreview .. "...\"") | |||
if content:match("^%s*{") then | |||
table.insert(output, "✓ Content starts with { (correct)") | |||
else | |||
table.insert(output, "❌ Content does NOT start with { (INCORRECT)") | |||
end | |||
-- Check for common issues | |||
if content:match("^<!DOCTYPE") or content:match("^<[Hh][Tt][Mm][Ll]") then | |||
table.insert(output, "❌ CRITICAL ERROR: Content appears to be HTML, not JSON!") | |||
elseif content:match("^%s*<") then | |||
table.insert(output, "❌ CRITICAL ERROR: Content appears to have XML/HTML markup!") | |||
end | |||
-- Try mw.loadJsonData | |||
table.insert(output, "\n== TESTING mw.loadJsonData ==") | |||
local loadJsonSuccess, loadJsonResult = pcall(function() | |||
return mw.loadJsonData(ACHIEVEMENT_DATA_PAGE) | |||
end) | |||
if not loadJsonSuccess then | |||
table.insert(output, "❌ mw.loadJsonData failed: " .. tostring(loadJsonResult or "Unknown error")) | |||
-- Analyze error | |||
local errorMsg = tostring(loadJsonResult or "") | |||
if errorMsg:match("content model") then | |||
table.insert(output, "ERROR CAUSE: Content model issue - page must be set to 'json' model") | |||
elseif errorMsg:match("JSON decode") or errorMsg:match("syntax") then | |||
table.insert(output, "ERROR CAUSE: JSON syntax error - check for invalid JSON formatting") | |||
elseif errorMsg:match("permission") or errorMsg:match("access") then | |||
table.insert(output, "ERROR CAUSE: Permission/access error - check page permissions") | |||
end | |||
else | |||
table.insert(output, "✓ mw.loadJsonData SUCCESSFUL!") | |||
table.insert(output, "Data type: " .. type(loadJsonResult)) | |||
if type(loadJsonResult) == "table" then | |||
local keysFound = {} | |||
for k, _ in pairs(loadJsonResult) do | |||
table.insert(keysFound, k) | |||
end | |||
table.insert(output, "Top-level keys: " .. table.concat(keysFound, ", ")) | |||
if loadJsonResult.achievement_types then | |||
table.insert(output, "✓ Found " .. #loadJsonResult.achievement_types .. " achievement types") | |||
else | |||
table.insert(output, "❌ Missing 'achievement_types' array!") | |||
end | |||
if loadJsonResult.user_achievements then | |||
local userCount = 0 | |||
for _, _ in pairs(loadJsonResult.user_achievements) do | |||
userCount = userCount + 1 | |||
end | |||
table.insert(output, "✓ Found user achievements for " .. userCount .. " users") | |||
else | |||
table.insert(output, "❌ Missing 'user_achievements' object!") | |||
end | |||
end | |||
end | |||
-- Try direct JSON decoding | |||
table.insert(output, "\n== TESTING DIRECT JSON DECODING ==") | |||
-- Use mw.text.jsonDecode | |||
if mw.text and mw.text.jsonDecode then | |||
local jsonDecodeSuccess, jsonData = pcall(function() | |||
return mw.text.jsonDecode(content) | |||
end) | |||
if jsonDecodeSuccess and jsonData then | |||
table.insert(output, "✓ mw.text.jsonDecode SUCCESSFUL!") | |||
if jsonData.achievement_types then | |||
table.insert(output, "✓ Found " .. #jsonData.achievement_types .. " achievement types") | |||
else | |||
table.insert(output, "❌ Missing 'achievement_types' array in decoded data!") | |||
end | |||
else | |||
table.insert(output, "❌ mw.text.jsonDecode failed: " .. tostring(jsonData or "Unknown error")) | |||
end | |||
else | |||
table.insert(output, "⚠ Cannot test mw.text.jsonDecode (not available)") | |||
end | |||
-- Use Module:JSON if available | |||
if json and json.decode then | |||
local parseSuccess, parsedData = pcall(function() | |||
return json.decode(content) | |||
end) | |||
if parseSuccess and parsedData then | |||
table.insert(output, "✓ Module:JSON's json.decode SUCCESSFUL!") | |||
if parsedData.achievement_types then | |||
table.insert(output, "✓ Found " .. #parsedData.achievement_types .. " achievement types") | |||
else | |||
table.insert(output, "❌ Missing 'achievement_types' array in decoded data!") | |||
end | |||
else | |||
table.insert(output, "❌ Module:JSON's json.decode failed: " .. tostring(parsedData or "Unknown error")) | |||
end | |||
else | |||
table.insert(output, "⚠ Cannot test Module:JSON (not available)") | |||
end | |||
-- Add recommendations | |||
table.insert(output, "\n== RECOMMENDATIONS ==") | |||
if not loadJsonSuccess then | |||
table.insert(output, "1. Ensure '" .. ACHIEVEMENT_DATA_PAGE .. "' has content model set to 'json'") | |||
table.insert(output, "2. Verify the JSON is valid with no syntax errors") | |||
table.insert(output, "3. Check the page starts with { with no leading whitespace or comments") | |||
else | |||
table.insert(output, "✓ JSON loading appears to be working correctly!") | |||
end | |||
return table.concat(output, "\n") | |||
end | end | ||
return Achievements | return Achievements | ||