Modul:Wikidata/Sorters

Dokumentaciju za ovaj modul možete napraviti na stranici Modul:Wikidata/Sorters/dok

require "Modul:No globals"

local p = {}

local lib = require 'Modul:Wikidata/lib'
local Formatters = require 'Modul:Wikidata/Formatters'
local inverted = false
local sorters = {}
local current_sorter = 1

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(false)
	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
	if first_label == second_label then
		return sortSame(first, second)
	end

	local min_length = mw.ustring.len(first_label)
	if mw.ustring.len(second_label) < mw.ustring.len(first_label) then
		min_length = mw.ustring.len(second_label)
	end
	-- 
	local chars = " .,:-/'0123456789abcčćdđefghijklmnoprsštuvzž"
	local function charValue(char)
		return mw.ustring.match(chars, '()' .. char) or (mw.ustring.len(chars) + 1)
	end
	for i = 1, min_length do
		local first_char = mw.ustring.sub(mw.ustring.lower(first_label), i, i)
		local second_char = mw.ustring.sub(mw.ustring.lower(second_label), i, i)
		if charValue(first_char) ~= charValue(second_char) then
			return checkInvert(charValue(first_char) < charValue(second_char))
		end
	end
	return checkInvert(mw.ustring.len(second_label) == 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 pairs(array) do
				if first.qualifiers[prop] then
					for _, snak in pairs(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 pairs(array) do
				if second.qualifiers[prop] then
					for _, snak in pairs(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
	local Date = require 'Modul:Wikidata/datum'
	if FirstValues.point or SecondValues.point then
		if FirstValues.point and SecondValues.point then
			if Date.AreDatesSame(FirstValues.point, SecondValues.point) then
				return sortSame(first, second)
			else
				return checkInvert(Date.IsSecondLaterThanFirst(FirstValues.point, SecondValues.point))
			end
		else
			return checkInvert(not FirstValues.point)
		end
	end

	if FirstValues.begin or SecondValues.begin then
		if FirstValues.begin and SecondValues.begin then
			if Date.AreDatesSame(FirstValues.begin, SecondValues.begin) then
				if FirstValues.ending or SecondValues.ending then
					if FirstValues.ending and SecondValues.ending then
						if Date.AreDatesSame(FirstValues.ending, SecondValues.ending) then
							return sortSame(first, second)
						else
							return checkInvert(Date.IsSecondLaterThanFirst(FirstValues.ending, SecondValues.ending))
						end
					else
						return checkInvert(FirstValues.ending)
					end
				else
					return sortSame(first, second)
				end
			else
				return checkInvert(Date.IsSecondLaterThanFirst(FirstValues.begin, SecondValues.begin))
			end
		else
			return checkInvert(FirstValues.begin)
		end
	end

	if FirstValues.ending or SecondValues.ending then
		if FirstValues.ending and SecondValues.ending then
			return checkInvert(Date.IsSecondLaterThanFirst(FirstValues.ending, SecondValues.ending))
		else
			return checkInvert(not FirstValues.ending)
		end
	end
	return checkInvert(false)
end

local function sortByRank(first, second)
	if not (first and second) then
		return checkInvert(false)
	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 pairs(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)
	if lib.IsOptionTrue(options, 'invert') then
		inverted = true
	end

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

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

return p