پودمان:Cite Q

از ویکی‌پدیا، دانشنامهٔ آزاد
توضیحات پودمان[ایجاد] [پاکسازی]
require('Module:No globals')
local wdib = require('Module:WikidataIB')
local getValue = wdib._getValue
local getPropOfProp = wdib._getPropOfProp
local followQid = wdib._followQid

local simple_properties = { -- PXXX, is multiple?, linked?
	publisher = {id="P123", maxvals=1},
	oclc = {id="P243", maxvals=1},
	place = {id="P291", maxvals=0,linked='no'},
	doi = {id="P356", maxvals=1},
	issue = {id="P433", maxvals=1, populate_from_journal = true},
	pmid = {id="P698", maxvals=1},
	arxiv = {id="P818", maxvals=1},
	bibcode = {id="P819", maxvals=1},
	jstor = {id="P888", maxvals=1},
	mr = {id="P889", maxvals=1},
	ssrn = {id="P893", maxvals=1},
	pmc = {id="P932", maxvals=1},
	lccn = {id="P1144", maxvals=1},
	hdl = {id="P1184", maxvals=1},
	ismn = {id="P1208", maxvals=1},
	journal = {id="P1433", maxvals=1},
	citeseerx = {id="P3784", maxvals=1},
	osti = {id="P3894", maxvals=1},
	biorxiv = {id="P3951", maxvals=1},
	isbn = {id="P212", maxvals=1, populate_from_journal = true}, -- ISBN 13
	issn = {id="P236", maxvals=1, populate_from_journal = true},
	chapter = {id="P792", maxvals=1},
	["date"] = {id="P577", maxvals=1, populate_from_journal = true},
	series = {id="P179", maxvals=1, populate_from_journal = true},
	volume = {id="P478", maxvals=1, populate_from_journal = true},
	title = {id="P1476", maxvals=1},
	url = {id="P953", maxvals=1}, -- full work available at
	pages = {id="P304", maxvals=1, populate_from_journal = true},
	translator = {id="P655", maxvals=0}, -- does **not** go to "others" section!
	illustrator = {id="P110", maxvals=0, others=true}, -- goes to "others" section
	composer = {id="P86", maxvals=0, others=true}, -- goes to "others" section
	animator = {id="P6942", maxvals=0, others=true}, -- goes to "others" section
	director = {id="P57", maxvals=0, others=true}, -- goes to "others" section
	screenwriter = {id="P58", maxvals=0, others=true}, -- goes to "others" section
}

local citeq = {}

--[[--------------------------< I S _ S E T >------------------------------------------------------------------

Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.

]]
local function is_set( var )
	return not (var == nil or var == '');
end


--[=[-------------------------< G E T _ N A M E _ L I S T >----------------------------------------------------

get_name_list -- adapted from getAuthors code taken from [[Module:RexxS]]
arguments:
	nl_type - type of name list to fetch: nl_type = 'author' for authors; 'editor' for editors
	args - pointer to the parameter arguments table from the template call
	qid - value from |qid= parameter; the Q-id of the source (book, etc.) in qid
	wdl - value from the |wdlinks= parameter; a boolean passed to enable links to Wikidata when no article exists

returns nothing; modifies the args table

]=]

