Module:Wikidata art
Jump to navigation
Jump to search
Lua
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules
- This module is intended to provide localized text for different infobox fields of {{Artwork}} template. Used by Module:Artwork.
Code
--[[
__ __ _ _ __ ___ _ _ _ _ _
| \/ | ___ __| |_ _| | ___ \ \ / (_) | _(_) __| | __ _| |_ __ _ __ _ _ __| |_
| |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | / _` | '__| __|
| | | | (_) | (_| | |_| | | __/_ \ V V / | | <| | (_| | (_| | || (_| | | (_| | | | |_
|_| |_|\___/ \__,_|\__,_|_|\___(_) \_/\_/ |_|_|\_\_|\__,_|\__,_|\__\__,_| \__,_|_| \__|
This module is intended to provide localized text for different infobox fields.
At the moment we have:
|====================|===========================|=====================|
| Infobox Field | Property | Template |
|====================|===========================|=====================|
| object history | commissioned by (P88) | {{ProvenanceEvent}} |
| | owned by (P127) | |
| | significant event (P793) | |
| exhibition history | exhibition history (P608) | none |
| inscriptions | inscription (P1684) | {{inscription}} |
| medium | material used (P186) | {{Technique}} |
| work location | work location (P937) | none |
| creator | creator(P170), author(P50)| |
| | architect (P84) | {{Creator}} |
| institution | inventory number (P217) | {{Institution}} |
| | collection (P195) | |
| | location (P276) | |
| accession number | inventory number (P217) | none |
|====================|===========================|=====================|
Please do not modify this code without applying the changes first at
"Module:Artwork/sandbox" and testing at "Module:Artwork/testcases".
Authors and maintainers:
* User:Jarekt - original version
]]
local getLabel = require("Module:Wikidata label")._getLabel -- used for creation of name based on wikidata
local getDate = require("Module:Wikidata date")._date -- used for processing of date properties
local qualifierDate = require("Module:Wikidata date")._qualifierDate -- used for processing of date qualifiers
local creator = require("Module:Creator")._creator -- render creator templates
local institution = require("Module:Institution")._institution -- render institution templates
local material_LUT = require('Module:Technique/WikidataLUT')
local id_prop_LUT = require('Module:Artwork/Artwork ID properties LUT')
local TagQS = require('Module:TagQS')
local core = require('Module:Core')
-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function length(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
-------------------------------------------------------------------------------
local function getProperty(entity, prop)
return (core.parseStatements(entity:getBestStatements( prop ), nil) or {nil})[1]
end
-------------------------------------------------------------------------------
local function getBestProperties(entity, prop)
return core.parseStatements(entity:getBestStatements( prop ), nil)
end
-------------------------------------------------------------------------------
local function getAllProperties(entity, prop)
return core.parseStatements(entity:getAllStatements( prop ), nil)
end
-------------------------------------------------------------------------------
local function getItemProperty(item, prop)
return (core.parseStatements(mw.wikibase.getBestStatements( item, prop ), nil) or {nil})[1]
end
-------------------------------------------------------------------------------
local function getAllItemProperties(item, prop)
return core.parseStatements(mw.wikibase.getAllStatements( item, prop ), nil)
end
-------------------------------------------------------------------------------
local function getPropertyQual(entity, prop, qualifiers, lang, offset)
local Res = {}
if entity.claims and entity.claims[prop] then
for k, statement in ipairs( entity:getBestStatements( prop )) do
if (statement.mainsnak.snaktype == "value") then
local res = {} -- table with fields: key, value, P... (qualifiers)
local jdn = k + (offset or 0) -- "Julian day number" will be used as a key for sorting events; initialize
local val = statement.mainsnak.datavalue.value
if val.id then
res.value_id = val.id
val = core.getLabel(val.id, lang)
elseif val.text then
res.value_lang = val.language
val = val.text
end
res.value = val
for iQual, qual in ipairs( qualifiers ) do
if statement.qualifiers and statement.qualifiers[qual] then
local snak = statement.qualifiers[qual][1]
if (snak.snaktype == "value" and snak.datatype == 'wikibase-item') then
val = core.getLabel(snak.datavalue.value.id, lang)
res[qual ..'_id'] = snak.datavalue.value.id
elseif (snak.snaktype == "value" and snak.datatype == 'string') then
val = snak.datavalue.value
elseif (snak.snaktype == "value" and snak.datatype == 'quantity') then
val = snak.datavalue.value
elseif (snak.snaktype == "value" and snak.datatype == 'monolingualtext') then
val = snak.datavalue.value.text
res[qual.."_lang"] = snak.datavalue.value.language
elseif (snak.snaktype == "value" and snak.datatype == 'time') then
val = qualifierDate(snak, lang)
if iQual==1 then -- first qualifier in the qualifiers list will be used as a sorting value
jdn = val.jdn
end
val = val.str
else
val = nil
end
res[qual] = val
end
end
res.key = jdn
table.insert(Res, res)
end
end
end
local tableComp = function (rec1, rec2) return rec1.key<rec2.key end
table.sort(Res, tableComp)
return Res
end
-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {creator=creator, institution=institution}
-- ===========================================================================
function p.get_object_history(entity, lang)
-- Provenance look up table converting items IDs to template inputs
local ProvenanceLUT = {
Q760089 = "commission", --commission
Q753297 = "discovery", --discovery
Q1369832 = "purchase", --purchasing
Q22340494 = "acquisition", --acquisition (gain possession of an object by a museum)
Q1362753 = "acquisition", --acquisition (gain possession of a literary work)
Q420708 = "acquisition", --Acquisition (Wikimedia disambiguation page)
Q177923 = "auction", --auction
Q194189 = "sale", --sales (exchange of goods for money over a targeted time period)
Q200303 = "inheritance", --inheritance
Q707482 = "gift", --gift (voluntary transfer of property)
Q184303 = "gift", --gift (object given without the expectation of payment)
Q1124860 = "gift", --donation
Q959782 = "excavation", --archaeological excavation
Q211557 = "bequest", --bequest
Q601401 = "exchange", --trade
Q105334701 = "loan", --loan
Q5260774 = "deposit", --deposit
Q178564 = "nationalization", --nationalization
Q186302 = "seizure", --distraint (seizure of property to obtain payments)
Q86757 = "seizure", --expropriation (seizure of private property by government without full compensation)
Q14903979 = "conveyance", --change of ownership
Q315364 = "transfer", --transfer (transfer of ownership)
Q1756454 = "theft", --art theft
Q2727213 = "theft", --theft
Q53706 = "theft", --robbery
Q328376 = "theft", --Nazi plunder
Q192623 = "theft", --looting
Q19880899 = "theft", --Isabella Stewart Gardner Museum theft
Q851304 = "theft", --Looted art
Q1156800 = "restitution", --restitution
Q3196 = "burnt", --fire
Q17781833 = "destruction", --destruction (damage to an object, system or an idea)
Q21745157 = "destruction", --destroyed artwork
Q3030513 = "missing", --disappearance
Q1417098 = "inauguration",--inauguration
Q6498684 = "in collection", --ownership
Q555829 = "acceptance in lieu", -- Acceptance in lieu
Q217102 = "restored", --conservation
Q6160 = "damaged", --vandalism
Q106379705 = "damaged", --damaged (state of conservation for museum or archival objects)
Q25339601 = "deaccession", --deaccessioning (process by which an object is permanently removed from a museum’s collection)
}
--{{ProvenanceEvent|time=1950-03-01|type=discovery|newowner=Elias Cohen|place=The Hague}}
local frame = mw.getCurrentFrame()
local EventList = {}
-- discovery statements
local discoveror = getProperty(entity, 'P61') -- discoverer or inventor (P61)
local discoveryPlace = getProperty(entity, 'P189') -- location of discovery (P189)
local d = getDate(entity, 'P575' , lang) -- discovery date
local discoveryTime = d.str
local event = {}
if discoveror or discoveryPlace or discoveryTime then
event.str = frame:expandTemplate{ title = 'ProvenanceEvent', args = { type='discovery', time=discoveryTime, discoveror=discoveror, place=discoveryPlace, inline=1, lang=lang } }
event.key = 0; -- keys are usually dates passed from getPropertyQual but place this one at the front of the queue
table.insert(EventList, event)
end
-- from commissioned by (P88) / point in time (P585) (time property)
local eIcon = core.editAtWikidata(entity.id, 'P88', lang)
local provEvents = getPropertyQual(entity, 'P88', {'P585'}, lang) -- 0 is where the numbering of undated events will start
for _, event in ipairs( provEvents) do
if event.P585 then
event.str = frame:expandTemplate{ title = 'ProvenanceEvent', args = { type='commissioned', time=event.P585, newowner=event.value, inline=1, lang=lang } } .. eIcon
table.insert(EventList, event)
end
end
-- from owned by (P127) / P580 (time property)
eIcon = core.editAtWikidata(entity.id, 'P127', lang)
provEvents = getPropertyQual(entity, 'P127', {'P580'}, lang, 100) -- 100 is where the numbering of undated events will start
for _, event in ipairs( provEvents) do
if event.P580 then
event.str = frame:expandTemplate{ title = 'ProvenanceEvent', args = { type='in collection', time=event.P580, newowner=event.value, inline=1, lang=lang } } .. eIcon
table.insert(EventList, event)
end
end
-- from significant event P793 property with point in time (P585) qualifier
eIcon = core.editAtWikidata(entity.id, 'P793', lang)
provEvents = getPropertyQual(entity, 'P793', {'P585', 'P276', 'P547', 'P664', 'P2284', 'P4775', 'P11811', 'P11812'}, lang, 200) -- 200 is where the numbering of undated events will start
for _, event in ipairs( provEvents) do
local eventType = ProvenanceLUT[event.value_id] -- look up event type based on stored item ID
if event.P585 and eventType then
if event.P2284 then
local number = mw.ustring.gsub(event.P2284.amount, '+', '')
local unitQid = mw.ustring.sub(event.P2284.unit, 32)
local unit = core.getLabel(unitQid, lang)
event.P2284 = number .. ' ' .. unit
end
event.str = frame:expandTemplate{ title = 'ProvenanceEvent', args = {
type=eventType,
time=event.P585,
oldowner=event.P11811,
newowner=event.P11812,
at=event.P664,
place=event.P276,
memory=event.P547,
price=event.P2284,
lotno=event.P4775,
inline=1, lang=lang } } .. eIcon
table.insert(EventList, event)
elseif event.P585 then
event.str = event.P585 .. ": unknown event: "..event.value .. eIcon.."<br/>"
table.insert(EventList, event)
end
end
if #EventList>0 then -- if any events
local tableComp = function (rec1, rec2) return rec1.key<rec2.key end
table.sort(EventList, tableComp) -- sort them by the date using sort key
local X, event = {}, {}
for _, event in ipairs(EventList) do -- collect just text of the template
table.insert(X, event.str)
end
return '* ' .. table.concat(X,"\n* ")
end
return nil
end
-- ===========================================================================
function p.get_exhibition_history(entity, lang)
local comma = mw.message.new( "comma-separator"):inLanguage(lang):plain()
-- exhibition history (P608) (item property) / 'P580', 'P582' (time properties)
local prop = getPropertyQual(entity, 'P608', {'P580', 'P582', 'P585', 'P276'}, lang)
local rows, cells = {}, {}
for i, p in ipairs(prop) do
local places, dates_flag, row, cell
if p.P580 or p.P585 then
-- likely the item is for a GLAM institution or a city
cells = { p.value }
if p.P276 then
table.insert(cells, p.P276 )
end
if p.P580 then
table.insert(cells, mw.ustring.format("%s - %s", p.P580, p.P582 or '') )
else
table.insert(cells, p.P585 )
end
row = table.concat(cells, comma)
if i==1 then
row = row .. core.editAtWikidata(p.value_id, 'P608', lang)
end
else -- likely the item is for the exhibit where dates and location will be a property of the exhibit
cells = { "<i>" .. p.value .. "</i>" }
local exh_entity = mw.wikibase.getEntity(p.value_id)
places = getPropertyQual(exh_entity, 'P276', {'P580', 'P582'}, lang)
for _, pl in ipairs(places) do
dates_flag = pl.P580 or pl.P585
if pl.P580 then
cell = mw.ustring.format("%s (%s - %s)", pl.value, pl.P580, pl.P582 or '')
elseif pl.P585 then
cell = mw.ustring.format("%s (%s)", pl.value, pl.P585)
else
cell = pl.value
end
end
table.insert(cells, cell)
local start = getDate(exh_entity, 'P580', lang).str
local stop = getDate(exh_entity, 'P582', lang).str
if start and not dates_flag then
table.insert(cells, mw.ustring.format("%s - %s", start, stop or '') )
end
row = table.concat(cells, comma) .. core.editAtWikidata(p.value_id, '', lang)
end
table.insert(rows, row)
end
if length(rows)>0 then
return "\n*" .. table.concat(rows,"\n*")
end
return nil
end
-- ===========================================================================
function p.get_medium(entity, lang)
local prop = {}
local prop_ids = {}
for _,p in ipairs({'P186', 'P2079'}) do
-- material used (P186), fabrication method (P2079)
local statements = getPropertyQual(entity, p, {'P462', 'P518'}, lang)
-- color (P462), applies to part (P518)
for _,v in ipairs(statements) do
table.insert(prop, v)
prop_ids[p] = true
end
end
if not prop or length(prop)==0 then
return nil -- if no P186 or P2079 statements than exit
end
local caseGroups = {default = {}, over = {}, on = {}, mounted = {}}
for _, p in ipairs(prop) do
local lc_value_id = string.lower(p.value_id)
local supports = {['Q33123524'] = true, ['Q861259'] = true}
if supports[p.P518_id] ~= nil then -- applies to part: a support
caseGroups.on[#caseGroups.on+1] = {noun = lc_value_id, adj = {p.P462_id}}
elseif p.P518_id=='Q107105674' then -- applies to part: mount
caseGroups.mounted[#caseGroups.mounted+1] = {noun = lc_value_id, adj = {p.P462_id}}
else
caseGroups.default[#caseGroups.default+1] = {noun = lc_value_id, adj = {p.P462_id}}
end
end
local technique = require("Module:Technique")._technique
local medium = technique({lang = lang, system = 'templates', compat = 'yes', caseGroups = caseGroups})
if medium then
for id,_ in pairs(prop_ids) do
medium = medium .. core.editAtWikidata(entity.id, id, lang)
end
end
return medium
end
-- ===========================================================================
function p.get_inscription(entity, lang)
--[[
Wikidata
inscription (P1684) - Monolingual text
applies to part (P518) - item property
instance of (P31) - item property
Commons template:
{{inscription |1= |full form= |type= |side= |position= |description= |comment= |ID= |language= |translation= |en= |de= |medium= }}
]]
local LUT = {
-- positions stored in "applies to part (P518)" qualifier
Q15332388 = "bottom",
Q17525439 = "bottom",
Q11812678 = "bottom",
Q15332375 = "top",
Q17525438 = "top",
Q23595 = "center",
Q13196750 = "left",
Q17525441 = "left",
Q14565199 = "right",
Q17525442 = "right",
Q27956549 = "top left",
Q27956533 = "top right",
Q27956553 = "bottom left",
Q27956561 = "bottom right",
Q82383 = "on the base",
Q860792 = "on the frame",
-- if 2 positions are provided they can be combined into a single entry
-- supported by the template
bottom_left = "bottom left",
bottom_right = "bottom right",
bottom_center = "bottom center",
center_left = "center left",
center_right = "center right",
left_top = "top left",
right_top = "top right",
center_top = "top center",
-- sides in "applies to part (P518)" qualifier
Q9305022 = "recto",
Q257418 = "recto", -- obverse
Q9368452 = "verso",
Q1542661 = "verso", -- reverse
Q32198402 = "verso", -- reverse
Q16938807 = "verso", -- reverse
-- inscription type stored in "object has role (P3831)" qualifier
Q188675 = "signature", -- signature
Q1373131 = "signature", -- signature
Q205892 = "date", -- calendar date
Q1898184 = "dedication", -- dedication
Q168346 = "monogram", -- monogram
Q2221906 = "place", -- geographic location
Q783521 = "title", -- title
Q206287 = "quotation", -- quotation
Q644099 = "stamp", -- rubber stamp
Q162919 = "seal", -- seal
Q1417099 = "accession number", -- accession number
Q319608 = "artist's address", -- postal address
Q14659 = "coat of arms", -- coat of arms
Q42470 = "motto", -- motto
Q1772 = "epitaph", -- epitaph
Q43065 = "watermark", -- watermark
Q827198 = "speech balloon", -- speech balloon
Q98877418 = "reign mark", -- reign mark on Chinese ceramics
Q18585177 = "caption", -- caption
Q2374398 = "cartouche", -- cartouche
Q112110 = "emblem", -- emblem
Q3509975 = "in memoriam", -- in memoriam
Q1272809 = "kalos inscription",-- kalos inscription
Q15873403 = "publisher's mark", -- publisher's mark
Q105844397 = "initials",
-- if 2 inscription types are provided they can be combined into a
-- single entry supported by the template
date_signature = "signature and date",
date_monogram = "monogram and date"
}
local max_insc = 20
local frame = mw.getCurrentFrame()
local AllInsc = {}
local addIconFlag = true
for iInsc, statement in ipairs( entity:getBestStatements( 'P1684' )) do -- "inscription (P1684)"
if (statement.mainsnak.snaktype == "value") then
local val = statement.mainsnak.datavalue.value
local temp_args, position, iType = {}, {}, {}
temp_args['1'] = val.text -- text
temp_args.language = val.language -- language of the text
temp_args.nocat = '1' -- no inscription categories (They are very slow)
if statement.qualifiers then
if statement.qualifiers.P3831 then -- object has role (P3831)
for _, snak in ipairs( statement.qualifiers.P3831) do
table.insert(iType, LUT[snak.datavalue.value.id])
end
if length(iType)==2 then -- two iType values can be combined into a single value used by the template
table.sort(iType) -- order them alphabetically
local val = LUT[table.concat(iType, '_')]
if val ~= nil then
iType = { [1]=val } -- example: signature and date
end
end
temp_args.type = table.concat(iType, '/')
end
if statement.qualifiers.P518 then -- applies to part (P518) used for locattion
for _, snak in ipairs( statement.qualifiers.P518) do
local part = LUT[snak.datavalue.value.id]
if (part=="recto" or part=="verso") then
temp_args.side = part
else
table.insert(position, part)
end
end
end
if statement.qualifiers.P2441 then -- literal translation (P2441)
for _, snak in ipairs(statement.qualifiers.P2441) do
if (snak.snaktype == "value") then
local val = snak.datavalue.value
temp_args[val.language] = val.text
end
end
end
if statement.qualifiers.P7008 then -- unabbreviated text (P7008)
local snak = statement.qualifiers.P7008[1]
if (snak.snaktype == "value") then
local val = snak.datavalue.value
temp_args['full form'] = val.text
end
end
if length(position)==1 then
temp_args.position = position[1]
elseif length(position)==2 then -- two position values can be combined into a single value used by the template
table.sort(position) -- order them alphabetically
temp_args.position = LUT[table.concat(position, '_')]
end
end
if addIconFlag and iInsc==1 then
temp_args['1'] = temp_args['1'].. core.editAtWikidata(entity.id, 'P1684', lang)
addIconFlag = false
end
if iInsc<max_insc then
val = frame:expandTemplate{ title = 'inscription', args=temp_args }
table.insert(AllInsc, val)
end
end
end
if length(AllInsc)>0 then
return "<ul><li>" .. table.concat(AllInsc,"</li><li>") .. "</li></ul>"
end
return nil
end
-- ===========================================================================
function p.get_work_location(entity, lang)
-- work_location (P937) / 'P580', 'P582' (time properties)
local prop = getPropertyQual(entity, 'P937', {'P580', 'P582', 'P585'}, lang)
local X={}
for _, p in ipairs(prop) do
local str = p.value
if p.P580 or p.P582 then
str = mw.ustring.format("%s (%s - %s)", p.value, p.P580 or '', p.P582 or '')
elseif p.P585 then
str = mw.ustring.format("%s (%s)", p.value, p.P585)
else
str = p.value
end
table.insert(X, str)
end
if length(X)>0 then
return table.concat(X,"; ") .. core.editAtWikidata(entity.id, 'P937', lang)
end
return nil
end
-- ===========================================================================
function p.get_depicted(entity, lang)
local maxDepict = 50 -- maximum number of Depict statements to check, to prevent running out of memory for items with 100's of them
local X, Y, Done = {{},{}}, {}, {}
local ID_LUT = {
Q5 = 1, -- human
Q21070568 = 1, -- human who may be fictional
Q95074 = 1, -- fictional character
Q4271324 = 1, -- mythical character
Q18563360 = 1, -- Quranic character
Q20643955 = 1, -- human biblical figure
Q22813672 = 1, -- two biblical humans
Q22813674 = 1, -- group of biblical humans
Q235113 = 1, -- angel
Q178342 = 1, -- archangel
Q581450 = 1, -- fallen angel
Q178885 = 1, -- deity
Q979507 = 1, -- Hindu deity
Q22989102 = 1, -- Greek deity
Q11688446 = 1, -- Roman deity
Q16513881 = 1, -- Norse deity
Q22704077 = 2, -- biblical episode
Q1406161 = 2, -- artistic theme
Q46999986 = 2 -- martyrdom
}
local props = {P921='main subject', P180='depicts'}
for prop, _ in pairs(props) do
local iCounter = {0, 0} -- two counters
local items = getBestProperties(entity, prop)
for iDepict, pid in ipairs(items or {}) do
if iDepict<maxDepict then
local P31s = getAllItemProperties(pid, 'P31')
for _, p31 in ipairs(P31s or {}) do
local dType = ID_LUT[p31] -- type of depicts: 1 or 2
if dType and (not Done[pid]) then -- instance of "human", etc. was found
local text = core.getLabel(pid, lang)
iCounter[dType] = iCounter[dType] + 1 -- how many of that type
if iCounter[dType]==1 then
text = text .. core.editAtWikidata(entity.id, prop, lang)
end
table.insert(X[dType], text)
Done[pid] = true
break
end
end
end
end
end
for i = 1,2 do
local n = #X[i]
if n==0 then
X[i] = nil
elseif n==1 then
X[i] = X[i][1]
else
X[i] = '* ' .. table.concat(X[i],"\n* ")
end
end
return X
end
-- ===========================================================================
function p.get_depicted_people(entity, lang)
return p.get_depicted(entity, lang)[1]
end
-- ===========================================================================
function p.get_accession_number (entity, lang)
local Res = {} -- initialize final output
-- harvest data from inventory number (P217) property with qualifiers: collection (P195) and end time (P582)
local Y = {} -- Y is a structure where we have a table of IDs for each collection
local prop = getPropertyQual(entity, 'P217', {'P195', 'P582'}, lang)
for k, p in ipairs(prop) do -- loop over all IDs found
if not p.P582 then -- skip if there is an "end date"
local key = p.P195_id or k
if not Y[key] then Y[key]={} end -- initialize if it does not exist
table.insert(Y[key], p.value) -- group IDs by collection
Res.id = p.value -- return one of the pure ID strings, to be used as category sortkey
end
end
--assemble the wikitext of the accession_number field
local X = {} -- table with wikitext strings for each "collection"
for key, id in pairs(Y) do -- loop over institutions
local id=mw.text.listToText(id) -- convert all the IDs into a single string (in most cases there will be only one)
if type(key)=='string' then -- if "collection" qualifier is used than add it to the ID
table.insert(X, mw.ustring.format( "%s <small>(%s)</small>", id , core.getLabel(key, lang) ) )
else
table.insert(X, id) -- if no "collection" is mentioned than just return ID
end
end
-- assemble final output structure
if length(X)==1 then -- single ID case
Res.str = X[1] .. core.editAtWikidata(entity.id, 'P217', lang) -- just return the string
elseif length(X)>1 then -- if more than one then return bulleted list
X[1] = X[1] .. core.editAtWikidata(entity.id, 'P217', lang)
Res.str = "* " .. table.concat(X, "\n* ")
end
return Res
end
-- ===========================================================================
local function renderInstitution(entity, lang)
-- local function to create wikitext for a single institution template or {{Private collection}} template
-- once we have entity check if Institution template exist and call it or assemble one based on Wikidata
local frame = mw.getCurrentFrame()
local inst
-- first check for few special cases which will result in {{Private collection}} template
if entity.id == 'Q768717' then -- render {{Private collection}} template
return frame:expandTemplate{ title ='Private collection'} .. '<br/>'
end
local P31 = getBestProperties(entity, 'P31') -- look up "instance of" property for "Institution" entity
for _, p in ipairs(P31 or {}) do
if p=='Q5' then -- if "Institution" entity is a person than render {{Private collection}} template
return frame:expandTemplate{ title ='Private collection', args={ owner = getLabel(entity, lang)}} .. '<br/>'
elseif p=='Q768717' then -- if "Institution" is an instance of "Private collection" than render {{Private collection}} template
return frame:expandTemplate{ title ='Private collection'} .. '<br/>'
end
end
-- render Institution template
local P1612 = getProperty(entity, 'P1612') -- look up "Commons Institution page" property
-- make sure second argument is string, never nil (it returns nil for the empty string, but throws error for nil)
local templateName = mw.title.makeTitle( 'Institution', P1612 or '' )
if templateName and templateName.exists then
return frame:expandTemplate{ title ='Institution:' .. P1612, args={'collapse'} } -- use existing template
else
local inst,_ = institution({wikidata=entity.id, lang=lang, collapse=1}) -- create institution based on item id
return inst
end
end
-- ===========================================================================
local function isPrivateCollection(entity)
-- test if collection is a private_collection
-- see https://www.wikidata.org/wiki/Wikidata:WikiProject_sum_of_all_paintings/Private_collection
local private_collection = 'Q768717'
if entity.claims and entity.claims.P195 then
for _, statement in ipairs( entity:getBestStatements( 'P195' )) do
if (statement.mainsnak.snaktype == "somevalue") then
local quals = statement.qualifiers
if quals and not quals.P582 and quals.P3831 then -- object has role (P3831) and no end time (P582)
for _, qual in ipairs( quals.P3831 ) do
local role0 = qual.datavalue.value.id -- specify the role of "collection"
if role0 == private_collection then
return true
end
end
end
end
end
end
return false
end
-- ===========================================================================
function p.get_institution(entity, lang)
local collection, location = {}, {} -- relevant data is stored in collection (P195) and location (P276) properties
-- harvest data from inventory number (P217) property with qualifiers: collection (P195), and end time (P582)
local prop = getPropertyQual(entity, 'P217', {'P580', 'P582', 'P195'}, lang) -- P580 if present is used for sorting
for _, p in ipairs(prop) do
if not p.P582 and p.P195_id then -- skip if there is an "end date"
collection[p.P195_id] = 1 -- store collection item ID
end
end
-- harvest data from collection (P195) / start time (P580) + end time (P582)
local prop = getPropertyQual(entity, 'P195', {'P580', 'P582'}, lang) -- P580 if present is used for sorting
for _, p in ipairs(prop) do
if p.P582 then -- skip if there is an "end date"
collection[p.value_id] = nil -- and delete from Collection list
else
collection[p.value_id] = 1 -- otherwise collection item ID to the list
end
end
if isPrivateCollection(entity) then
collection.Q768717 = 1
end
-- harvest data from location (P276) / start time (P580) + end time (P582)
local prop = getPropertyQual(entity, 'P276', {'P580', 'P582'}, lang)
for _, p in ipairs(prop) do
if not p.P582 and not collection[p.value_id] then -- skip if there is an "end date" or the value is in collection table
location[p.value_id] = 1 -- store location item ID
end
end
-- initialize output structure
local Res = {}
Res.institution = nil
Res.location = nil
Res.id = nil
-- first try usual cases of single collection item
if length(collection)==1 then -- only a single collection item
local cId, _ = next(collection, nil) -- collection item ID
local cEntity = mw.wikibase.getEntity(cId) -- collection entity
local cParent = getProperty(cEntity, 'P361') -- collection parent object of which collection item is part of (P361)
if cParent == 'Q19675' or cParent == 'Q1075988' then -- special case where collection is part of Louvre Museum
local frame = mw.getCurrentFrame()
Res.institution = frame:expandTemplate{ title ='Institution:Louvre', args={'collapse'} } -- render existing {{Institution:Louvre}} template
Res.id = 'Q1075988'
Res.location = getLabel(cEntity, lang) -- use collection and location tables to populate location/department field
if length(location)>0 then
local lId, _ = next(location, nil) -- Location item ID
Res.location = Res.location .. '<br/>\n' .. core.getLabel(lId, lang)
end
return Res
end
if cId=='Q812285' and length(location)>0 then -- if collection is Bavarian State Painting Collections (Q812285)
collection = location -- use location instead collection
else
Res.institution = renderInstitution(cEntity, lang) -- use collection entity to render Institution template
Res.id = cEntity.id
if length(location)>0 then -- single collection and at least one location
local lId, _ = next(location, nil) -- location item ID
local lParent = getItemProperty(lId, 'P361') -- location parent object of which location item is part of (P361)
if lParent == cId then -- location is part of the collection listed above
Res.location = getLabel(cEntity, lang) -- use location entity as location/department field
end -- if collection and locations are not related so ignore location(s)
end
return Res
end
end
-- If the case is not usual try generic approach
if length(collection)==0 and length(location)>0 then -- no collections but we have some locations
collection = location -- use location instead collection
end
if length(collection)>0 then -- collections or locations only or locations same as collections
local X = {} -- table with wikitext of all the institution templates
for cId, _ in pairs(collection) do -- render all collections
local inst = renderInstitution(mw.wikibase.getEntity(cId), lang)
table.insert(X, inst )
end
Res.institution = table.concat(X, '\n')
end
return Res
end
-- ===========================================================================
function p.get_creator(entity, prop, lang)
-- harvest the data
local IDs = {}
local qualifiers = {P1774='workshop of', P1775='follower of', P1776='circle of',
P1777='manner of', P1779='possibly', P1780='school of', P1877='after'};
local LUT = {Q18122778='presumably', Q30230067='possibly', Q56644435='probably',
Q50137645='attributed to', Q230768='attributed to'}
local anonymous = 'Q4233718'
if entity.claims and entity.claims[prop] then
for _, statement in ipairs( entity:getBestStatements( prop )) do
local option, itemID1, itemID2, role, quals
if (statement.mainsnak.snaktype == "value") then
itemID1 = statement.mainsnak.datavalue.value.id
else -- if (statement.mainsnak.snaktype == "somevalue")
itemID1 = nil
end
-- look for role of "creator" like: bookbinding, lithography, etc.
quals = statement.qualifiers
if quals and quals.P518 then -- applies to part (P518)
role = quals.P518[1].datavalue.value.id -- specify the role of "creator"
end
if quals and quals.P3831 then -- object has role (P3831)
for _, qual in ipairs( quals.P3831 ) do
local role0 = qual.datavalue.value.id -- specify the role of "creator"
if itemID1 == nil and role0 == anonymous then
itemID1 = anonymous -- user is anonymous
else
role = role0
end
end
end
-- look for "options" related to certainty ('presumably', 'possibly', 'probably', etc.)
if quals and (quals.P5102 or quals.P1480) then -- sourcing circumstances (P1480) and nature of statement (P5102)
local q = quals.P5102 or quals.P1480 -- values used by P1480 were moved to P5102
option = LUT[q[1].datavalue.value.id] -- add certainty qualifiers
end
-- look for qualifiers thet provide new creator ID ('school of', 'after', etc.)
for qual, opt in pairs( qualifiers ) do
if quals and quals[qual] then
itemID2 = quals[qual][1].datavalue.value.id
-- add creator with role, but without role if the mainsnak
-- value is used and the qualifier is P1877='after'
local qualRole = role
if not (itemID1==anonymous or itemID1==nil) and (qual == 'P1877') then
qualRole = nil
end
table.insert(IDs, {itemID=itemID2, option=opt, role=qualRole});
break
end
end
-- add new creator, except for the case when they are anonymous and we have secondary ID
if not ((itemID1==anonymous or itemID1==nil) and itemID2) then
table.insert(IDs, {itemID=itemID1, option=option, role=role});
end
end
end
--sort the table
local tableComp = function (rec1, rec2) return (rec1.itemID or 'ZZZ')<(rec2.itemID or 'ZZZ') end
table.sort(IDs, tableComp)
-- IDs table cleanup
-- "workshop of", "circle of", "school of", "studio of", "or follower", "or workshop", "and workshop", "attributed to", "after", "formerly attributed to", "follower of", "manner of", "namepiece", "possibly", "probably".
for k = 2, #IDs do
if IDs[k-1].itemID==IDs[k].itemID then
local val = (IDs[k-1].option or '') .. (IDs[k].option or '')
if val=='workshop of' then
IDs[k ].option = "and workshop"
IDs[k-1].option = "delete"
elseif val=="follower of" then
IDs[k ].option = "or follower"
IDs[k-1].option = "delete"
end
end
end
-- render the output template(s)
local Creators = {}
local frame = mw.getCurrentFrame()
for k =1, #IDs do
local val, _
local itemID = IDs[k].itemID
local option = IDs[k].option
local role = IDs[k].role
if itemID == nil then -- render {{Unknown|author}} template
val = frame:expandTemplate{ title ='Unknown', args={'author'}}
table.insert(Creators, val)
elseif itemID == anonymous then -- render anonymous label
val = core.getLabel(itemID, lang)
table.insert(Creators, val)
elseif option ~= "delete" then
local P1472 = getItemProperty(itemID, 'P1472') -- look up "Commons Creator page" property
local templateName = mw.title.makeTitle('Creator',P1472 or '' )
if P1472 and templateName.exists then
if option then option=option..'/collapse' else option='collapse' end
val = frame:expandTemplate{ title ='Creator:' .. P1472, args = {option} } -- use existing template
else
val, _ = creator({wikidata=itemID, lang=lang, option=option, collapse=1})-- create creator based on item id
end
if role then
local tag = TagQS.createTag('creator_role', nil, role)
val = "'''" .. core.getLabel(role, lang) .. "''': " .. tag .. val
end
table.insert(Creators, val)
end
end -- for
-- gather the output structure
local Res = {}
Res.str = nil
Res.id = nil -- if only one creator and no "option" modifier than return ID
Res.IDs = IDs -- raw data used to render the template(s)
if #Creators>0 then
Res.str = string.format('<table cellpadding=0 cellspacing=0><tr><td>%s</td><td valign="top">%s</td></tr></table>',
table.concat(Creators, '\n'),
core.editAtWikidata(entity.id, prop, lang))
end
if #IDs==1 and not IDs[1].option then
Res.id = IDs[1].itemID
end
return Res
end
-- ===========================================================================
function p.get_references(entity, lang)
local Res -- initialize final output
local wordsep = mw.message.new( "Word-separator" ):inLanguage(lang):plain()
local colon = mw.message.new( "Colon-separator" ):inLanguage(lang):plain() .. wordsep
local comma = mw.message.new( "Comma-separator" ):inLanguage(lang):plain() .. wordsep
-- harvest data from catalog code (P528) property with qualifiers: catalog (P972)
local strTable = {} -- table with wikitext strings for each "reference"
local prop = getPropertyQual(entity, 'P528', {'P972'}, lang)
local str
local addIconFlag = true
for _, p in ipairs(prop) do -- loop over all IDs found
if p.P972 then
str = "''" .. p.P972 .. "''" .. comma .. p.value
if addIconFlag then
str = str .. core.editAtWikidata(entity.id, 'P528', lang)
addIconFlag = false
end
table.insert(strTable, str) -- group IDs by collection
end
end
-- harvest data from "described at URL" (P973) property with qualifier: language (P407), title (P1476), publisher (P123) and author (P50)
local label
prop = getPropertyQual(entity, 'P973', {'P407', 'P1476', 'P123', 'P50'}, lang)
for k, p in ipairs(prop) do
local linkTitle = p.P1476 or p.value
-- display title if available rather than raw URL
linkTitle = mw.ustring.gsub(linkTitle, '%]', ']')
-- escape closing square brackets that close links in MediaWiki
str = string.format("[%s ''%s'']", p.value, linkTitle)
if p.P50 then -- add author
str = str .. ", " .. p.P50
end
if p.P123 then -- add publisher
str = str .. ", " .. p.P123
end
if p.P407 then -- add language
str = str .. " (" .. p.P407 .. ")"
end
if k==1 then
str = str .. core.editAtWikidata(entity.id, 'P973', lang)
end
table.insert(strTable, str) -- group IDs by collection
end
-- collect links to museum databases from Wikidata and display them
for prop, prop_urls in pairs(id_prop_LUT) do
for _, dbase_id in ipairs(getBestProperties(entity, prop) or {}) do
local id, url, dbase, db_url, eIcon
id = mw.uri.encode( mw.text.trim(dbase_id), 'PATH')
id = id:gsub("%%", "%%%%") -- in case of "%" in ID -> replace with "%%" so gsub works
_, url = next(prop_urls) -- use any URL if none found with langSwitch
db_url = core.langSwitch(prop_urls, lang) or url
db_url = db_url:gsub('$1', id) -- replace $1 in the URL with the ID
dbase = getLabel(prop, lang) -- name of the database
eIcon = core.editAtWikidata(entity.id, prop, lang)
str = string.format('%s%s[%s %s]%s', dbase, colon, db_url, dbase_id, eIcon)
table.insert(strTable, str)
end
end
-- harvest data from described by source (P1343) property with qualifiers: pages (P304), publication date (P577),
-- section, verse, or paragraph (P958), volume (P478), reference URL (P854), title (P1476), statement is subject of (P805)
prop = getPropertyQual(entity, 'P1343', {'P304', 'P958', 'P478', 'P854', 'P1476', 'P805', 'P577'}, lang)
label = nil
for k, p in ipairs(prop) do
local frame = mw.getCurrentFrame()
local cite_arg = {}
cite_arg.title = p.value -- described by source (P1343)
cite_arg.url = p.P854 or '' -- reference URL (P854)
cite_arg.volume = p.P478 or '' -- volume (P478)
cite_arg.pages = p.P304 or '' -- pages (P304)
-- differentiate between "pages" and "page"
if not string.find(cite_arg.pages, '%p') then
cite_arg.page = cite_arg.pages
cite_arg.pages = nil
end
cite_arg.chapter = p.P958 or '' -- section, verse, or paragraph (P958)
cite_arg.series = p.P805 or '' -- statement is subject of (P805)
cite_arg.date = p.P577 or '' -- statement is subject of (P805)
str = frame:expandTemplate{ title ='Cite_book', args = cite_arg }
if k==1 then
str = str .. core.editAtWikidata(entity.id, 'P1343', lang)
end
table.insert(strTable, str)
end
-- assemble final output structure
if #strTable==1 then -- single ID case
Res = strTable[1] -- just return the string
elseif #strTable>1 then -- if more than one than return bulleted list
Res = "* " .. table.concat(strTable, "\n* ")
end
return Res
end
-- ===========================================================================
function p.get_other_versions(entity, pagename, lang)
-- look through properties of the current item
local property = {P10='video', P18='image', P996='scan',
P3451='nighttime view', P5555='schematic', P4896='model3D',
P5252='winter view', P5775='image of interior', P7420='framed_image', P7417='image of backside',
P7457="creator's signature", P8592='aerial view', P10093='image with color chart'
}
local files = {}
for prop, field in pairs( property ) do
for __, file in ipairs( getAllProperties(entity, prop) or {} ) do
if file ~= pagename then
local label = getLabel(prop, lang, '-')
local eIcon = core.editAtWikidata(entity.id, prop, lang)
table.insert(files, file .. "|" .. label .. eIcon)
end
end
end
-- look through related items
local property = { P144='based on', P361='part of', P179='part of the series', P1889='different from' }
for prop, field in pairs( property ) do
local item = getProperty(entity, prop)
if item then
local file = getItemProperty(item, 'P18')
if file then
local label = getLabel(prop, lang, '-') .. ': ' .. getLabel(item, lang)
local eIcon = core.editAtWikidata(entity.id, prop, lang)
table.insert(files, file .. "|" .. label .. eIcon)
end
end
end
if #files<=1 then -- #files==1 means gallery with a single image (which is also displayed above)
return ''
end
files = '\n' .. table.concat(files, '\n')
local frame = mw.getCurrentFrame()
return frame:extensionTag{ name = 'gallery', content = files, args = {mode = 'packed-hover'}}
end
-- ===========================================================================
function p.debug(frame)
local field = frame.args.field
local lang = frame.args.lang
local entity = mw.wikibase.getEntity(frame.args.wikidata)
local str, X
if field=='object_history' then
return p.get_object_history(entity, lang) -- object history
elseif field=='exhibition_history' then
return p.get_exhibition_history(entity, lang) -- exhibition history
elseif field=='inscription' then
return p.get_inscription(entity, lang)
elseif field=='medium' then
return p.get_medium(entity, lang)
elseif field=='work_location' then
return p.get_work_location(entity, lang)
elseif field=='institution' then
X = p.get_institution(entity, lang)
return (X.institution or '') .. '\n' .. (X.location or '')
elseif field=='accession_number' then
local res = p.get_accession_number(entity, lang)
return res.str or ''
elseif field=='creator' then
local res = p.get_creator(entity, 'P170', lang)
return res.str or '';
elseif field=='references' then
return p.get_references(entity, lang) or ''
elseif field=='depicted_people' then
return p.get_depicted(entity, lang)[1] or ''
elseif field=='depicted_themes' then
return p.get_depicted(entity, lang)[2] or ''
elseif field=='other_versions' then
return p.get_other_versions(entity, '', lang) or ''
end
return ''
end
return p