Přeskočit na obsah

Modul:Wikidata/Sorters

Tato stránka je zamčena
Z Wikipedie, otevřené encyklopedie

require "Modul:No globals"

local p = {}

local lib = require 'Modul:Wikidata/lib'
local Formatters = require 'Modul:Wikidata/Formatters'

local current_sorter, inverted, sorters

local function checkInvert(value)
	if inverted then
		return not value
	end
	return value and true
end

local function sortSame(first, second)
	current_sorter = current_sorter + 1
	if sorters[current_sorter] then
		return sorters[current_sorter](first, second)
	end
	current_sorter = 1
	return checkInvert(false)
end

local function sortBySnaktype(first, second)
	if not (first and second) then
		return checkInvert(second)
	end
	if first.mainsnak.snaktype == second.mainsnak.snaktype then
		return sortSame(first, second)
	end
	if first.mainsnak.snaktype == 'value' and second.mainsnak.snaktype ~= 'value' then
		return checkInvert(true)
	elseif first.mainsnak.snaktype == 'somevalue' and second.mainsnak.snaktype == 'novalue' then
		return checkInvert(true)
	end
	return checkInvert(false)
end

local function alphaSort(first, second)
	if not (first and second) then
		return checkInvert(second)
	end
	if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
		return sortBySnaktype(first, second)
	end

	local first_value = Formatters.getRawValue(first.mainsnak)
	local second_value = Formatters.getRawValue(second.mainsnak)
	if first_value == second_value then
		return sortSame(first, second)
	end

	local first_label = mw.wikibase.label(first_value)
	local second_label = mw.wikibase.label(second_value)
	if not (first_label and second_label) then
		return checkInvert(first_label)
	end

	-- tohle přesunout do vlastního modulu
	-- na Wikislovníku mají Modul:Collation
	-- http://prirucka.ujc.cas.cz/?id=900
	local first_chars, second_chars = mw.text.split(first_label, ""), mw.text.split(second_label, "")
	if mw.ustring.lower(first_label) == mw.ustring.lower(second_label) then
		if first_label == second_label then
			return sortSame(first, second)
		end
		local i = 1
		while true do
			local first_char = first_chars[i]
			local second_char = second_chars[i]
			if first_char ~= second_char then
				return checkInvert(first_char == mw.ustring.upper(first_char))
			end
			i = i + 1
		end
	end

	local min_length = math.min(#first_chars, #second_chars)
	local chars = "aábcčdďeéěfghiíjklmnňoópqrřsštťuúůvwxyýzž0123456789.,;?!:„“–|/\()[]‹›{}"
	local function charValue(char)
		return mw.ustring.match(chars, '()' .. mw.ustring.lower(char)) or (mw.ustring.len(chars) + 1)
	end
	for i = 1, min_length do
		local first_charvalue = charValue(first_chars[i])
		local second_charvalue = charValue(second_chars[i])
		if first_charvalue ~= second_charvalue then
			return checkInvert(first_charvalue < second_charvalue)
		end
	end
	return checkInvert(#first_chars == min_length)
end

local function sortHasLabel(first, second)
	if not (first and second) then
		return checkInvert(second)
	end
	if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
		return sortBySnaktype(first, second)
	end

	local first_value = Formatters.getRawValue(first.mainsnak)
	local second_value = Formatters.getRawValue(second.mainsnak)
	if first_value == second_value then
		return sortSame(first, second)
	end

	local first_label = mw.wikibase.label(first_value)
	local second_label = mw.wikibase.label(second_value)
	if first_label and second_label then
		return sortSame(first, second)
	end
	return checkInvert(first_label)
end

local function sortByDate(first, second)
	local FirstValues, SecondValues
	if first and first.qualifiers then
		FirstValues = {}
		for key, array in pairs(lib.props) do
			for _, prop in ipairs(array) do
				if first.qualifiers[prop] then
					for _, snak in ipairs(first.qualifiers[prop]) do
						if lib.IsSnakValue(snak) then
							FirstValues[key] = Formatters.getRawValue(snak)
							break
						end
					end
				end
			end
		end
	end
	if second and second.qualifiers then
		SecondValues = {}
		for key, array in pairs(lib.props) do
			for _, prop in ipairs(array) do
				if second.qualifiers[prop] then
					for _, snak in ipairs(second.qualifiers[prop]) do
						if lib.IsSnakValue(snak) then
							SecondValues[key] = Formatters.getRawValue(snak)
							break
						end
					end
				end
			end
		end
	end

	if not (FirstValues and SecondValues) then
		return checkInvert(FirstValues)
	end
	if FirstValues.point or SecondValues.point then
		if FirstValues.point and SecondValues.point then
			if FirstValues.point == SecondValues.point then
				return sortSame(first, second)
			else
				return checkInvert(FirstValues.point < SecondValues.point)
			end
		else
			return checkInvert(FirstValues.point)
		end
	end

	if not (SecondValues.begin or SecondValues.ending) then
		if not (FirstValues.begin or FirstValues.ending) then
			return sortSame(first, second)
		else
			return checkInvert(true)
		end
	end
	if not (FirstValues.begin or FirstValues.ending) then
		return checkInvert(false)
	end

	if FirstValues.begin and SecondValues.begin then
		if FirstValues.begin ~= SecondValues.begin then
			return checkInvert(FirstValues.begin < SecondValues.begin)
		end
		if FirstValues.ending and SecondValues.ending then
			if FirstValues.ending ~= SecondValues.ending then
				return checkInvert(FirstValues.ending < SecondValues.ending)
			else
				return sortSame(first, second)
			end
		end
		if FirstValues.ending or SecondValues.ending then
			return checkInvert(FirstValues.ending)
		else
			return sortSame(first, second)
		end
	end
	if FirstValues.ending and SecondValues.ending then
		if FirstValues.ending == SecondValues.ending then
			if FirstValues.begin or SecondValues.begin then
				return checkInvert(not FirstValues.begin)
			else
				return sortSame(first, second)
			end
		end
	end
	if FirstValues.begin and SecondValues.ending then
		return checkInvert(FirstValues.begin < SecondValues.ending)
	end
	if FirstValues.ending and SecondValues.begin then
		return checkInvert(not (SecondValues.begin < FirstValues.ending))
	end
	if FirstValues.ending and SecondValues.ending then
		return checkInvert(FirstValues.ending < SecondValues.ending)
	end
	return checkInvert(false)
end

local function sortByRank(first, second)
	if not (first and second) then
		return checkInvert(second)
	end
	if first.rank == second.rank then
		return sortSame(first, second)
	end
	if first.rank == 'preferred' and second.rank ~= 'preferred' then
		return checkInvert(true)
	elseif first.rank == 'normal' and second.rank == 'deprecated' then
		return checkInvert(true)
	end
	return checkInvert(false)
end

local function sortByQuantity(first, second)
	if not (first and second) then
		return checkInvert(second)
	end

	if not (lib.IsSnakValue(first.mainsnak) and lib.IsSnakValue(second.mainsnak)) then
		return sortBySnaktype(first, second)
	end

	local first_quantity = Formatters.getRawValue(first.mainsnak)
	local second_quantity = Formatters.getRawValue(second.mainsnak)
	if first_quantity == second_quantity then
		return sortSame(first, second)
	end

	return checkInvert(first_quantity > second_quantity)
end

local Methods = {
	alpha = {
		datatypes = { 'wikibase-item', 'wikibase-property' },
		sorter = alphaSort,
	},
	date = {
		sorter = sortByDate
	},
	hasLabel = {
		datatypes = { 'wikibase-item', 'wikibase-property' },
		sorter = sortHasLabel
	},
	number = {
		datatypes = { 'quantity' },
		sorter = sortByQuantity,
	},
	rank = {
		sorter = sortByRank
	},
	snaktype = {
		sorter = sortBySnaktype
	},
}

local function getSorter(method, curr_datatype)
	if Methods[method] then
		if Methods[method].datatypes then
			for _, datatype in ipairs(Methods[method].datatypes) do
				if curr_datatype == datatype then
					return Methods[method].sorter
				end
			end
			return error(lib.formatError('invalid-datatype', method))
		end
		return Methods[method].sorter
	end
	return error(lib.formatError('invalid-sort', method))
end

function p.sortStatements(statements, options)
	current_sorter = 1
	inverted = lib.IsOptionTrue(options, 'invert')
	sorters = {}

	local datatype = statements[math.random(#statements)].mainsnak.datatype
	local methods = lib.textToTable(options.sort)
	for key, method in ipairs(methods) do
		table.insert(sorters, getSorter(method, datatype))
	end

	table.sort(statements, sorters[1])
	-- @deprecated
	return statements
end

return p