local function get_name_list (nl_type, args, qid, wdl)
	local propertyID = "P50"
	local fallbackID = "P2093" -- author name string
	
	if 'author' == nl_type then
		propertyID = 'P50';														-- for authors
		fallbackID = 'P2093';
	elseif 'editor' == nl_type then
		propertyID = 'P98';														-- for editors
		fallbackID = nil;
	else
		return;																	-- not specified so return
	end
	
	-- wdlinks is a boolean passed to enable links to Wikidata when no article exists
	-- if "false" or "no" or "0" is passed set it false
	-- if nothing or an empty string is passed set it false
	if wdl and (#wdl > 0) then
		wdl = wdl:lower()
		wdl = (wdl == "false") or (wdl == "no") or (wdl == "0")
	else
		-- wdl is empty, so
		wdl = false
	end
	
	local entity = mw.wikibase.getEntity(qid)
	local props = nil
	local fallback = nil
	if entity and entity.claims then
		props = entity.claims[propertyID]
		if fallbackID then
			fallback = entity.claims[fallbackID]
		end
	end
	
	-- Make sure it actually has at least one of the properties requested
	if not (props and props[1]) and not (fallback and fallback[1]) then 
		return nil
	end
	
	-- So now we have something to return:
	-- table 'out' is going to store the names(s):
	-- and table 'link' will store any links to the name's article
	local out = {}
	local link = {}
	local maxpos = 0
	if props and props[1] then
		for k, v in pairs(props) do
			local qnumber = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
			local sitelink = mw.wikibase.sitelink(qnumber)
			local label = mw.wikibase.label(qnumber)
			if label then
				label = mw.text.nowiki(label)
			else
				label = qnumber
			end
			local position = maxpos + 1 -- Default to 'next' author.
			-- use P1545 (series ordinal) instead of default position.
			if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
				position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
			end
			maxpos = math.max(maxpos, position)
			if sitelink then
				-- just the plain name,
				-- but keep a record of the links, using the same index
				out[position] = label
				link[position] = sitelink
			else
				-- no sitelink, so check first for a redirect with that label
				-- this code works, but causes the article to appear in WhatLinksHere for the possible destination, so remove
				-- local artitle = mw.title.new(label, 0)
				-- if artitle.id > 0 then
				--	if artitle.isRedirect then
						-- no sitelink,
						-- but there's a redirect with the same title as the label;
						-- so store the link to that
				--		out[position] = label
				--		link[position] = label
				--	else
						-- no sitelink and not a redirect but an article exists with the same title as the label
						-- that's probably a dab page, so output the plain label
				--		out[position] = label
				--	end
				--else
				-- no article or redirect with the same title as the label
				if wdl then
					-- show that there's a Wikidata entry available
					out[position] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]&nbsp;<span title='" .. i18n["errors"]["local-article-not-found"] .. "'>[[File:Wikidata-logo.svg|16px|alt=|link=]]</span>"
				else
					-- no wikidata links wanted, so just give the plain label
					out[position] = label
				end
				-- end
			end
		end
	end
	if fallback and fallback[1] then
		-- Fallback to name-only authors / editors
		for k, v in pairs(fallback) do
			local label = v.mainsnak.datavalue["value"]
			local position = maxpos + 1 -- Default to 'next' author.
			-- use P1545 (series ordinal) instead of default position.
			if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then
				position = tonumber(v.qualifiers["P1545"][1].datavalue.value)
			end
			maxpos = math.max(maxpos, position)
			out[position] = label
		end
	end

	-- if there's anything to return, then insert the additions in the template arguments table
	-- in the form |author1=firstname secondname |author2= ...
	-- Renumber, in case we have inconsistent numbering
	local keys = {}
	for k,v in pairs(out) do
		keys[#keys+1] = k
	end
	table.sort(keys) -- as they might be out of order
	for i, k in ipairs(keys) do
		mw.log(i.." "..k.." "..out[k])
		args[nl_type .. i] = out[k]												-- author-n or editor-n
		if link[k] then
			args[nl_type .. '-link' .. i] = link[k]								-- author-linkn or editor-linkn
		end
	end
end


--[[-------------------------< C I T E _ Q >------------------------------------------------------------------

Takes standard cs1|2 template parameters and passes all to {{citation}}.  If neither of |author= and |author1=
are set, calls get_authors() to try to get an author name-list from wikidata.  The result is passed to 
{{citation}} for rendering.

]]

local function wrap_nowiki(str)
	return mw.text.nowiki(str or '')
end

function citeq.cite_q (frame)
	local citeq_args = {};

	for k, v in pairs(frame:getParent().args) do
		citeq_args[k] = v
	end

	for k, v in pairs(frame.args) do
		citeq_args[k] = v
	end
	
	local qid = citeq_args.qid
	local wdl = citeq_args.wdl
	citeq_args.qid = nil
	citeq_args.wdl = nil
	
	local oth = {}
	
	citeq_args.language = citeq_args.language or getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P218", ps = 1} )
	if citeq_args.language == '' then
		citeq_args.language = nil
	end
	if not citeq_args.language then
		-- try fallback to journal's language
		local journal_qid = followQid({qid = qid, props = "P1433"})
		citeq_args.language = journal_qid and getPropOfProp( {qid = journal_qid, prop1 = "P407", prop2 = "P218", ps = 1} )
	end
	for name, data in pairs(simple_properties) do
		citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals=data.maxvals, linked=data.linked, citeq_args[name] } )
		
		if data.populate_from_journal then
			citeq_args[name] = getValue( {"P1433", ps = 1, qid = qid, maxvals=0, citeq_args[name], qual=data.id, qualsonly='yes'} )
			citeq_args[name] = citeq_args[name] or getPropOfProp({qid = qid, prop1 = "P1433", prop2 = data.id, maxvals=data.maxvals, ps = 1})
		end
		if citeq_args[name] and citeq_args[name]:find('[[Category:Articles with missing Wikidata information]]', 1, true) then
			-- try fallback to work's native language
			citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals=data.maxvals, linked="no", lang = citeq_args.language } )
			if citeq_args[name]:find('^Q%d+$') then -- qid was returned
				-- try fallback to qid's native language
				local qid_language = getPropOfProp( {qid = citeq_args[name], prop1 = "P407", prop2 = "P218", ps = 1} )
				citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals=data.maxvals, linked="no", lang = qid_language } )
				if citeq_args[name]:find('^Q%d+$') then -- qid was returned again
					citeq_args[name] = nil
				end
			end
		end
		if data.others then
			oth[#oth+1] = citeq_args[name] and (name:gsub("^%l", string.upper) .. ": " .. citeq_args[name])
			citeq_args[name] = nil
		end
	end
	citeq_args.others = citeq_args.others or table.concat(oth, ". ")

	citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub("^''", ""):gsub("''$", ""):gsub("|''", "|"):gsub("'']]", "]]")

	citeq_args.ol = (getValue( {"P648", ps = 1, qid = qid, maxvals=1, citeq_args.ol } ) or ''):gsub("^OL(.+)$", "%1")
	citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)

	citeq_args.isbn = getValue( {"P957", ps = 1, qid = qid, maxvals=0, citeq_args.isbn } ) -- try ISBN 10
	
	citeq_args.url = getValue( {"P856", ps = 1, qid = qid, maxvals=0, citeq_args.url } ) -- try official website
	citeq_args.url = getValue( {"P2699", ps = 1, qid = qid, maxvals=0, citeq_args.url } ) -- try url

	local slink = mw.wikibase.getSitelink(qid)
	local label = mw.wikibase.getLabel(qid) or citeq_args.language and mw.wikibase.getLabelByLang(qid, citeq_args.language)
	if citeq_args.title then
		if slink then
			citeq_args.url = nil
			citeq_args.title = '[[' .. slink .. '|' .. wrap_nowiki(citeq_args.title) .. ']]'
		else
			citeq_args.title = wrap_nowiki(citeq_args.title)	
		end
	else
		if slink then
			citeq_args.url = nil
			if slink:lower() == label:lower() then
				citeq_args.title = '[[' .. slink .. ']]'
			else
				citeq_args.title = '[[' .. slink .. '|' .. wrap_nowiki(slink:gsub("%s%(.+%)$", ""):gsub(",.+$", "")) .. ']]'
			end
		else
			citeq_args.title = wrap_nowiki(label)
		end
	end

	if citeq_args.p or citeq_args.page then
		citeq_args.pages = nil
	end
	if citeq_args.pages then
		local _, count = string.gsub(citeq_args.pages, " %d+", "")
		if count == 1 then
			citeq_args.p = citeq_args.pages
			citeq_args.pages = nil
		end
	end

	for k, v in pairs(citeq_args) do
		if v == 'unset' or type(k) ~= 'string' then
			citeq_args[k] = nil
		end
	end

	if is_set (qid) then
		if not is_set (citeq_args.author) and not is_set (citeq_args.author1) then	-- if neither are set, try to get authors from wikidata
			get_name_list ('author', citeq_args, qid, wdl);						-- modify citeq_args table with authors from wikidata
		end

		if not is_set (citeq_args.editor) and not is_set (citeq_args.editor1) then	-- if neither are set, try to get editors from wikidata
			get_name_list ('editor', citeq_args, qid, wdl);						-- modify citeq_args table with editors from wikidata
		end
	end
	
	local author_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^author%d+$") then
			author_count = author_count + 1
		end
	end
	if author_count > 8 then -- convention in astronomy journals
		citeq_args['display-authors'] = citeq_args['display-authors'] or 3
	end
	
	local editor_count = 0
	for k, v in pairs(citeq_args) do
		if k:find("^editor%d+$") then
			editor_count = editor_count + 1
		end
	end
	if editor_count > 8 then -- convention in astronomy journals
		citeq_args['display-editors'] = citeq_args['display-editors'] or 3
	end

	local returntext = frame:expandTemplate{title = 'citation', args = citeq_args}	-- render the template
	if citeq_args.mode ~= 'cs1' then
		returntext = returntext .. ','
	end
	returntext = returntext .. ' [[WDQ (identifier)|Wikidata]]&nbsp;[[:d:' .. qid .. '|' .. qid .. ']]' -- go through special "WDQ (identifier)" redirect to reduce clutter in "What links here" and improve reverse lookup. (A better name might be "QID (identifier)", but needs to be kept in sync with what's used by Template:QID.)
	return returntext
end

return citeq