Для документации этого модуля может быть создана страница Модуль:WD/doc

local wd = {
--
----------------------------------------------------------------------------------------------------------
-- Константы ET_xxx (entity types)
--
ET_ITEM       = "item",
ET_PROPERTY   = "property",
ET_STATEMENT  = "statement",
--
----------------------------------------------------------------------------------------------------------
-- Константы DT_xxx (data types)
--
DT_ITEM       = "wikibase-item",        
DT_PROPERTY   = "wikibase-property",    
DT_DATETIME   = "time",                 
DT_COMMONS    = "commonsMedia",         
DT_STRING     = "string",               
DT_URL        = "url",                  
DT_ML_STRING  = "monolingualtext",      
DT_COORD      = "globe-coordinate",     
DT_QUANTITY   = "quantity",             
--
----------------------------------------------------------------------------------------------------------
-- Константы VT_xxx (value types)
--
VT_ENTITY_ID  = "wikibase-entityid",
VT_DATETIME   = "time",
VT_COORD      = "globecoordinate",
VT_STRING     = "string",
VT_ML_STRING  = "monolingualtext",
VT_QUANTITY   = "quantity",
--
----------------------------------------------------------------------------------------------------------
-- Константы ST_xxx (snak types)
--
ST_VALUE     = "value",        -- "конкретное значение"
ST_UNKNOWN   = "somevalue",    -- "значение неизвестно"
ST_NO_VALUE  = "novalue",      -- "значение отсутствует"
--
----------------------------------------------------------------------------------------------------------
-- Константы RANK_xxx (ranks)
--
RANK_NORNAL      = "normal",       -- "нормальный ранг"
RANK_PREFERRED   = "preferred",    -- "предпочтительный ранг"
RANK_DEPRECATED  = "deprecated",   -- "нерекомендуемый ранг"
--
----------------------------------------------------------------------------------------------------------
-- Константы TP_xxx (time precision)
--
TP_10E8_YEARS  =  1,   -- 100 млн лет
TP_10E7_YEARS  =  2,   -- 10 млн лет
TP_10E6_YEARS  =  3,   -- 1 млн лет
TP_10E5_YEARS  =  4,   -- 100 тыс лет
TP_10E4_YEARS  =  5,   -- 10 тыс лет
TP_10E3_YEARS  =  6,   -- 1 тыс лет
TP_CENTURY     =  7,   -- 100 лет
TP_DECADE      =  8,   -- 10 лет
TP_YEAR        =  9,   -- 1 год
TP_MONTH       = 10,   -- 1 месяц
TP_DAY         = 11,   -- 1 день
--
----------------------------------------------------------------------------------------------------------
-- Константы CM_xxx (calendar model)
--
CM_GREGORIAN  = "http://www.wikidata.org/entity/Q1985727",  -- пролептический григорианский календарь
CM_JULIAN     = "http://www.wikidata.org/entity/Q1985786",  -- пролептический юлианский календарь
--
----------------------------------------------------------------------------------------------------------
-- Константа GLOBE_xxx (небесное тело, к которому относятся координаты)
--
GLOBE_EARTH   = "http://www.wikidata.org/entity/Q2",    -- Земля
GLOBE_MOON    = "http://www.wikidata.org/entity/Q405"   -- Луна
--
};
--
----------------------------------------------------------------------------------------------------------
-- Константы
--
local LANG_EN = "en";
local MSG_NO_LABEL = "нет метки";
local MSG_NO_DESCRIPTION = "нет описания";
--
----------------------------------------------------------------------------------------------------------
-- Сообщения об ошибках
--
local ERRMSG_PROPERTY_NOT_FOUND = "свойство %s не найдено";
local ERRMSG_ITEM_NOT_FOUND = "элемент %s не найден";
local ERRMSG_ENTITY_NOT_FOUND = "сущность %s не найдена";
local ERRMSG_CLAIM_NOT_FOUND = "%s не имеет %s";
--
----------------------------------------------------------------------------------------------------------
-- БЕЗОПАСНАЯ ФУНКЦИЯ ПОЛУЧЕНИЯ ДАННЫХ (Предотвращает ошибку "a nil value")
--
local function get_entity_safe(id)
  if mw.wikibase and mw.wikibase.getEntity then
    return mw.wikibase.getEntity(id)
  end
  return nil
