Перейти к содержанию

Модуль:WD

Материал из Викитеки — свободной библиотеки
Документация Документация

Назначение

В модуле собираются сервисные фукнции, связанные с чтением Викиданных.

Функции, доступные из вики-разметки

label()

Описание

Возвращает метку свойства или элемента Викиданных на указанном языке. Если на указанном языке метки нет, возвращается метка на английском. Если нет метки и на английском, возвращается «нет метки» (константа MSG_NO_LABEL). Если указать несуществующее свойство или элемент Викиданных, будет сгенерирована ошибка Lua (см. константы ERRMSG_PROPERTY_NOT_FOUND, ERRMSG_ITEM_NOT_FOUND и ERRMSG_ENTITY_NOT_FOUND). Для элементов-перенаправлений возвращается метка элемента, на который оно указывает.
Функция используется в шаблоне {{WD label}}.

Использование

{{#invoke:WD|label|идентификатор|язык}}

Параметры:

  • идентификатор — идентификатор свойства (Pxxx или pxxx) или элемента Викиданных (Qxxx или qxxx);
  • язык — двухбуквенный код языка (ru, en, de и т. д.).

Примеры для свойств:

  • {{#invoke:WD|label|P569|ru}} → дата рождения
  • {{#invoke:WD|label|P569|en}} → date of birth
  • {{#invoke:WD|label|P569|de}} → Geburtsdatum
  • {{#invoke:WD|label|P569|la}} → dies natalis
  • {{#invoke:WD|label|P97|ru}} → титул
  • {{#invoke:WD|label|P97|en}} → noble title
  • {{#invoke:WD|label|P97|la}} (нет метки на латыни) → noble title

Примеры для элементов:

  • {{#invoke:WD|label|Q2|ru}} → Земля
  • {{#invoke:WD|label|Q2|en}} → Earth
  • {{#invoke:WD|label|Q2|de}} → Erde
  • {{#invoke:WD|label|Q2|la}} → Tellus
  • {{#invoke:WD|label|Q8258093|ru}} (перенаправление Q8258093 → Q6305566) → Category:Aquitani

Примеры ошибок:

  • {{#invoke:WD|label|P999999|ru}} (несуществующее свойство) → ошибка Lua «свойство P999999 не найдено»
  • {{#invoke:WD|label|Q6|ru}} (несуществующий элемент) → ошибка Lua «элемент Q6 не найден»
  • {{#invoke:WD|label|BOND007|ru}} (неверный идентификатор) → ошибка Lua «Введённый идентификатор не известен системе. Используйте один из действующих идентификаторов сущностей»

description()

Описание

Возвращает описание свойства или элемента Викиданных на указанном языке. Если на указанном языке описания нет, возвращается описание на английском. Если нет описания и на английском, возвращается «нет описания» (константа MSG_NO_DESCRIPTION). Если указать несуществующее свойство или элемент Викиданных, будет сгенерирована ошибка Lua (см. константы ERRMSG_PROPERTY_NOT_FOUND, ERRMSG_ITEM_NOT_FOUND и ERRMSG_ENTITY_NOT_FOUND). Для элементов-перенаправлений возвращается описание элемента, на который оно указывает.
Функция используется в шаблоне {{WD description}}.

Использование

{{#invoke:WD|description|идентификатор|язык}}

Параметры:

  • идентификатор — идентификатор свойства (Pxxx или pxxx) или элемента Викиданных (Qxxx или qxxx);
  • язык — двухбуквенный код языка (ru, en, de и т. д.).

Примеры для свойств:

  • {{#invoke:WD|description|P569|ru}} → день, месяц, год, в который субъект был рождён
  • {{#invoke:WD|description|P569|en}} → date on which the subject was born
  • {{#invoke:WD|description|P569|de}} → Datum, an dem eine Person geboren wurde
  • {{#invoke:WD|description|P97|ru}} → дворянский титул персоны
  • {{#invoke:WD|description|P97|en}} → titles held by the person
  • {{#invoke:WD|description|P97|la}} (нет описания на латыни) → titles held by the person

Примеры для элементов:

  • {{#invoke:WD|description|Q2|ru}} → третья от Солнца планета о солнечное системе
  • {{#invoke:WD|description|Q2|en}} → third planet from the Sun in the Solar System
  • {{#invoke:WD|description|Q2|de}} → dritter Planet von der Sonne aus im Sonnensystem
  • {{#invoke:WD|description|Q2|la}} (нет описания на латыни) → tertius planeta a Sole in systemate solari
  • {{#invoke:WD|description|Q8258093|ru}} (перенаправление Q8258093 → Q6305566) → категория в проекте Викимедиа

Примеры ошибок:

  • {{#invoke:WD|description|P999999|ru}} (несуществующее свойство) → ошибка Lua «свойство P999999 не найдено»
  • {{#invoke:WD|description|Q6|ru}} (несуществующий элемент) → ошибка Lua «элемент Q6 не найден»
  • {{#invoke:WD|description|BOND007|ru}} (неверный идентификатор) → ошибка Lua «Введённый идентификатор не известен системе. Используйте один из действующих идентификаторов сущностей»

datatype()

Описание

Возвращает тип данных свойства Викиданных (одно из значений констант DT_xxx). Если свойство не найдено или функции передан идентификатор элемента, генерируется ошибка Lua (константа ERRMSG_PROPERTY_NOT_FOUND).

Использование

{{#invoke:WD|datatype|идентификатор}}

Примеры:

  • {{#invoke:WD|datatype|P31}} → wikibase-item
  • {{#invoke:WD|datatype|P1687}} → wikibase-property
  • {{#invoke:WD|datatype|P304}} → string
  • {{#invoke:WD|datatype|P1476}} → monolingualtext
  • {{#invoke:WD|datatype|P18}} → commonsMedia
  • {{#invoke:WD|datatype|P854}} → url
  • {{#invoke:WD|datatype|P569}} → time
  • {{#invoke:WD|datatype|P625}} → globe-coordinate
  • {{#invoke:WD|datatype|P2067}} → quantity

Примеры ошибок:

  • {{#invoke:WD|datatype|P999999}} (несуществующее свойство) → ошибка Lua «свойство P999999 не найдено»
  • {{#invoke:WD|datatype|Q2}} (элемент) → ошибка Lua «свойство Q2 не найдено»
  • {{#invoke:WD|datatype|BOND007}} (неверный идентификатор) → ошибка Lua «Введённый идентификатор не известен системе. Используйте один из действующих идентификаторов сущностей»

format_property()

Описание

Для указанного свойства Викиданных возвращает ссылку на него в виде [[:d:Property:Pxxx|<метка> (Pxxx)]] с меткой на указанном языке. Если на указанном языке метки нет, используется метка на английском. Если нет метки и на английском, вместо неё используется «нет метки». Если свойство не найдено или функции передан идентификатор элемента, генерируется ошибка Lua (константа ERRMSG_PROPERTY_NOT_FOUND).
Функция используется в шаблоне {{WD property}}

Использование

{{#invoke:WD|format_property|идентификатор|язык}}

Параметры:

  • идентификатор — идентификатор свойства Викиданных (Pxxx или pxxx);
  • язык — двухбуквенный код языка (ru, en, de и т. д.).

Примеры:

Примеры ошибок:

  • {{#invoke:WD|format_property|P999999|ru}} (несуществующее свойство) → ошибка Lua «свойство P999999 не найдено»
  • {{#invoke:WD|format_property|Q2|ru}} (не свойство) → ошибка Lua «свойство Q2 не найдено»
  • {{#invoke:WD|format_property|BOND007|ru}} (неверный идентификатор) → ошибка Lua «Введённый идентификатор не известен системе. Используйте один из действующих идентификаторов сущностей»

format_item()

Описание

Для указанного элемента Викиданных возвращает ссылку на него в виде [[:d:Qxxx|<метка> (Qxxx)]] с меткой на указанном языке. Если на указанном языке метки нет, используется метка на английском. Если нет метки и на английском, вместо неё используется «нет метки». Если элемент не найден или функции передан идентификатор свойства, генерируется ошибка Lua (константа ERRMSG_ITEM_NOT_FOUND). Для элементов-перенаправлений возвращается ссылка метка и идентификатор на того элемента, на который оно указывает.
Функция используется в шаблоне {{WD item}}.

Использование

{{#invoke:WD|format_item|идентификатор|язык}}

Параметры:

  • идентификатор — идентификатор элемента Викиданных (Qxxx или qxxx);
  • язык — двухбуквенный код языка (ru, en, de и т. д.).

Примеры:

Примеры ошибок:

  • {{#invoke:WD|format_item|Q6|ru}} (несуществующий элемент) → ошибка Lua «элемент Q6 не найден»
  • {{#invoke:WD|format_item|P569|ru}} (не элемент) → ошибка Lua «элемент P569 не найден»
  • {{#invoke:WD|format_item|BOND007|ru}} (неверный идентификатор) → ошибка Lua «Введённый идентификатор не известен системе. Используйте один из действующих идентификаторов сущностей»

dump_entity()

Описание

Отладочная функция, позволяющая показать значение в формате JSON, возвращаемое функцией mw.wikibase.getEntityObject() для указанного элемента или свойства Викиданных.

Использование

{{#invoke:WD|dump_entity|идентификатор}}

Параметр:

  • идентификатор — идентификатор свойства (Pxxx или pxxx) или элемента Викиданных (Qxxx или qxxx).

Примеры (из-за объёмности вывода результат не показан, чтобы увидеть его, используйте Служебная:Песочница для шаблонов):

  • {{#invoke:WD|dump_entity|P569}}
  • {{#invoke:WD|dump_entity|Q2188189}}

dump_sitelinks()

Описание

Отладочная функция, позволяющая показать ссылки в формате JSON для указанного элемента Викиданных.

Использование

{{#invoke:WD|dump_sitelinks|идентификатор}}

Параметр:

  • идентификатор — идентификатор элемента Викиданных (Qxxx или qxxx).

Пример:

Вызов Результат
{{#invoke:WD|dump_sitelinks|Q15920521}} {
"ruwikisource": {
  "site": "ruwikisource",
  "title": "Ночные мысли (Гейне; Михайлов)",
  "badges": {    }
  }
}
{{#invoke:WD|dump_sitelinks|Q4100335}}
{{#invoke:WD|dump_sitelinks|Q644102}}

dump_statements()

Описание

Отладочная функция, позволяющая показать утверждения данного свойства в формате JSON для указанного элемента или свойства Викиданных.

Использование

{{#invoke:WD|dump_statements|идентификатор|свойство}}

Параметры:

  • идентификатор — идентификатор свойства (Pxxx или pxxx) или элемента Викиданных (Qxxx или qxxx);
  • свойство — идентификатор свойства (Pxxx или pxxx), значение которого надо показать.

Примеры:

Вызов Результат
{{#invoke:WD|dump_statements|Q359|P856}}
{{#invoke:WD|dump_statements|P527|P1696}} {
1: {
  "mainsnak": {
    "snaktype": "value",
    "property": "P1696",
    "datavalue": {
      "value": {
        "id": "P361",
        "numeric-id": 361,
        "entity-type": "property"
        },
      "type": "wikibase-entityid"
      },
    "datatype": "wikibase-property"
    },
  "type": "statement",
  "id": "P527$a8ab8c55-4864-573a-67d0-613917797d1d",
  "rank": "normal"
  }
}
{{#invoke:WD|dump_statements|Q15920521|P1476}} {
1: {
  "mainsnak": {
    "snaktype": "value",
    "property": "P1476",
    "datavalue": {
      "value": {
        "language": "ru",
        "text": "Ночные мысли"
        },
      "type": "monolingualtext"
      },
    "datatype": "monolingualtext"
    },
  "type": "statement",
  "id": "Q15920521$f7c04336-4b68-8307-c98c-569412466eaa",
  "rank": "normal"
  }
}
{{#invoke:WD|dump_statements|Q4100335|P570}}

Функции, доступные из других модулей

is_property()

is_item()

is_statement()

get_statement_value()

get_item_id()

get_item_qid()

has_valid_item_value()

get_label()

get_description()

get_sitelink()

table_to_string()

local wd = {
--
----------------------------------------------------------------------------------------------------------
-- Константы ET_xxx	(entity types)
--
ET_ITEM       = "item",
ET_PROPERTY   = "property",
ET_STATEMENT  = "statement",
--
----------------------------------------------------------------------------------------------------------
-- Константы DT_xxx	(data types)
--
DT_ITEM       = "wikibase-item",        --  "datavalue": { "value": { "numeric-id": 1551807, "entity-type": "item" }, "type": "wikibase-entityid" }
DT_PROPERTY   = "wikibase-property",    --  "datavalue": { "value": { "numeric-id": 1753, "entity-type": "property" }, "type": "wikibase-entityid" }
DT_DATETIME   = "time",                 --  "datavalue": { "value": { "before": 0, "time": "+2013-10-28T00:00:00Z", "timezone": 0, "precision": 11, "after": 0,
                                        --                            "calendarmodel": "http://www.wikidata.org/entity/Q1985727"},
                                        --                 "type": "time"}
DT_COMMONS    = "commonsMedia",         --  "datavalue": { "value": "Turgenev by Repin.jpg", "type": "string" }
DT_STRING     = "string",               --  "datavalue": { "value": "/m/04j_pj", "type": "string" }
DT_URL        = "url",                  --  "datavalue": { "value": "http://www.acme.com/", "type": "string" }
DT_ML_STRING  = "monolingualtext",      --  "datavalue": { "value": { "language": "ru", "text": "Накануне" }, "type": "monolingualtext" }
DT_COORD      = "globe-coordinate",     --  "datavalue": { "value": { "longitude": 30.352547, "precision": 1e-05,
                                        --                            "globe": "http://www.wikidata.org/entity/Q2", "latitude": 59.943044 },
                                        --                 "type": "globecoordinate" }
DT_QUANTITY   = "quantity",             --  "datavalue": { "value": { "amount": "+103", "upperBound": "+104", "lowerBound": "+102",
                                        --                            "unit": "http://www.wikidata.org/entity/Q42289" },
                                        --                 "type": "quantity" }
DT_EXT_ID     = "external-id",          --  "datavalue": { "value": "tt0058770", "type": "string" }
--
----------------------------------------------------------------------------------------------------------
-- Константы VT_xxx	(value types)
--
VT_ENTITY_ID  = "wikibase-entityid",   -- используется для DT_ITEM и DT_PROPERTY
VT_DATETIME   = "time",                -- используется для DT_DATETIME
VT_COORD      = "globecoordinate",     -- используется для DT_COORD
VT_STRING     = "string",              -- используется для DT_STRING, DT_URL и DT_COMMONS
VT_ML_STRING  = "monolingualtext",     -- используется для DT_ML_STRING
VT_QUANTITY   = "quantity",            -- используется для DT_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"
--
----------------------------------------------------------------------------------------------------------
--
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["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
--
----------------------------------------------------------------------------------------------------------
-- Функции для получения ссылки на данный вики-проект в виде (пример для Q644102):
--   { "site": "ruwiki", "title": "Спасо-Преображенский собор (Санкт-Петербург)", "badges": { 1: "Q17437798" } }
-- Если ссылки нет, возвращает nil.
-- Примеры
--   local sitelink = wd.get_sitelink(entity, "ruwiki")
--   local sitelink = wd.get_sitelink_by_lang(entity, "wiki", ["ru", "de", "en"])
--
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 = {
  ruwikisource    = "",
  ruwiki          = "w:",
  ruwikiquote     = "q:",
  ruwikinews      = "n:",
  ruwikivoyage    = "voy:",
  commonswiki     = "commons:",
  wikidatawiki    = "d:",
  specieswiki     = "species:"
}
--
local lang_map = {
  wikisource    = ":",
  wiki          = "w:",
  wikiquote     = "q:",
  wikinews      = "n:",
  wikivoyage    = "voy:"
}
--
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
    --mw.log("suffix: "..s_suffix)
    --mw.log("interwiki prefix: "..s_iw_prefix)
    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
  error("Can't map " .. s_site .. " to a interwiki link")
end
--
--  {{#invoke:WD|map_sitelink|ruwikisource}}
--  {{#invoke:WD|map_sitelink|enwikisource}}
--  {{#invoke:WD|map_sitelink|enwiki}}
--  {{#invoke:WD|map_sitelink|ruwiki}}
function wd.map_sitelink (frame)
  return "[["..wd.map_sitelink_to_prefix(tostring(frame.args[1])).."]]"
end
--
--  {{#invoke:WD|dump_iwikimap}}
--  {{#invoke:WD|dump_iwikimap|local}}
--  {{#invoke:WD|dump_iwikimap|!local}}
--function wd.dump_iwikimap (frame)
--  return wd.table_to_string(mw.site.interwikiMap(tostring(frame.args[1])), "")
--end
--
----------------------------------------------------------------------------------------------------------
-- Функция для Шаблон:WD label
-- Возвращает метку элемента или свойства
--
-- Примеры для свойств:
--  {{#invoke:WD|label|P569|ru}}  → дата рождения
--  {{#invoke:WD|label|P569|en}}  → date of birth
--  {{#invoke:WD|label|P569|de}}  → Geburtsdatum
--  {{#invoke:WD|label|P569|la}}  → natus
--
--  {{#invoke:WD|label|P97|ru}}  → титул
--  {{#invoke:WD|label|P97|en}}  → noble title
--  {{#invoke:WD|label|P97|la}}  → noble title (так как метки на латыни нет)
--
--  {{#invoke:WD|label|P999999|ru}}  → ошибка  (несуществующее свойство)
--
-- Примеры для элементов:
--  {{#invoke:WD|label|Q2|ru}} → Земля
--  {{#invoke:WD|label|Q2|en}} → Earth
--  {{#invoke:WD|label|Q2|de}} → Erde
--  {{#invoke:WD|label|Q2|la}} → Tellus
--
--  {{#invoke:WD|label|Q6|ru}} → ошибка  (несуществующий элемент)
--  {{#invoke:WD|label|Q8258093|ru}} → Category:Aquitanians (перенаправление Q8258093 → Q6305566)
--
function wd.label (frame)
  local s_id = tostring(frame.args[1])
  local s_lang = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(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 = mw.wikibase.getEntityObject(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 = mw.wikibase.getEntityObject(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
--
-- [[{{#invoke:WD|sitelink|Q9301454|itwiki}}]]
-- [[{{#invoke:WD|sitelink|Q213141|specieswiki}}]]
-- [[{{#invoke:WD|sitelink|Q213141|commonswiki}}]]
-- [[{{#invoke:WD|sitelink|Q4115189|wikidatawiki}}]]
-- [[{{#invoke:WD|sitelink|Q656|ruwikivoyage}}]]
--
function wd.sitelink (frame)
  local s_item_id = tostring(frame.args[1])
  local s_code = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(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 проекта ("wiki", "wikisource", "wikiwikiquote", "wikivoyage" или "wikinews")
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;          -- кэш языков из Модуль:Wikidata:Dictionary/P424
  for key1, s_lang_property in pairs(lang_properties) do
    --mw.log("look at " .. s_lang_property)
    for key2, statement in pairs(entity:getBestStatements(s_lang_property)) do
      local s_lang_qid = wd.get_item_qid(statement)
      --mw.log("  " .. s_lang_qid)
      if s_lang_qid ~= nil and langs[s_lang_qid] == nil then
        if lang_cache == nil then      -- кэш языков загружаем только в первый раз
          lang_cache = mw.loadData('Модуль:Wikidata:Dictionary/P424')
        end
        local l = lang_cache[s_lang_qid]
        if l == nil then
          mw.log("В Модуль:Wikidata:Dictionary/P424 отсутствует язык " .. s_lang_qid)
        else 
          for key3, s_lang in pairs(l) do
            sitelink = entity.sitelinks[s_lang..s_suffix]
            if sitelink ~= nil then
              return wd.map_sitelink_to_prefix(sitelink.site) .. sitelink.title
            end
            langs[s_lang_qid] = s_lang
          end
        end
      end
    end
  end
  return nil
end
--[[
-- {{#invoke:Sandbox|test_get_multilingual_sitelink|Q18090050|wikisource}}
function s.test_get_multilingual_sitelink (frame)
  local s_qid     = tostring(frame.args[1])
  local s_suffix  = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(s_qid)
  local s_sitelink = s.get_multilingual_sitelink(entity, s_suffix, {"P1412","P103"})
  if s_sitelink == nil then
    return "nil"
  else
    return s_sitelink
  end
end
]]
--
----------------------------------------------------------------------------------------------------------
-- Функция для Шаблон:WD property
-- Возвращает строку вида: [[:d:Property:Pxxx|<метка> (Pxxx)]]
--  {{#invoke:WD|format_property|id|lang}}
--
-- Параметры:
--  id — идентификатор свойства Викиданных (Pxxx)
--  lang — двухбуквенный код языка, на котором отображается метка (ru, en, de, etc)
--
-- Если метки на этом языке нет, то возвращается метка на английском (она почти всегда есть)
--
-- Примеры:
--  {{#invoke:WD|format_property|P569|ru}}  → [[:d:Property:P569|дата рождения (P569)]]
--  {{#invoke:WD|format_property|P569|en}}  → [[:d:Property:P569|date of birth (P569)]]
--  {{#invoke:WD|format_property|P569|de}}  → [[:d:Property:P569|Geburtsdatum (P569)]]
--  {{#invoke:WD|format_property|P569|la}}  → [[:d:Property:P569|natus (P569)]]
--
--  {{#invoke:WD|format_property|P97|ru}}  → [[:d:Property:P97|титул (P97)]]
--  {{#invoke:WD|format_property|P97|en}}  → [[:d:Property:P569|noble title (P97)]]
--  {{#invoke:WD|format_property|P97|la}}  → [[:d:Property:P569|noble title (P97)]] (так как метки на латыни нет)
--
--  {{#invoke:WD|format_property|P999999|ru}}  → ошибка  (несуществующее свойство)
--
function wd.format_property (frame)
  local s_property_id = tostring(frame.args[1])
  local s_lang = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(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
--
----------------------------------------------------------------------------------------------------------
-- Функция для Шаблон:WD item
-- Возвращает строку вида: [[:d:Qxxx|<метка> (Qxxx)]]
--  {{#invoke:WD|format_item|id|lang}}
--
-- Параметры:
--  id — идентификатор элемента Викиданных (Qxxx)
--  lang — двухбуквенный код языка, на котором отображается метка (ru, en, de, etc)
--
-- Если метки на этом языке нет, то возвращается метка на английском
--
-- Примеры:
--  {{#invoke:WD|format_item|Q2|ru}} → [[:d:Q2|Земля (Q2)]]
--  {{#invoke:WD|format_item|Q2|en}} → [[:d:Q2|Earth (Q2)]]
--  {{#invoke:WD|format_item|Q2|de}} → [[:d:Q2|Erde (Q2)]]
--  {{#invoke:WD|format_item|Q2|la}} → [[:d:Q2|Tellus (Q2)]]
--
--  {{#invoke:WD|format_item|Q6|ru}} → ошибка (несуществующий элемент)
--  {{#invoke:WD|format_item|Q8258093|ru}} → [[:d:Q6305566|Category:Aquitanians (Q6305566)]] (перенаправление Q8258093 → Q6305566)
--
function wd.format_item (frame)
  local s_item_id = tostring(frame.args[1])
  local s_lang = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(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 = "{"
  -- флажок, чтобы не добавлять запятую после последнего элемента, и чтобы при пустой таблице возвращалось "{" .. s_indent .. "}"
  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
--
----------------------------------------------------------------------------------------------------------
-- Отладочная функция, позволяющая показать элемент или свойство Викиданных, возвращаемое функцией mw.wikibase.getEntityObject()
--
-- Примеры:
-- {{#invoke:WD|dump_entity|Q3207456}}
-- {{#invoke:WD|dump_entity|P31}}
--
function wd.dump_entity(frame)
  local s_id = tostring(frame.args[1])
  local entity = mw.wikibase.getEntityObject(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
--
-- {{#invoke:WD|dump_sitelinks|Q359}}
-- {{#invoke:WD|dump_sitelinks|Q9301454}}
--
function wd.dump_sitelinks(frame)
  local s_id = tostring(frame.args[1])
  local entity = mw.wikibase.getEntityObject(s_id)
  if entity == nil or not wd.is_item(entity) then
    error(mw.ustring.format(ERRMSG_ITEM_NOT_FOUND, s_item_id))
  end
  if entity["sitelinks"] == nil then
    return "nil"
  else
    return wd.table_to_string(entity.sitelinks, "")
  end
end
--
--[[
{{#invoke:WD|dump_property|Q44403|P20}} (item, одно значение)
{{#invoke:WD|dump_property|Q15920521|P19}} (wikibase-entityid)
{{#invoke:WD|dump_property|Q44403|P569}} (time)

{{#invoke:WD|dump_property|Q15920521|P1476}} (monolingualtext)
{{#invoke:WD|dump_property|Q359|P856}} (url)

Примеры значений переменной value (вязто с помощью https://www.wikidata.org/wiki/Special:EntityData/Q15920521.json):
[{"id":"Q15920521$f7c04336-4b68-8307-c98c-569412466eaa",
  "mainsnak":{"snaktype":"value",
              "property":"P1476",
              "datatype":"monolingualtext",
              "datavalue":{"value":{"text":"Ночные мысли","language":"ru"},"type":"monolingualtext"}},
  "type":"statement",
  "rank":"normal"}]

[{"id":"Q359$36814403-433c-591b-eaff-22a38472a46b",
  "mainsnak":{"snaktype":"value",
              "property":"P856",
              "datatype":"url",
              "datavalue":{"value":"https://wikileaks.org/","type":"string"}},
              "type":"statement",
              "rank":"normal"}]
]]
function wd.dump_statements(frame)
  local s_id = tostring(frame.args[1])
  local s_property_id = tostring(frame.args[2])
  local entity = mw.wikibase.getEntityObject(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, "")
--[[
  local s_result = ""
  for key, value in pairs(claim) do
    local snak = value.mainsnak
    s_result = s_result .. "[" .. key .. "]: id = " .. value.id .. ", type = " .. value.type .. ", rank = " .. value.rank .. ", snaktype = " .. snak.snaktype .. ", property = " .. snak.property
    if snak.snaktype == "value" then
      s_result = s_result .. ", datatype = " .. snak.datavalue.type
      if snak.datavalue.type == "wikibase-entityid" then
          s_result = s_result .. ", value = Q".. tostring(snak.datavalue.value["numeric-id"])
      elseif snak.datavalue.type == "string" then
          s_result = s_result .. ", value = '" .. snak.datavalue.value .."'"
      elseif snak.datavalue.type == "monolingualtext" then
          s_result = s_result .. ", language = '" .. snak.datavalue.value.language .. "', value = '" .. snak.datavalue.value.text .."'"
      elseif snak.datavalue.type == "url" then
          s_result = s_result .. ", value = '" .. snak.datavalue.value .."'"
      elseif snak.datavalue.type == "time" then
          s_result = s_result .. ", value = " .. snak.datavalue.value.time .. " (precision = " .. tostring(snak.datavalue.value.precision) .. ", before = " .. tostring(snak.datavalue.value.before) .. ",  after = " .. tostring(snak.datavalue.value.after) .. ", timezone = " .. tostring(snak.datavalue.value.timezone) .. ", calendarmodel = " .. tostring(snak.datavalue.value.calendarmodel) .. ")"
      end
    end
    s_result = s_result .. "<br/>"
  end
  return s_result
]]
end

-- Функция возвращает массив значений свойства propertyId из элемента entity
-- Возвращаются значения предпочтительного ранга "preferred", если они есть, иначе ранга "normal".
function wd.getClaimValues_ranked( entity, propertyId )
    local result = {}
    local claim = nil
    if entity ~= nil and entity.claims ~= nil then
		claim = entity.claims[ propertyId ]
    end
    if claim ~= nil then
		local b_prefered_mode = false
		for key, value in pairs(claim) do
			local snak = nil
	        if value.rank == wd.RANK_PREFERRED then
				if not b_prefered_mode then
	            	result = {}
	            	b_prefered_mode = true
				end
				snak = value.mainsnak
	        elseif value.rank == wd.RANK_NORNAL and not b_prefered_mode then
				snak = value.mainsnak
	        end
	        if snak ~= nil and snak.snaktype == wd.ST_VALUE then
				if snak.datavalue.type == wd.VT_ENTITY_ID then
	            	result[#result+1] = snak.datavalue.value["id"]
				else
	            	result[#result+1] = snak.datavalue.value
				end
	        end
		end
    end
    return result
end

function wd.is_encyclopedic_article(entity)
	-- для энциклопедических, словарных статей и перенаправлений (P31 in [Q10389811, Q13433827, Q1580166, Q1302249]) просматриваем P921 ("основная тема произведения")
	return wd.has_valid_item_value (entity, "P31", 'Q10389811')  -- энциклопедическая запись
			or wd.has_valid_item_value (entity, "P31", 'Q13433827')  -- энциклопедическая статья
			or wd.has_valid_item_value (entity, "P31", 'Q1580166')  -- словарная статья
			or wd.has_valid_item_value (entity, "P31", 'Q30279428') -- словарная статья в Викитеке
			or wd.has_valid_item_value (entity, "P31", 'Q1302249')  -- перекрёстная ссылка, вроде https://de.wikisource.org/wiki/RE:Andicavi
end

-- mw.wikibase.getEntity( id ) - дорогая функция. Викидвижок кэширует до 14 запросов. https://doc.wikimedia.org/Wikibase/master/php/md_docs_topics_lua.html#autotoc_md231
wd.entities_cache = {} -- кэш запрошенных элементов Викиданных
function wd.get_entity_by_id(entity_id, use_cache)
	if not entity_id then return end
	local e
	if use_cache then 
		e = wd.entities_cache[entity_id]
		if e == nil then
			e = mw.wikibase.getEntity(entity_id)
			table.insert(wd.entities_cache, { [entity_id] = e })
		end
	else
		e = mw.wikibase.getEntity(entity_id)
	end
	return e
end

return wd