2,265
edits
(Update package: OSW Core) |
(import from wiki-dev) |
||
Line 23: | Line 23: | ||
} | } | ||
p.slots = { --slot names | p.slots = { --slot names | ||
main='main', | |||
jsondata='jsondata', | jsondata='jsondata', | ||
jsonschema='jsonschema', | jsonschema='jsonschema', | ||
Line 34: | Line 35: | ||
query='query' | query='query' | ||
} | } | ||
p.cache = {} | |||
--loads json from a wiki page | --loads json from a wiki page | ||
Line 40: | Line 43: | ||
function p.loadJson(args) | function p.loadJson(args) | ||
local page_title = p.defaultArg(args.title, "JsonSchema:Entity") --for testing | local page_title = p.defaultArg(args.title, "JsonSchema:Entity") --for testing | ||
local slot = p.defaultArg(args.slot, | local slot = p.defaultArg(args.slot, 'main') | ||
local debug = p.defaultArg(args.debug, nil) | local debug = p.defaultArg(args.debug, nil) | ||
local msg = "" | local msg = "" | ||
Line 46: | Line 49: | ||
local json = {} | local json = {} | ||
if (slot == | if p.cache[page_title] ~= nil then | ||
if p.cache[page_title][slot] ~= nil then | |||
if (debug) then msg = msg .. "Fetch slot " .. p.slots.jsondata .. " of page " .. page_title .. " from cache <br>" end | |||
json = p.cache[page_title][slot] | |||
return {json=json, debug_msg=msg} | |||
end | |||
else p.cache[page_title] = {} | |||
end | |||
if (slot == 'main') then | |||
--json = mw.loadJsonData( "JsonSchema:Entity" ) --requires MediaWiki 1.39 | --json = mw.loadJsonData( "JsonSchema:Entity" ) --requires MediaWiki 1.39 | ||
local page = mw.title.makeTitle(p.splitString(page_title, ':')[1], p.splitString(page_title, ':')[2]) | local page = mw.title.makeTitle(p.splitString(page_title, ':')[1], p.splitString(page_title, ':')[2]) | ||
Line 52: | Line 64: | ||
if (text ~= nil) then json = mw.text.jsonDecode(text) end | if (text ~= nil) then json = mw.text.jsonDecode(text) end | ||
else | else | ||
if (debug) then msg = msg .. "Fetch slot " .. p.slots.jsondata .. " | if (debug) then msg = msg .. "Fetch slot " .. p.slots.jsondata .. " of page " .. page_title .. "<br>" end | ||
local text = mw.slots.slotContent( slot , page_title ) | local text = mw.slots.slotContent( slot , page_title ) | ||
if (text ~= nil) then json = mw.text.jsonDecode(text) end | if (text ~= nil) then json = mw.text.jsonDecode(text) end | ||
end | end | ||
local generated_content = json["$defs"] and json["$defs"]["generated"] | |||
if generated_content then | |||
-- Check if "$ref": "#/$defs/generated" exists directly in the table | |||
if json["$ref"] == "#/$defs/generated" then | |||
json = p.tableMerge(p.copy(generated_content), json) | |||
json["$ref"] = nil -- Remove the reference after merging | |||
end | |||
-- Check if "$ref": "#/$defs/generated" is contained in "allOf" | |||
if json["allOf"] then | |||
for _, item in ipairs(json["allOf"]) do | |||
if item["$ref"] == "#/$defs/generated" then | |||
-- Remove the reference after merging | |||
for i, v in ipairs(json["allOf"]) do | |||
if v["$ref"] == "#/$defs/generated" then | |||
table.remove(json["allOf"], i) | |||
break | |||
end | |||
end | |||
json = p.tableMerge(p.copy(generated_content), json) | |||
break | |||
end | |||
end | |||
end | |||
json["$defs"]["generated"] = nil | |||
end | |||
--[[local defs = p.defaultArgPath(json, {"$defs", "generated"}, nil) | |||
if defs == nil then defs = p.defaultArgPath(json, {"definitions", "generated"}, nil) end | |||
if defs ~= nil then | |||
json = p.tableMerge(p.copy(defs), json) | |||
json["allOf"] = p.tableMerge(json["allOf"], p.copy(defs["allOf"])) | |||
end]]-- | |||
--mw.logObject(json) | --mw.logObject(json) | ||
p.cache[page_title][slot] = json | |||
return {json=json, debug_msg=msg} | return {json=json, debug_msg=msg} | ||
end | end | ||
-- test: mw.logObject(p.walkJsonSchema({jsonschema=p.loadJson({title="Category:Hardware", slot="jsonschema"}).json, debug=true}).jsonschema) | -- test: mw.logObject(p.walkJsonSchema({jsonschema=p.loadJson({title="Category:Hardware", slot="jsonschema"}).json, debug=true}).jsonschema) | ||
Line 70: | Line 122: | ||
local mode = p.defaultArg(args.mode, p.mode.header) | local mode = p.defaultArg(args.mode, p.mode.header) | ||
--local merged_jsonschema = p.defaultArg(args.merged_jsonschema, {}) | --local merged_jsonschema = p.defaultArg(args.merged_jsonschema, {}) | ||
local template = p.defaultArg(args.template, nil) | |||
local templates = p.defaultArg(args.templates, {}) | local templates = p.defaultArg(args.templates, {}) | ||
local recursive = p.defaultArg(args.recursive, true) | local recursive = p.defaultArg(args.recursive, true) | ||
Line 81: | Line 134: | ||
if (mode == p.mode.header) then category_template_slot = p.slots.header_template end | if (mode == p.mode.header) then category_template_slot = p.slots.header_template end | ||
if (categories == nil) then categories = p.getCategories({jsonschema=jsonschema, includeNamespace=true}).categories end | if (categories == nil) then categories = p.getCategories({jsonschema=jsonschema, includeNamespace=true, includeSchemas=true}).categories end | ||
if (type(categories) ~= 'table') then categories = {categories} end | if (type(categories) ~= 'table') then categories = {categories} end | ||
if (debug) then msg = msg .. "Supercategories: " .. mw.dumpObject(categories) .. "\n<br>" end | if (debug) then msg = msg .. "Supercategories: " .. mw.dumpObject(categories) .. "\n<br>" end | ||
Line 88: | Line 141: | ||
--mw.logObject("Visit " .. category) | --mw.logObject("Visit " .. category) | ||
if (debug) then msg = msg .. "Fetch slot " .. p.slots.jsonschema .. " from page " .. category .. "\n<br>" end | if (debug) then msg = msg .. "Fetch slot " .. p.slots.jsonschema .. " from page " .. category .. "\n<br>" end | ||
local | local super_jsonschema = nil | ||
if ( | if p.splitString(category, ':')[1] == "JsonSchema" then super_jsonschema = p.loadJson({title=category, slot=p.slots.main}).json | ||
else super_jsonschema = p.loadJson({title=category, slot=p.slots.jsonschema}).json end | |||
mw.logObject("INSERT") | |||
mw.logObject(super_jsonschema) | |||
if (super_jsonschema ~= nil) then | |||
if (recursive) then | if (recursive) then | ||
local res = p.walkJsonSchema({jsonschema=super_jsonschema, jsonschemas=jsonschemas, templates=templates, mode=mode, visited=visited, root=false}) | local res = p.walkJsonSchema({jsonschema=super_jsonschema, jsonschemas=jsonschemas, templates=templates, mode=mode, visited=visited, root=false}) | ||
Line 99: | Line 155: | ||
--mw.logObject("Store " .. category) | --mw.logObject("Store " .. category) | ||
table.insert(visited, category) | table.insert(visited, category) | ||
jsonschemas[category] = | |||
jsonschemas[category] = p.copy( super_jsonschema ) --keep a copy of the schema, super_jsonschema passed by references gets modified | |||
--jsonschema = p.tableMerge(jsonschema, super_jsonschema) --merge superschema is done by the caller | --jsonschema = p.tableMerge(jsonschema, super_jsonschema) --merge superschema is done by the caller | ||
end | end | ||
Line 107: | Line 164: | ||
end | end | ||
end | end | ||
if (root and p.tableLength(jsonschema) > 0) then | |||
table.insert(visited, "_") -- dummy category for own schema | |||
jsonschemas["_"] = p.copy(jsonschema) | |||
templates["_"] = template | |||
end | |||
if (root) then | if (root) then | ||
for i, category in ipairs(visited) do | for i, category in ipairs(visited) do | ||
Line 137: | Line 199: | ||
local msg = "" | local msg = "" | ||
local res = p.defaultArg(args.jsondata, "") | local res = p.defaultArg(args.jsondata, "") | ||
local root = p.defaultArg(args.root, true) -- first entry into recursion | |||
for k,v in pairs(jsondata) do | for k,v in pairs(jsondata) do | ||
Line 160: | Line 223: | ||
end | end | ||
elseif type(v) == 'table' then | elseif type(v) == 'table' then | ||
if (v[1] == nil) then --key value array = object/dict | if (p.tableLength(v) > 0 and v[1] == nil) then --key value array = object/dict | ||
local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=v, jsonschema=p.defaultArgPath(jsonschema, {"properties", k}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays}) | local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=v, jsonschema=p.defaultArgPath(jsonschema, {"properties", k}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false}) | ||
msg = msg .. sub_res.debug_msg | msg = msg .. sub_res.debug_msg | ||
jsondata[k] = sub_res.res | jsondata[k] = sub_res.res | ||
Line 181: | Line 244: | ||
if type(e) == 'table' then | if type(e) == 'table' then | ||
local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=e, jsonschema=p.defaultArgPath(jsonschema, {"properties", k, "items"}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays}) | local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=e, jsonschema=p.defaultArgPath(jsonschema, {"properties", k, "items"}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false}) | ||
msg = msg .. sub_res.debug_msg | msg = msg .. sub_res.debug_msg | ||
if (type(sub_res.res) == 'table') then | if (type(sub_res.res) == 'table') then | ||
Line 193: | Line 256: | ||
--evaluate single array item string as json {"self": "<value>", ".": "<value>"} => does not work since jsondata is an object | --evaluate single array item string as json {"self": "<value>", ".": "<value>"} => does not work since jsondata is an object | ||
--e = p.expandEmbeddedTemplates({frame=frame, jsondata={["self"]=e,["."]=e}, jsonschema=p.defaultArgPath(jsonschema, {"properties", k, "items"}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays}) | --e = p.expandEmbeddedTemplates({frame=frame, jsondata={["self"]=e,["."]=e}, jsonschema=p.defaultArgPath(jsonschema, {"properties", k, "items"}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false}) | ||
Line 216: | Line 279: | ||
if (template == nil) then | if (template == nil and root == false) then -- don't stringify root json objects | ||
local templates = jsondata[p.keys.template] | local templates = jsondata[p.keys.template] | ||
if (templates == nil) then templates = p.defaultArg(jsonschema[p.keys.template], {}) end | if (templates == nil) then templates = p.defaultArg(jsonschema[p.keys.template], {}) end | ||
Line 228: | Line 291: | ||
end | end | ||
if template ~= nil then | if (template ~= nil and root == false) then -- don't stringify root json objects | ||
if (template.type == "wikitext") then | if (template.type == "wikitext") then | ||
for k,v in pairs(jsondata) do | for k,v in pairs(jsondata) do | ||
Line 276: | Line 339: | ||
if (not p.nilOrEmpty(jsondata[p.keys.category])) then categories = jsondata[p.keys.category] end -- let json property overwrite function param | if (not p.nilOrEmpty(jsondata[p.keys.category])) then categories = jsondata[p.keys.category] end -- let json property overwrite function param | ||
local schema_res = p.walkJsonSchema({jsonschema=jsonschema, categories=categories, mode=mode, recursive=recursive, debug=debug}) | local schema_res = p.walkJsonSchema({jsonschema=jsonschema, template=template, categories=categories, mode=mode, recursive=recursive, debug=debug}) | ||
local expand_res = p.expandJsonRef({json=schema_res.jsonschema, debug=debug}) | local expand_res = p.expandJsonRef({json=schema_res.jsonschema, debug=debug}) | ||
jsonschema = expand_res.json | jsonschema = expand_res.json | ||
--mw.log(mw.text.jsonEncode(jsonschema)) | --mw.log(mw.text.jsonEncode(jsonschema)) | ||
local jsonld = p.copy(jsondata) | local jsonld = p.copy(jsondata) | ||
Line 291: | Line 349: | ||
json_res_store = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_store, mode='store'}) | json_res_store = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_store, mode='store'}) | ||
msg = msg .. json_res_store.debug_msg | msg = msg .. json_res_store.debug_msg | ||
mw.log("JSONDATA STORE") | |||
mw.logObject(json_res_store.res) | |||
local smw_res = nil | local smw_res = nil | ||
Line 302: | Line 360: | ||
-- store metadata where properties were defined / overridden | -- store metadata where properties were defined / overridden | ||
for i, category in ipairs(schema_res.visited) do | for i, category in ipairs(schema_res.visited) do | ||
for k, v in pairs(schema_res.jsonschemas | for k, v in pairs(p.defaultArgPath(schema_res.jsonschemas, {category, 'properties'}, {})) do --property section may not exisit | ||
if smw_res.definitions[k] == nil then smw_res.definitions[k] = {} end | if smw_res.definitions[k] == nil then smw_res.definitions[k] = {} end | ||
if smw_res.definitions[k]['defined_in'] == nil then smw_res.definitions[k]['defined_in'] = {} end | if smw_res.definitions[k]['defined_in'] == nil then smw_res.definitions[k]['defined_in'] = {} end | ||
Line 320: | Line 378: | ||
end | end | ||
end | end | ||
wikitext = wikitext .. "<div class='jsonld-header' style='display:none' data-jsonld='" .. mw.text.jsonEncode( jsonld ):gsub("'","`") .. "'></div>" | -- wikitext = wikitext .. "<div class='jsonld-header' style='display:none' data-jsonld='" .. mw.text.jsonEncode( jsonld ):gsub("'","`") .. "'></div>" | ||
end | end | ||
Line 362: | Line 420: | ||
end | end | ||
local set_categories_in_wikitext = {} | local set_categories_in_wikitext = {} | ||
p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.subcategory]) --classes/categories, nil for items | p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.subcategory]) --classes/categories, nil for items | ||
Line 369: | Line 425: | ||
p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.category]) -- categories from schema type | p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.category]) -- categories from schema type | ||
end | end | ||
-- Todo: Consider moving the category and this block to p.getSemanticProperties with store=true. However, settings categories with @category is only possible for subobjects | -- Todo: Consider moving the category and this block to p.getSemanticProperties with store=true. However, settings categories with @category is only possible for subobjects | ||
if (smw_res ~= nil) then | if (smw_res ~= nil) then | ||
local display_label = p.getDisplayLabel(jsondata, smw_res.properties) | |||
if title.nsText == "Property" then display_label = p.defaultArgPath(jsondata, {p.keys.name}, display_label) end | |||
if (debug) then msg = msg .. "Store page properties" end | if (debug) then msg = msg .. "Store page properties" end | ||
-- category handling | -- category handling | ||
Line 380: | Line 441: | ||
-- label and display title handling | -- label and display title handling | ||
smw_res.properties['Display title of'] = display_label --set special property display title | if display_label ~= nil then | ||
smw_res.properties['Display title of'] = display_label --set special property display title | |||
smw_res.properties['Display title of lowercase'] = display_label:lower() --store lowercase for case insensitive query | |||
smw_res.properties['Display title of normalized'] = display_label:lower():gsub('[^%w]+','') --store with all non-alphanumeric chars removed for normalized query | |||
end | |||
p.setNormalizedLabel(smw_res.properties) --build normalized multilang label | p.setNormalizedLabel(smw_res.properties) --build normalized multilang label | ||
mw.ext.displaytitle.set(display_label) | mw.ext.displaytitle.set(display_label) | ||
Line 484: | Line 547: | ||
elseif (type(v) == 'boolean') then | elseif (type(v) == 'boolean') then | ||
if (v) then v = "✅" else v = "❌" end -- green check mark or red cross | if (v) then v = "✅" else v = "❌" end -- green check mark or red cross | ||
elseif ((string.len(e) > 100) and (string.find(e, "{{") == nil) and (string.find(e, "</") == nil)) then | elseif ((string.len(e) > 100) and (string.find(e, "{{") == nil) and (string.find(e, "</") == nil) and (string.find(e, "%[%[") == nil)) then -- no markup, no links | ||
e = string.sub(e, 1, 100) .. "..."; -- limit infobox plain text to max 100 chars | e = string.sub(e, 1, 100) .. "..."; -- limit infobox plain text to max 100 chars | ||
elseif (debug) then | elseif (debug) then | ||
Line 511: | Line 574: | ||
elseif (type(v) == 'boolean') then | elseif (type(v) == 'boolean') then | ||
if (v) then v = "✅" else v = "❌" end -- green check mark or red cross | if (v) then v = "✅" else v = "❌" end -- green check mark or red cross | ||
elseif ((string.len(v) > 100) and (string.find(v, "{{") == nil) and (string.find(v, "</") == nil)) then | elseif ((string.len(v) > 100) and (string.find(v, "{{") == nil) and (string.find(v, "</") == nil) and (string.find(v, "%[%[") == nil)) then -- no markup, no links | ||
v = string.sub(v, 1, 100) .. "..."; -- limit infobox plain text to max 100 chars | v = string.sub(v, 1, 100) .. "..."; -- limit infobox plain text to max 100 chars | ||
elseif (debug) then | elseif (debug) then | ||
Line 534: | Line 597: | ||
local jsonschema = p.defaultArg(args.jsonschema, {}) | local jsonschema = p.defaultArg(args.jsonschema, {}) | ||
local includeNamespace = p.defaultArg(args.includeNamespace, false) | local includeNamespace = p.defaultArg(args.includeNamespace, false) | ||
local includeSchemas = p.defaultArg(args.includeSchemas, false) | |||
local categories = {} | local categories = {} | ||
Line 540: | Line 604: | ||
--properties['@category'] = {} | --properties['@category'] = {} | ||
for k, entry in pairs(allOf) do | for k, entry in pairs(allOf) do | ||
if type(entry) == 'table' then -- "allOf": [{"$ref": "/wiki/Category:Test?action=raw"}] | local refs = nil | ||
if type(entry) == 'table' then refs = entry -- "allOf": [{"$ref": "/wiki/Category:Test?action=raw"}] | |||
else refs = {entry} end-- "allOf": {"$ref": "/wiki/Category:Test?action=raw"} | |||
for p, v in pairs(entry) do | |||
if (p == '$ref') then | |||
table.insert(categories, | for category in v:gmatch("Category:([^?]+)") do -- e.g. "/wiki/Category:Test?action=raw" | ||
if (includeNamespace) then category = "Category:" .. category end | |||
table.insert(categories, category) | |||
end | |||
if includeSchemas then | |||
for schema in v:gmatch("JsonSchema:([^?]+)") do -- e.g. "/wiki/JsonSchema:Test?action=raw" | |||
if (includeNamespace) then schema = "JsonSchema:" .. schema end | |||
table.insert(categories, schema) | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
Line 739: | Line 803: | ||
context = p.tableMerge(context, subcontext) -- pull up nested context | context = p.tableMerge(context, subcontext) -- pull up nested context | ||
local values = {} | local values = {} | ||
if (v[1] == nil) then --key value array = object/dict | if (p.tableLength(v) > 0 and v[1] == nil) then --key value array = object/dict => subobject | ||
local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=v, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]}) | local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=v, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]}) | ||
local id = subproperties_res.id --subobject_id | local id = subproperties_res.id --subobject_id | ||
Line 790: | Line 854: | ||
p.tableMerge(properties['@category'], properties[p.keys.category_pseudoproperty]) -- from json-ld context 'Property:Category' | p.tableMerge(properties['@category'], properties[p.keys.category_pseudoproperty]) -- from json-ld context 'Property:Category' | ||
properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property | properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property | ||
local display_label = p.getDisplayLabel(jsondata, properties) | |||
if properties['Display title of'] == nil and properties['Display_title_of'] == nil then | |||
if (display_label ~= nil and display_label ~= "") then properties['Display title of'] = display_label | |||
else properties['Display title of'] = p.defaultArg(subschema['title'], "") end -- fall back to property name in schema | |||
end | |||
p.setNormalizedLabel(properties) --build normalized multilang label | p.setNormalizedLabel(properties) --build normalized multilang label | ||
if (p.tableLength(properties) > 0) then | if (p.tableLength(properties) > 0) then | ||
Line 1,032: | Line 1,099: | ||
for k, v in pairs(obj) do res[p.copy(k, s)] = p.copy(v, s) end | for k, v in pairs(obj) do res[p.copy(k, s)] = p.copy(v, s) end | ||
return res | return res | ||
end | |||
-- get normalized label | |||
function p.getDisplayLabel(jsondata, properties) | |||
local display_label = nil | |||
-- check if label properties are mapped | |||
if (properties["HasLabel"] ~= nil and properties["HasLabel"][1] ~= nil) then display_label = p.splitString(properties["HasLabel"][1], '@')[1] | |||
elseif (properties["HasName"] ~= nil) then | |||
if type(properties["HasName"]) == 'table' then display_label = properties["HasName"][1] | |||
else display_label = properties["HasName"] end | |||
-- fall back to unmapped keywords | |||
elseif (jsondata[p.keys.label] ~= nil and jsondata[p.keys.label][1] ~= nil) then display_label = p.splitString(jsondata[p.keys.label][1], '@')[1] | |||
elseif (jsondata[p.keys.name] ~= nil) then display_label = jsondata[p.keys.name] | |||
end | |||
return display_label | |||
end | end | ||