end
--
----------------------------------------------------------------------------------------------------------
--
function wd.is_property(entity)
  return (entity ~= nil) and (entity["type"] == wd.ET_PROPERTY);
end;
--
function wd.is_item(entity)
  return (entity ~= nil) and (entity["type"] == wd.ET_ITEM);
end;
--
function wd.is_statement(statement)
  return (statement ~= nil) and (statement["type"] == wd.ET_STATEMENT);
end;
--
function wd.get_statement_value(statement, s_datatype, s_value_type)
  if statement == nil then
    return nil;
  end;
  local snak = statement["mainsnak"];
  if (snak == nil) or (snak["datatype"] ~= s_datatype) or (snak["snaktype"] ~= wd.ST_VALUE) then
    return nil;
  end;
  local datavalue = snak["datavalue"];
  if (datavalue == nil) or (datavalue["type"] ~= s_value_type) then
    return nil;
  end;
  return datavalue["value"];
end;
--
function wd.get_item_id(statement)
  local value = wd.get_statement_value(statement, wd.DT_ITEM, wd.VT_ENTITY_ID);
  if (value == nil) or (value["entity-type"] ~= wd.ET_ITEM) then
    return nil;
  end;
  return value["numeric-id"];
end;
--
function wd.get_item_qid(statement)
  local n_id = wd.get_item_id(statement);
  if n_id == nil then
    return nil;
  else
    return "Q" .. tostring(n_id);
  end;
end;
--
function wd.has_valid_item_value (entity, s_property_id, n_item_id)
  local claim = nil;
  if (entity ~= nil) and (entity.claims ~= nil) then
    claim = entity.claims[s_property_id];
  end;
  if claim ~= nil then
    for key, value in pairs(claim) do
      if (value.rank ~= wd.RANK_DEPRECATED) and (wd.get_item_id(value) == n_item_id) then
        return true;
      end;
    end;
  end;
  return false;
end;
--
function wd.get_label(entity, s_lang, s_default)
  local s_label = s_default;
  if entity["labels"] ~= nil then
    local l = entity.labels[s_lang];
    if (l == nil) and (s_lang ~= LANG_EN) then
      l = entity.labels[LANG_EN];
    end;
    if l ~= nil then
      s_label = l.value;
    end;
  end;
  return s_label;
end;
--
function wd.get_description(entity, s_lang, s_default)
  local s_description = s_default;
  if entity["descriptions"] ~= nil then
    local l = entity.descriptions[s_lang];
    if (l == nil) and (s_lang ~= LANG_EN) then
      l = entity.descriptions[LANG_EN];
    end;
    if l ~= nil then
      s_description = l.value;
    end;
  end;
  return s_description;
end;
--
----------------------------------------------------------------------------------------------------------
-- Функции для получения ссылки на локальный проект
--
function wd.get_sitelink(entity, s_code)
  if entity["sitelinks"] == nil then
    return nil;
  end;
  return entity.sitelinks[s_code];
end;
--
function wd.get_sitelink_by_lang(entity, s_suffix, langs)
  if entity["sitelinks"] == nil then
    return nil;
  end;
  local sitelink = nil;
  for key, s_lang in pairs(langs) do
    sitelink = entity.sitelinks[s_lang .. s_suffix];
    if sitelink ~= nil then
      break;
    end;
  end;
  return sitelink;
end;
--
local map = {
  wiki = ""
};
--
local lang_map = {
  wiki = ""
};
--
function wd.map_sitelink_to_prefix(s_site)
  local s_iw_prefix = map[s_site];
  if s_iw_prefix ~= nil then
    return s_iw_prefix;
  end;
  local n_len = mw.ustring.len(s_site);
  for s_suffix, s_iw_prefix in pairs(lang_map) do
    local n_suffix_len = mw.ustring.len(s_suffix);
    local n_lang_len = n_len - n_suffix_len;
    if (n_lang_len > 0) and (mw.ustring.sub(s_site, -n_suffix_len) == s_suffix) then
      return s_iw_prefix .. mw.ustring.sub(s_site, 1, n_lang_len) .. ":";
    end;
  end;
  return ""; -- Возвращаем пустую строку вместо ошибки для локальной вики
