Module:Wikidata art: Difference between revisions
From WWII Archives
wc>Jarekt (retire P1773 which was deleted) |
Paul Sidle (talk | contribs) m (1 revision imported) |
(No difference)
|
Latest revision as of 13:21, 30 May 2024
Documentation for this module may be created at Module:Wikidata art/doc
--[[ __ __ _ _ __ ___ _ _ _ _ _ | \/ | ___ __| |_ _| | ___ \ \ / (_) | _(_) __| | __ _| |_ __ _ __ _ _ __| |_ | |\/| |/ _ \ / _` | | | | |/ _ (_) \ /\ / /| | |/ / |/ _` |/ _` | __/ _` | / _` | '__| __| | | | | (_) | (_| | |_| | | __/_ \ 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.lang = lang -- language of the reader 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 "\n*" .. table.concat(AllInsc,"\n*") 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 than 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', 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