end;
--
function wd.map_sitelink (frame)
  return "[["..wd.map_sitelink_to_prefix(tostring(frame.args[1])).."]]";
end;
--
----------------------------------------------------------------------------------------------------------
-- Возвращает метку элемента или свойства
--
function wd.label (frame)
  local s_id = tostring(frame.args[1]);
  local s_lang = tostring(frame.args[2]);
  local entity = get_entity_safe(s_id);
  if entity == nil then
    if mw.ustring.match(s_id, "[Qq]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_id));
    elseif mw.ustring.match(s_id, "[Pp]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_id));
    else
      error(mw.ustring.format(ERRMSG_ENTITY_NOT_FOUND, s_id));
    end;
  end;
  return wd.get_label(entity, s_lang, MSG_NO_LABEL);
end;
--
function wd.description (frame)
  local s_id = tostring(frame.args[1]);
  local s_lang = tostring(frame.args[2]);
  local entity = get_entity_safe(s_id);
  if entity == nil then
    if mw.ustring.match(s_id, "[Qq]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_id));
    elseif mw.ustring.match(s_id, "[Pp]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_id));
    else
      error(mw.ustring.format(ERRMSG_ENTITY_NOT_FOUND, s_id));
    end;
  end;
  return wd.get_description(entity, s_lang, MSG_NO_DESCRIPTION);
end;
--
function wd.datatype (frame)
  local s_id = tostring(frame.args[1]);
  local entity = get_entity_safe(s_id);
  if (entity == nil) or (not wd.is_property(entity)) then
    error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_id));
  end;
  return entity.datatype;
end;
--
function wd.sitelink (frame)
  local s_item_id = tostring(frame.args[1]);
  local s_code = tostring(frame.args[2]);
  local entity = get_entity_safe(s_item_id);
  if (entity == nil) or not wd.is_item(entity) then
    error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_item_id));
  end;
  local sitelink = nil;
  local s_lang = frame.args[3];
  if s_lang == nil then    -- если третий параметр не указан, вызываем get_sitelink
    sitelink = wd.get_sitelink(entity, s_code);
  else
    local langs = {}; -- пустой массив
    langs[3] = tostring(s_lang);
    local n_index = 4;
    s_lang = frame.args[3];
    while s_lang ~= nil do
      langs[n_index] = tostring(s_lang);
      n_index = n_index + 1;
      s_lang = frame.args[n_index];
    end;
    sitelink = wd.get_sitelink_by_lang(entity, s_code, langs);
  end;
  if sitelink == nil then
    return nil;
  else
    return wd.map_sitelink_to_prefix(sitelink.site) .. sitelink.title;
  end;
end;
--
-- s_suffix - sitelink проекта
function wd.get_multilingual_sitelink(entity, s_suffix, lang_properties)
  if (entity == nil) or (entity["sitelinks"] == nil) then
    return nil;
  end;
  local sitelink = entity.sitelinks["ru" .. s_suffix];
  if sitelink ~= nil then
    return wd.map_sitelink_to_prefix(sitelink.site) .. sitelink.title;
  elseif lang_properties == nil then
    return nil;
  end;
  -- собираем языки в langs
  local langs = {Q7737="ru"};   -- список языков, sitelink'и для которых мы уже проверили
  local lang_cache = nil;          -- кэш языков 
  for key1, s_lang_property in pairs(lang_properties) do
    for key2, statement in pairs(entity:getBestStatements(s_lang_property)) do
      local s_lang_qid = wd.get_item_qid(statement);
      if (s_lang_qid ~= nil) and langs[s_lang_qid] == nil then
        -- Упрощено для локальной вики без словарных модулей
        langs[s_lang_qid] = "unknown";
      end;
    end;
  end;
  return nil;
end;
--
----------------------------------------------------------------------------------------------------------
-- Возвращает строку вида: [[:d:Property:Pxxx|<метка> (Pxxx)]]
--
function wd.format_property (frame)
  local s_property_id = tostring(frame.args[1]);
  local s_lang = tostring(frame.args[2]);
  local entity = get_entity_safe(s_property_id);
  if (entity == nil) or not wd.is_property(entity) then
    error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_property_id));
  end;
  return "[[:d:Property:" .. entity.id .. "|" .. wd.get_label(entity, s_lang, MSG_NO_LABEL) .. " (" .. entity.id .. ")]]";
end;
--
----------------------------------------------------------------------------------------------------------
-- Возвращает строку вида: [[:d:Qxxx|<метка> (Qxxx)]]
--
function wd.format_item (frame)
  local s_item_id = tostring(frame.args[1]);
  local s_lang = tostring(frame.args[2]);
  local entity = get_entity_safe(s_item_id);
  if (entity == nil) or not wd.is_item(entity) then
    error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_item_id));
  end;
  return "[[:d:" .. entity.id .. "|" .. wd.get_label(entity, s_lang, MSG_NO_LABEL) .. " (" .. entity.id .. ")]]";
end;
--
----------------------------------------------------------------------------------------------------------
-- Сервисная функция, позволяющая преобразовать таблицу LUA в её текстовое представление
--
function wd.table_to_string(t, s_indent)
  local s_result = "{";
  local b_first = true;
  for key, value in pairs(t) do
    if b_first then
      s_result = s_result .. "<br/>";  -- перед первым элементом добавляем перевод строки
      b_first = false;                 -- и сбрасываем флажок
    else
      s_result = s_result .. ",<br/>";  -- перед всеми последующими добавляем запятую и перевод строки
    end;
    local s_str = "";
    if value == nil then
      s_str = "nil";
    elseif type(value) == "table" then
      s_str = wd.table_to_string(value, s_indent .. "&nbsp;&nbsp;");
    elseif type(value) == "string" then
      s_str = "\"" .. value .. "\"";
    else -- type(value) == "number", "boolean" или "function"
      s_str = tostring(value);
    end;
    if (type(key) == "number") or (type(key) == "boolean") then
      s_result = s_result .. s_indent .. tostring(key) .. ": " .. s_str;
    else
      s_result = s_result .. s_indent .. "\"" .. tostring(key) .. "\": " .. s_str;
    end;
  end;
  if not b_first then
    s_result = s_result .. "<br/>";  -- если был хотя бы один элемент, добавляем перевод строки
  end;
  return s_result .. s_indent .. "}";
end;
--
----------------------------------------------------------------------------------------------------------
-- Отладочная функция, позволяющая показать элемент или свойство Викиданных
--
function wd.dump_entity(frame)
  local s_id = tostring(frame.args[1]);
  local entity = get_entity_safe(s_id);
  if entity == nil then
    if mw.ustring.match(s_id, "[Qq]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_id));
    elseif mw.ustring.match(s_id, "[Pp]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_id));
    else
      error(mw.ustring.format(ERRMSG_ENTITY_NOT_FOUND, s_id));
    end;
  end;
  return wd.table_to_string(entity, "");
end;
--
function wd.dump_sitelinks(frame)
  local s_id = tostring(frame.args[1]);
  local entity = get_entity_safe(s_id);
  if (entity == nil) or not wd.is_item(entity) then
    error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_id));
  end;
  if entity["sitelinks"] == nil then
    return "nil"
  else
    return wd.table_to_string(entity.sitelinks, "");
  end;
end;
--
function wd.dump_statements(frame)
  local s_id = tostring(frame.args[1]);
  local s_property_id = tostring(frame.args[2]);
  local entity = get_entity_safe(s_id);
  if entity == nil then
    if mw.ustring.match(s_id, "[Qq]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_id));
    elseif mw.ustring.match(s_id, "[Pp]%d+") ~= nil then
      error(mw.ustring.format(ERRMSG_PROPERTY_NOT_FOUND, s_id));
    else
      error(mw.ustring.format(ERRMSG_ENTITY_NOT_FOUND, s_id));
    end;
  end;
  local claim = nil;
  if entity.claims ~= nil then
    claim = entity.claims[s_property_id];
  end;
  if claim == nil then
    error(mw.ustring.format(ERRMSG_CLAIM_NOT_FOUND, s_id, s_property_id));
  end;
  return wd.table_to_string(claim, "");
end;

return wd;