Hoppa till innehållet

Modul:Sportdata

Från Wikipedia
Version från den 22 oktober 2024 kl. 20.25 av Gunnar Larsson (Diskussion | Bidrag) (hantering av situation när får vektor när inte förväntas/borde få det)
(skillnad) ← Äldre version | visa nuvarande version (skillnad) | Nyare version → (skillnad)

Dokumentation [visa] [redigera] [historik] [rensa sidcachen]


Detta är en hjälpmodul som innehåller funktioner som används av flera andra moduler, som :

För att använda funktionerna behöver du skriva require( 'Modul:Sportdata') i modulen som använder funktionerna. Modulen utnyttjar själv modulerna Modul:Sportdata localisation (för att underlätta översättning till andra språk ligger alla textsträngar som är språkberoende här), Modul:Sportdata rankings (ranking för olika idrottstävlingar för att t.ex. prioritera VM & OS högre än spel i lägre divisionerna i den nationella ligan) samt Modul:Referenshantering (för att visa referenser).

Funktioner

[redigera wikitext]

competitionData

[redigera wikitext]

isNationalTeam

[redigera wikitext]

processDates

[redigera wikitext]

tblSeasonData

[redigera wikitext]

tblTeamInfo

[redigera wikitext]

txtPosition

[redigera wikitext]
  • Syfte: Skapa tabellcell med en person/ett lags placering i en tävling, som är färgsatt om de kom på medaljplats
  • Returnerar: textsträng (html-kod för cellen)
  • Argument:
    • iPosition – placeringen i tävlingen
  • Vad funktionen gör:
    • Skapar en tabellcell med placering
    • Placeringen är 1 (), 2 () eller 3 () får cellen respektive färg, annars ingen bakgrund.

txtTeamname

[redigera wikitext]
  • Syfte: Formatera en textsträng med start- och/eller slutår
  • Returnerar: textsträng
  • Argument:
    • startyear – startår (kan vara tom)
    • endyear – slutår (kan vara tom)
  • Vad funktionen gör (returnerar):
    • Om både startyear och endyear finns:
      • Om de är olika: returnerar startyearendyear
      • Om de är samma: returnerar startyear
    • Om startyear finns, men inte endyear: returnerar startyear
    • Om endyear finns, men inte startyear: returnerar –endyear
    • Om var sig startyear eller endyear finns: returnerar tom sträng
localisation = require( 'Modul:Sportdata localisation' )
local sdr = require( 'Modul:Sportdata rankings' )
settings = require( 'Modul:Sportdata settings' )
local rm = require( 'Modul:Referenshantering' )

local sd={}
local veryearly="+0001-01-01T00:00:00Z"

arrayFlagCountries={ Q29999 ='Q55'}			-- Cases where on object will not produce a flag and hence another object will be used

function txtShowLink(tblItem)
	if (tblItem.txtSitelink) then
		return '[[' .. tblItem.txtSitelink .. '|' .. tblItem.txtLabel .. ']]'
		else
		return tblItem.txtLabel
		end
	end

p.competitions_without_ranking=function(frame)
	p.frame=frame
	p=p.data()
	local results=p.filter(p,"results","iCompetitionRank",9999999)
	txt=localisation.txtDescriptionMissingRanks .. "<table class='wikitable'><tr><th>" .. localisation.txtId_ltabel .. "</th><th>" .. localisation.txtLabel_label .. "</th></tr>"
	for i=1,#results do
		txt=txt.."<tr><td>" .. results[i].competitiontype.qid .. "</td><td>" .. results[i].competitiontype.txtLabel .. "</td>"
		end
	txt=txt.."</table>"
	return txt
	end

p.lastchecked=function()
	local results=p.all(p,"results")
	local latestdate=findlatestdate(results)
	if not latestdate then
		latestdate=veryearly
		end
	if (p.age) then -- If a person and not a club 
		local teams_played=p.all(p,"teams_played")
		if (teams_played) then
			local latestdate_teams_played=findlatestdate(teams_played)
			if (latestdate_teams_played>latestdate) then
				latestdate=latestdate_teams_played
				end
			end
		if (teams_coached) then
			local teams coached=p.all(p,"teams_coached")
			local latestdate_teams_coached=findlatestdate(teams_coached)
			if (latestdate_teams_coached>latestdate) then
				latestdate=latestdate_teams_coached
				end
			end
		else
			p.alive=not p.dissolved
			end
	local latestyear=tonumber(string.sub(latestdate,2,5))
	local yearsince = tonumber(os.date("%Y"))-latestyear
	
	local potentially_active= yearsince<settings.iCareerAssumedEndedIfNothingNewInThisManyYears
	local bNoDateFormat=true
	if ((localisation.dateformat=='YYYY-MM-DD') or (localisation.dateformat=='DD-MM-YYYY')) then
		bNoDateFormat=false
		end
	local txtLastChecked=p.frame.args['lastchecked']
	if (not (bNoDateFormat or isempty(txtLastChecked))) and ((#txtLastChecked==8) or (#txtLastChecked==10)) then
		local yearLastChecked,monthLastChecked,dayLastChecked
		if (#txtLastChecked==8) then
			if (localisation.dateformat=='YYYY-MM-DD') then
				yearLastChecked=tonumber(string.sub(txtLastChecked,1,4))
				monthLastChecked=tonumber(string.sub(txtLastChecked,5,6))
				dayLastChecked=tonumber(string.sub(txtLastChecked,7,8))
				end
			if (localisation.dateformat=='DD-MM-YYYY') then
				dayLastChecked=tonumber(string.sub(txtLastChecked,1,2))
				monthLastChecked=tonumber(string.sub(txtLastChecked,3,4))
				yearLastChecked=tonumber(string.sub(txtLastChecked,5,8))
				end
			end
		if (#txtLastChecked==10) then
			if (localisation.dateformat=='YYYY-MM-DD') then
				yearLastChecked=tonumber(string.sub(txtLastChecked,1,4))
				monthLastChecked=tonumber(string.sub(txtLastChecked,6,7))
				dayLastChecked=tonumber(string.sub(txtLastChecked,9,10))
				end
			if (localisation.dateformat=='DD-MM-YYYY') then
				dayLastChecked=tonumber(string.sub(txtLastChecked,1,2))
				monthLastChecked=tonumber(string.sub(txtLastChecked,4,5))
				yearLastChecked=tonumber(string.sub(txtLastChecked,7,10))
				end
			end
		local argsLastChecked={dayLastChecked,monthLastChecked,yearLastChecked}
		local iYearsBeforeUpdateNeeded
		if p.alive then
			if potentially_active then
				iYearsBeforeUpdateNeeded=settings.iCheckFrequencyWhenActive	-- Need to check frquently
				else
				iYearsBeforeUpdateNeeded=settings.iCheckFrequencyLikelyNoLongerActive	-- No need to check frquently if career likely to have ended (but can let it pop up after a long time in case some information was missed or restarted career, for example as a coach)
				end					
			else
			iYearsBeforeUpdateNeeded=settings.iCheckFrequencyWhenDead	-- No need to check frquently if dead (but can let it pop up after a long time in case some information was missed)
			end			
		local argsUpdateAfter={yearLastChecked+iYearsBeforeUpdateNeeded,monthLastChecked,dayLastChecked,"","[[" .. txtCategory .. ":" .. txtNotUpdatedCategory .. "]]"}
		--Empty row added for esthetic reasons, to separate the date from the rest of the infobox
		p.iCounter=p.iCounter+1
		myArgs[txtContent .. p.iCounter]="  &nbsp;"

		--Write out the when data last checked, include a note about what is meant by the date
		p.iCounter=p.iCounter+1
		myArgs[txtLabel .. p.iCounter]=txtLastCheckedLabel .. p.frame:extensionTag("ref", txtLastCheckedLabel_Clarification)
		myArgs[txtContent .. p.iCounter]=p.frame:expandTemplate{title=localisation.txtDMYtemplate,args=argsLastChecked} .. p.frame:expandTemplate{title=localisation.txtShowAftertemplate,args=argsUpdateAfter}
		else
		if (p.alive and potentially_active) then
			p.iCounter=p.iCounter+1
			myArgs['under']=myArgs['under']..'[[' .. txtCategory .. ':' .. localisation.txtNoLastCheckedDate .. ']]'
			end	
		end
end

	-- Function to find the element with the latest date
function findlatestdate(tbl)
    if #tbl == 0 then return veryearly end
    local latestElement = tbl[1]
    latestElement.date=veryearly
    for _, element in ipairs(tbl) do
    	bFound=false
    	if (element.txtDateTo) then
    		element.date=element.txtDateTo
    		bFound=true
    		end
    	if (not bFound) and (element.txtDateFrom) then
    		element.date=element.txtDateFrom
    		bFound=true
			end
    	if (not bFound) and element.to and element.to.raw then
    		element.date=element.to.raw
    		bFound=true
			end
    	if (not bFound) and element.from and element.from.raw then
    		element.date=element.from.raw
    		bFound=true
			end
		if not (bFound) then
			element.date=veryearly
			end
        if element.date > latestElement.date then
            latestElement = element
        	end
	    end
    return latestElement.date
end

function txtPosition(iPosition,txtIsQualifier)
	local bMedal=0
	if (iPosition==999999) then
		iPosition=nil
		end
	if (iPosition==999998) then
		iPosition=txtQualifier
		end
	if not (iPosition==nil) then
		if ((iPosition==1) or (iPosition=="1")) then
			txtPosColour='gold'
			bMedal=1
			end
		if ((iPosition==2) or (iPosition=="2")) then
			txtPosColour='silver'
			bMedal=1
			end
		if ((iPosition==3) or (iPosition=="3")) then
			txtPosColour='#CC9966'
			bMedal=1
			end
		if (bMedal==1) then
			return '<td align="center" bgcolor="' .. txtPosColour .. '">' .. iPosition  .. '</td>'
			else
			return '<td>' .. iPosition  .. '</td>'
			end
		else
		return '<td></td>'
		end
end

function txtYears(startyear,endyear,txtSeparator)
if (not (txtSeparator)) then
	txtSeparator='–'
	end
if (startyear=='' or startyear==nil) then
	if (endyear=='' or endyear==nil) then
		if (endyear=='' or endyear==nil) then
			return ''
			else
			return txtSeparator
			end
		else
		return txtSeparator .. endyear
		end
	else
	if (endyear==startyear) then
		return startyear
		else
		return startyear .. txtSeparator .. endyear
		end
	end
end

function processDates_model(entityid,txtSeparator)
	txtDateFrom=''
	txtDateTo=''
	local p580=mw.wikibase.getAllStatements(entityid, 'P580' )
	local hasstart=next(p580)
	p582=mw.wikibase.getAllStatements(entityid, 'P582' )
	local hasend=next(p582)
	if hasstart then
		txtDateFrom=read(p580[1],'time')
		end
	if hasend then
		txtDateTo=read(p582[1],'time')
		end
	--For sorting purposes set date from and to date to the same if one of them is lacking
	if hasend and not hasstart then
		txtDateFrom=txtDateTo
		end
	if hasstart and not hasend then
		txtDateTo=txtDateFrom
		end
	hasanydate=hasstart or hasend
	return hasanydate,txtDateFrom,txtDateTo
end


function years(value)
	if value['qualifiers'] then
		if value['qualifiers']['P580'] then
			startdate=readQualifier(value.qualifiers.P580[1],'time')  or ''    -- readQualifier returns nil when P580 is "somevalue"
			startyear=string.sub(startdate,2,5)
			else
			startdate=''
			startyear=''
			end
		if value['qualifiers']['P582'] then
			enddate=readQualifier(value.qualifiers.P582[1],'time') or ''     -- readQualifier returns nil when P582 is "somevalue"
			endyear=string.sub(enddate,2,5)
			else
			enddate=''
			endyear=''
			end
		return startyear,endyear,startdate,enddate
	end
	return '','','',''
end

function readDate(value,pid)
	if value['qualifiers'] then
		if value['qualifiers'][pid] then
			if value['qualifiers'][pid][1] then
				local date={}
				datesnak=value['qualifiers'][pid][1]
				date.raw=readQualifier(datesnak,'time')
				date.precision=readQualifier(datesnak,'precision')
				if (date.raw) then
					date.year=string.sub(date.raw,2,5)
					else
					return nil
					end
				date.formatted=mw.wikibase.formatValue(datesnak)
				return date
			end
		end
	end
	return nil
end

function readStatementAtDate(qid,pid,type,asofdate)
	local ret={}
	if not(asofdate) then
		asofdate=rm.now()
		end
	local claims = mw.wikibase.getAllStatements(qid,pid) -- If the club have had many names, loop through to find the one that was used when the player played at the club
	if (next(claims)) then
		if (type=='text') then
			return rm.readtext(claims, asofdate)
			else
		-- Loop through results
	    for key, value in pairs( claims ) do
			local datefrom,dateto =  readDates(value)
			local txtFrom,txtTo
			if (datefrom) then
				txtFrom=datefrom.raw
				if isempty(txtFrom) then datefrom=nil end
				end
			if (dateto) then
				txtTo=dateto.raw
				if isempty(txtTo) then dateto=nil end
				end
	    	if (not(datefrom) and not(dateto)) or (datefrom and not(dateto) and asofdate >= txtFrom) or (not(datefrom) and dateto and asofdate <= txtTo) or (datefrom and dateto and asofdate >= txtFrom and asofdate <= txtTo) then
	    		return true,read(value,type)
	    		end
    		end
		end
	end
	return false
	end

function readDates(value)
	local startdate=readDate(value,'P580')
	local enddate=readDate(value,'P582')
	if not (startdate or enddate) then
		startdate=readDate(value,'P585')
		enddate=startdate
		end
	return startdate,enddate
end

function txtDate (iYear,iMonth,iDay)
	bHasMonth=not isempty(iMonth)
	bHasDay=not isempty(iDay)
	if (bHasMonth and bHasDay) then
		return mw.getCurrentFrame():preprocess('{{#time:[[j F]] [[Y]]|'..iYear..'-'..iMonth..'-'..iDay..'}}')
		else
		if (bHasMonth) then
			return mw.getCurrentFrame():preprocess('{{#time:F [[Y]]|'..iYear..'-'..iMonth..'-01}}')
			else
			return '[[' .. iYear .. ']]'
			end
		end
end

--Returns name of the team a given year
function txtTeamname(idTeam,year)
	return txtWikilink(idTeam,year)
end

function isClub( id,txtProperty,iLevel )
	if isempty(id) then
		return false
		end
	if not (iLevel) then
		iLevel=1
	end
	if (iLevel==1) then
		if (isNationalTeam( id,txtProperty,iLevel)) then return false end
 	end--Start by checking it is not a national team
	local instanceofs=mw.wikibase.getBestStatements(id, txtProperty )
	local key, value

    for key, value in pairs( instanceofs ) do
		local txtId=read(value,'id')
		if (txtId == 'Q847017' or txtId == 'Q12973014' or txtId=='Q108395614'  or txtId=='Q13580678' or txtId=='Q115898316' or txtId=='Q15944511' or txtId=='Q476028') then return true end
		if (iLevel<4) then
			if (isClub(txtId,'P279',iLevel+1)) then return true end
			end
		end
		return false
end

function isNationalTeam( id,txtProperty,iLevel)
	if isempty(id) then
		return false
		end
	if not (iLevel) then
		iLevel=1
		end
	local instanceofs=mw.wikibase.getBestStatements(id, txtProperty );
    for key, value in pairs( instanceofs ) do
		local txtId=read(value,'id')
		if txtId=='Q1194951' then return true end
		if (iLevel<4) then
			if (isNationalTeam(txtId,'P279',iLevel+1)) then return true end
			end
		end
	return false
end

function allsame(tbl,property)
	bAllSame=true
    iLastType=nil
    local key,value
    for key, value in pairs(tbl) do
		if (bAllSame and (value[property])) then	--Only loop till a different value has been found
	    	if (iLastType==nil) then					--If first item set value for iLastType, otherwise compare if same as last one
	    		iLastType=value[property]
    		else
	    		bAllSame=iLastType==value[property]
	    	end
		end
    end
	return bAllSame
end


function getImage(qid,pid)
	img=mw.wikibase.getBestStatements(qid, pid )
	bDescription=false -- variable to check if a description has been found
	if next(img) then
		image={}
		image.txtFilename = img[1]['mainsnak']['datavalue']['value']
--		txtFilename = read(img[1],'value')
	    if img[1].qualifiers and img[1].qualifiers.P2096 then
	    	-- Loop through all descriptions
		    for key, value in pairs(img[1].qualifiers.P2096) do
			    -- Only add if has the same language code as the local wikipedia
			    language=readQualifier(value,'language')
			    if (language==txtLanguageCode) then
			    	bDescription=true                                                                    -- description found!
				    image.txtDescription=readQualifier(value,'text')  -- add description (+ pen to make it easier to edit)
				    end
			    end
	    	end
	    image.width,image.height=getImageSize(image.txtFilename)
	    image.qid=qid
	    image.pid=pid
	    -- add image to the infobox array
--	    myArgs[txtImage]='[[File:' .. wdImage .. '|' .. txtWidth_px .. ']]'
		return image
		end
	return
end

function getImageSize(imageName)
    local title = mw.title.new(imageName, 'File')
    if not title then
        return nil, nil, "Invalid image name"
    end

    local file = title and title.file
    if not file then
        return nil, nil, "File not found"
    end

    return file.width, file.height
end

function showImage(tblImage)
	    myArgs[localisation.txtImage]='[[File:' .. tblImage.txtFilename .. '|' .. settings.iWidth_px .. 'px]]'
	    if (tblImage.txtDescription) then
	    	myArgs[localisation.txtCaption]=tblImage.txtDescription..txtEditPen(tblImage.qid,tblImage.pid)
	    	else
	    	myArgs[localisation.txtImage]=myArgs[localisation.txtImage]..txtEditPen(tblImage.qid,tblImage.pid)
	    	end
end


-- Custom comparison function
-- 
function compareUP(a, b)
    -- Handle nil values
--    if isempty(a) then
    if (a==nil) then
        return false
--    elseif isempty(b) then
    elseif (b==nil) then
        return true
--		  return false
   end
    -- Compare the attributes (assuming a and b are strings or numbers)
    return a < b
end

function compareDOWN(a, b)
    -- Handle nil values
--    if (isempty(a)) then
    if (a==nil) then
        return true
--    elseif (isempty(b)) then
    elseif (b==nil) then
        return false
    end
    -- Compare the attributes (assuming a and b are strings or numbers)
    return a < b
end

function isempty(foo)
	return (foo==nil) or (foo=='') or (foo==0)
end

function sortActivityLog(posts)
    table.sort(posts, function(a, b) return 
    	(compareDOWN(a.yearfrom,b.yearfrom)) or  
    	(a.yearfrom == b.yearfrom and compareUP(a.yearto,b.yearto)) or
    	(a.yearfrom == b.yearfrom and a.yearto == b.yearto and compareDOWN(a.datefrom,b.datefrom)) or  
    	(a.yearfrom == b.yearfrom and a.yearto == b.yearto and a.datefrom == b.datefrom and compareUP(a.dateto,b.dateto)) 
    end)
	return posts
end

function mergeTables(t1, t2)
    local mergedTable = {}
    for _,v in ipairs(t2) do
        table.insert(mergedTable, v)
    end
    for _,v in ipairs(t1) do
        table.insert(mergedTable, v)
    end
    return mergedTable
end

function tblAddLocation(frame,myArgs,iCounter,txtLocationLabel,entityid, iProperty)
	--Place
	entityPlace=mw.wikibase.getBestStatements(entityid, iProperty )
	--Only show if set 
    if next(entityPlace) then
	    idPlace=read(entityPlace[1],'id')
		txtCountry = readFirstStatement(idPlace,'P17')
		txtPlace = txtWikilink(idPlace)
		if txtCountry then 
			txtPlace = txtPlace .. ', ' .. '[[' .. txtCountry .. ']]' .. txtUnpackReference(frame,tblProcessAllRefsForItemWD(frame,entityPlace[1].references,entityid,iProperty)) ..  txtEditPen(entityid,iProperty)
	    end
    	iCounter=iCounter+1
	    myArgs[txtLabel .. iCounter]=txtLocationLabel
    	myArgs[txtContent  .. iCounter]=txtPlace
    end
	return myArgs,iCounter
end


function getHomeVenues (qid)
	claimHomeVenue=mw.wikibase.getBestStatements(qid, 'P115' )
	local venues={}
    for key, value in pairs( claimHomeVenue ) do
    	local venue={}
	    venue.id=read(value,'id')
		venue.txtWikilink=txtWikilink(venue.id)
		venue.from,venue.to=readDates(value)
		claimCapacity=mw.wikibase.getBestStatements(venue.id,'P1083')
		venue.capacity=useStatement(venue.id,'P1083')
		venue.bHasRef,venue.ref = processAllRefsForWDItem_model(value.references,qid,value.id)
		venue.qid=qid
		venue.pid=value.id
		table.insert(venues,venue)
    end
    return venues
end

function showPlace(data)
	local txt=''
	local bDifferentName=data.name_atthetime and (not (data.name_atthetime==data.name))
	local both = bDifferentName and (not (data.country_atthetime==data.country)) -- If both the name of the place and the country was different at the time compared to now
	if not (data.id==data.countryid) then -- Only show place if it is not set to a country (that will be shown after anyway)
		if (data.sitelink) then -- Add sitelink to place (i.e. use a wikilink)
			txt='[[' .. data.sitelink .. '|'
			end
		if (bDifferentName) then -- If it had another name at the time use it, otherwise use its current name
			txt=txt..data.name_atthetime
			else
			txt=txt..data.name
			end			
		if (data.sitelink) then -- Finish the wikilink for the place
			txt=txt..']]'
		end
		if (not (both) and bDifferentName) then -- Add current name for the place if it is not the same as the past name. Also, do not add it here if the country was different (then it will be added later)
			txt=txt .. " (" .. localisation.txtCurrently .. " ''" .. data.name .. "'')"
			end
		txt=txt .. ', '
		end
	if (not (data.country_atthetime==data.country)) then -- If the country at the time was not the current country of the place add information
		txt=txt..data.country_atthetime
		if (both) then -- If the place also had a different name include it
			txt = txt .. " (" .. localisation.txtCurrently .. " ''" .. data.name .. ", " .. data.country .. "'')"
			else
			txt = txt .. " (" .. localisation.txtCurrently .. " ''" .. data.country .. "'')"
			end
		else
		txt=txt..data.country
	end			
	return txt
	end

function getPlace (value,qid,pid,asofdate)
	local place={}
	place.id=read(value,'id')
	place.sitelink=mw.wikibase.getSitelink(place.id)
	place.date=asofdate
	local isok,txtNameNow=readStatementAtDate(place.id,'P2561','text')
	if (isok) then
		place.name=txtNameNow
		else
		place.name=mw.wikibase.getLabel(place.id)
		end
	local isok,txtNameAtTheTime=readStatementAtDate(place.id,'P2561','text',asofdate)
	if (isok) then
		place.name_atthetime=txtNameAtTheTime
		end
	local isok,iCountryNow=readStatementAtDate(place.id,'P17','id')
	if (isok) then
		place.countryid=iCountryNow
		place.country = txtWikilink(place.countryid)
		else
		place.country=mw.wikibase.getLabel(place.id)
		end
	local isok,iCountryAtTheTime=readStatementAtDate(place.id,'P17','id',asofdate)
	if (isok) then
		place.countryid_atthetime=iCountryAtTheTime
		place.country_atthetime=txtWikilink(iCountryAtTheTime)
		end
	place.from,place.to=readDates(value)
	place.bHasRef,place.ref = processAllRefsForWDItem_model(value.references,qid,pid)
	place.qid=qid
	place.pid=pid
	return place
end

function getLocation (qid,pid,bAllowMultiple,asofdate)
	local places={}
	local claimPlace=mw.wikibase.getBestStatements(qid, pid)
	if (bAllowMultiple) then
	    for key, value in pairs( claimPlace ) do
	    	local place=getPlace(value,qid,pid,asofdate)
			table.insert(places,place)
	    end
		return places
		else 
		if (claimPlace[1]) then
			return getPlace(claimPlace[1],qid,pid,asofdate)
			else
			return
			end
		end
return places
end

-- Function to split a string by a delimiter (by chatgpt)
function split(inputStr, delimiter)
    local result = {}
    for match in (inputStr .. delimiter):gmatch("(.-)" .. delimiter) do
        table.insert(result, match)
    end
    return result
end

-- Function to merge two tables
function merge(table1, table2)
    local result = {}
    -- Insert all key-value pairs from table1 into result
    for key, value in pairs(table1) do
        result[key] = value
    end
    -- Insert all key-value pairs from table2 into result
    for key, value in pairs(table2) do
        result[key] = value
    end
    return result
end

function useOneStatement(claim,qid,txtAlternative,arrayAllowedQualifiers,funcprocessing)
	local objOutput={}
	objOutput.id=read(claim,'id')
	objOutput.from,objOutput.to=readDates(claim)
	if ((not txtAlternative) or (txtAlternative=="formatValue")) then
		objOutput.data=mw.wikibase.formatValue(claim.mainsnak)
		end
	if (txtAlternative=="time") then
		tmpTable=claim.mainsnak.datavalue.value
		objOutput.raw=tmpTable.time
		objOutput.precision=tmpTable.precision
		if (objOutput.raw) then
			objOutput.year=string.sub(objOutput.raw,2,5)
			end
		objOutput.formatted=mw.wikibase.formatValue(claim.mainsnak)
		end
	if (txtAlternative=="wikilink") then
		objOutput.data=txtWikilink(read(claim,'id'))
		end
	if arrayAllowedQualifiers then
		for i=1,#arrayAllowedQualifiers do
			local qfid=arrayAllowedQualifiers[i]
			objOutput[qfid]=getQualifierId(claim,qfid)
			end
		end
	if (txtAlternative=="function") then
		objOutputProcessed=funcprocessing(objOutput,claim)
		objOutput=merge(objOutputProcessed,objOutput)
		end	
	objOutput.bHasRef,objOutput.ref=processAllRefsForWDItem_model(claim.references,qid)
	objOutput.qid=qid
	objOutput.pid=claim.id
	return objOutput 
end

function useStatement(qid,pid,txtAlternative,arrayAllowedQualifiers,funcprocessing,onlyallowone)
	local claim=mw.wikibase.getBestStatements(qid, pid )
	if #claim==0 then
		return
		end
	if onlyallowone or (#claim==1) then
		firstClaim=claim[1]
		return useOneStatement(firstClaim,qid,txtAlternative,arrayAllowedQualifiers,funcprocessing)
		end
	if #claim>1 then
		statements={}
		for key,value in pairs(claim) do
			table.insert(statements,useOneStatement(value,qid,txtAlternative,arrayAllowedQualifiers,funcprocessing))
		end
		return statements
	end
end

--Artiklar som använder modul Sportdata och person/klubb deltar i tävling som saknar P3450
function competitionData_model(tbl,claimsIndividualCompetition)
	--Get the id of the reoccuring sports event
    tbl.iLeague=read(claimsIndividualCompetition[1],'id')
    --Get name of the reoccuring sports event
    tbl.txtLeague=txtWikilinkWOsitelink(tbl.iLeague)
    tbl.txtLeague_plain=getLabelByEntity(tbl.iLeague)
    --Get its rank (lower number = more status)
    tbl.iCompetitionRank=CompetitionDb[tbl.iLeague]
    --If the reoccuring sports event has no stored rank number give it a very high one (= low status)
	if (tbl.iCompetitionRank==nil) then
		tbl.iCompetitionRank=9999999
	end
	tbl.iArea=sd.competitionarea(tbl.iLeague)
	tbl.iCompetitionClass,tbl.iGender=getGender(tbl.iLeague)
	tbl.iSport=readFirstStatementId(tbl.iLeague,'P641' )
	return tbl
end

function getGender(iLeague)
	--Get competition class (i.e. women's football, men's basketball etc.)
    local claimsType = mw.wikibase.getBestStatements(iLeague,'P2094' )
    local iGender
    if (next(claimsType)) then
		iCompetitionClass=read(claimsType[1],'id')
		claimsUnderclass = mw.wikibase.getAllStatements(iCompetitionClass,'P279' )
		for key, value in pairs( claimsUnderclass ) do
			local iObject=read(value,'id')
			if (iObject=="Q35555522") then
				return iCompetitionClass,1
				end
			if (iObject=="Q920057") then
				return iCompetitionClass,2
				end
			end
	    end
	return iCompetitionClass,0
end

function getOneCompetiton(qid,valueParticipitation)
	local tbl={}
	tbl.errors=''
	tbl.iSeason=read(valueParticipitation,'id')
	iTmpClass=getQualifierId(valueParticipitation,'P2094')
    tmpCompetitionClass=rm.tblWikilink(iTmpClass,rm.now(),true)
    if not next(tmpCompetitionClass) then
	    tmpCompetitionClass=rm.tblWikilink(readFirstStatementId(tbl.iSeason,'P2094'),rm.now(),true)
	    end
    if next(tmpCompetitionClass) then
	    tbl.competitionClassThisEvent=tmpCompetitionClass
	    end

	tbl.iStatus=1														-- Everything ok	
    --Default values if nothing found
    tbl.iCompetitionType="Q0"
    tbl.sitelink=mw.wikibase.getSitelink(tbl.iSeason)
    -- Assume team if nothing else set
    iThisParticipant=qid
	txtPropertyForParticipation='P1923'
	tbl.qid=qid
	tbl.pid='P1344'
	
	tbl.iCoached=getPropertyId(valueParticipitation,'P6087')
	if (tbl.iCoached) then
		tbl.bCoached=true
		iThisParticipant=tbl.iCoached
		end
	tbl.iPlayed=getPropertyId(valueParticipitation,'P54')
	if (tbl.iPlayed) then
		tbl.bPlayed=true
		iThisParticipant=tbl.iPlayed
		end
	if (tbl.bPlayed or tbl.bCoached) then
		txtPropertyForParticipation='P1923' --teams
		tbl.iParticipator=iThisParticipant
		iCountry=readFirstStatementId(iThisParticipant,'P17')
		if not (iCountry) then
			iLocation=readFirstStatementId(iThisParticipant,'P159')
			if (iLocation) then
				iCountry=readFirstStatementId(iLocation,'P17')
				end
			end
		tblTeam={id=iThisParticipant,country=iCountry}
		tbl.team=tblTeam
		end
	tbl.bIsClub=isClub(iThisParticipant,'P31')
	tbl.bIsNationalTeam=isNationalTeam(iThisParticipant,'P31')
	iPartner=getQualifierId(valueParticipitation,'P1706')
	bIndividual=not (tbl.bIsClub or tbl.bIsNationalTeam)
	if (bIndividual) then
		bIndividual=true
		txtPropertyForParticipation='P710' --individuals
		iThisParticipant=qid
		end

    --Check wether the object is an instance of a reoccuring sports event, a qualification event or not at all sports related
    local claimsMainorQualifier = mw.wikibase.getBestStatements(tbl.iSeason, 'P31' )

	tbl.bMainOrQualifier='-' -- Defaults to not being a competition-- Defaults to not being a competition (as P1344, that is usually used to call the function can cover a lot of things, not just sports events)
	bFoundQualification=false   -- Check if found qualification (will take priority of instance of both Q27020041 and Q2122052/Q51036091 )
    --Loop through instances statements
    for keyQualifier, valueQualifier in pairs( claimsMainorQualifier ) do
		iInstanceOf=read(valueQualifier,'id')
		
		--If a main competition
		if (not(bFoundQualification) and ((iInstanceOf=='Q27020041') or (iInstanceOf=='Q114609228') or (iInstanceOf=='Q18536594') or (iInstanceOf=='Q51031626') or (iInstanceOf=='Q26132862'))) then
			tbl.bMainOrQualifier='M' -- Main competition
			end
		--If a qualifier
		if ((iInstanceOf=='Q2122052') or (iInstanceOf=='Q51036091')) then
			--Set as qualifier in table
			tbl.bMainOrQualifier='Q'
--			--Get references
--			tbl.bHasref,tbl.ref=processAllRefsForWDItem_model(valueQualifier['references'],entityid,'P1344')
			--Look for statement for what it qualifies for and set that as the main competiton
			claimsMainCompetition=mw.wikibase.getAllStatements(tbl.iSeason, 'P3085' )
			iMainCompetition=read(claimsMainCompetition[1],'id')
            -- If there is data on what it qualifies for set that as the sports event (so for example a qualification for a world championship will be shown in the same way as the world championship itself (but with qualitifcation rather than position as result)
			if not isempty(iMainCompetition) then
				tbl.iSeason=iMainCompetition
				end
            -- Set that a relevant reoccuring sports event has been found and stop looking for one
			bFoundQualification=true
			end
		tbl.instanceof=read(claimsMainorQualifier[1],'id')
    end
	
	--Get the claim for the reoccuring sports event
    local claimsCompetition = mw.wikibase.getBestStatements(tbl.iSeason, 'P3450' )
    if (claimsCompetition[1]) then											-- Has a link to leauge (from the individual season) 
    	if (not (tbl.bMainOrQualifier=='Q')) then							-- If not a qualification tournament then a main tournament
    		tbl.bMainOrQualifier='M'
    		end
    	--Fill tbl with data about the reoccuring sports event (rather than the individual season)
    	tbl=competitionData_model(tbl,claimsCompetition)
		else
		--If no claim found try for a season that it might be part of instead (for example if there is a joint entity for all competitions independent of gender)
	    bFoundCompetition=false			--Start with setting that nothing has been found
	    local claimsCompetition2
	    local claimsCompetitionSeasonPartOf = mw.wikibase.getBestStatements(tbl.iSeason, 'P361' )
	    --If part of another reoccuring sports event
	    if (claimsCompetitionSeasonPartOf[1]) then
			iCompetitionSeasonPartOf=read(claimsCompetitionSeasonPartOf[1],'id')
		    claimsCompetition2 = mw.wikibase.getBestStatements(iCompetitionSeasonPartOf, 'P3450' )
		    bFoundCompetition=true
	    end
	    if (bFoundCompetition and claimsCompetition2[1]) then											-- Part of a reoccuring sports event (from the individual season) 
	    	tbl=competitionData_model(tbl,claimsCompetition2)
			else		
			--If neither instance of Q27020041 nor has a statement for P3450 -> assume not a sports competiton at all
			tbl.iCompetitionRank=9999999
			--If bMainOrQualifier still is its default value ('-'), it is not considered a sports results (might be a person participating in anything)
			if (tbl.bMainOrQualifier=='-') then
				tbl.iStatus=99					-- Return with a status that makes sure it is not considered a sports result
				tbl.txtLeague=''
				tbl.edit=''
				tbl.bHasRef=false
				return tbl
				else
				-- Sportsevent, but not part of a reoccuring sports event -> fill with information about the error (assuming all sports events are reoccuring and not one-off events at the moment)	
--				txtRefTmp=txtClickToAddCompetitionType .. ' [https://www.wikidata.org/wiki/' .. tbl.iSeason .. ' ' .. txtHere .. '].' .. txtaddWikiData(txtProperty3450,'tävlingstyp',txtExplanationProperty3450)
--				tbl.txtLeague=txtCompetitionTypeNotSet.. frame:extensionTag("ref", txtRefTmp) .. '[[' .. txtCategory .. ':' .. txtErrorCompetitionWithoutP3450 .. ']]'
--			    tbl.iCompetitionType="Q1"
--				tbl.iStatus=2
				end
			end
	    end
	bFounddates,tbl.txtDateFrom,tbl.txtDateTo=processDates_model(tbl.iSeason)
	if not bFounddates then
		--If no start year check for era	
		p2348=mw.wikibase.getAllStatements(tbl.iSeason, 'P2348' ) -- Om tidsperiod
		if (next(p2348)) then
			bFounddates,tbl.txtDateFrom,tbl.txtDateTo=processDates_model(read(p2348[1],'id') )
		end
	end
	if not bFounddates then
		p585=mw.wikibase.getAllStatements(tbl.iSeason,'P585' ) -- Om tidsperiod
		if (next(p585)) then
			tbl.txtDateFrom=read(p585[1],'time')
			tbl.txtDateTo=tbl.txtDateFrom
			else
			-- If neither startyear nor era, do not try do display more detailed information, just display information about the season of the league	
			if not tbl.iStatus==2 then
				-- If part of reoccuring sportsevent, but lack start/end info
				tbl.iStatus=3
				else
				-- If not part of reoccuring sportsevent set error message
				tbl.errors=tbl.errors .. txtDateMissing .. '[[' .. txtCategory .. ':' .. txtGeneralError .. ']]'
				end
			end
		end
    --Get name of the reoccuring sports event
    tbl.competitiontype=rm.tblWikilink(tbl.iLeague,tbl.txtDateTo,true)

	local iPositionDirect=getQualifier(valueParticipitation,'P1352')
	if iPositionDirect then
	    tbl.iPosition=iPositionDirect
	    end

	if (iPartner==nil) then
		iPartner=getQualifierId(valueParticipitation,'P1706')
		if (iPartner==nil) then
			iPartner=getQualifierId(valueParticipitation,'P1327')
			end
	end
	
	--If a main competition the position was not set directly in the statement using P1352
	if ((tbl.bMainOrQualifier=='M') and iPositionDirect==nil or (bIndividual and iPartner==nil)) then
		-- events
		claimParticipants=mw.wikibase.getAllStatements(tbl.iSeason,txtPropertyForParticipation)
--		tbl.txtPropertyForParticipation=txtPropertyForParticipation
		txtPos=''
		tbl.bHasRef=0
		--Loop through participants of the event until find the particpant for which this function was called
	    for keyParticipant, valueParticipant in pairs(claimParticipants ) do
	    	--Read participant id
			iParticipantLoop=read(valueParticipant,'id')
			--Check if it is the same one as for which the function was called
			if (iParticipantLoop==iThisParticipant) then
				txtPos=getQualifier(valueParticipant,'P1352')
				if (valueParticipant.qualifiers) then
--						--If individual look for any people that they played with (for example when people play in pairs)
					if (bIndividual and valueParticipant.qualifiers.P1327) then
					    iPartner=readQualifier(valueParticipant.qualifiers.P1327[1],'id')
					    else
					    	if (bIndividual and valueParticipant.qualifiers.P1706) then
						    iPartner=readQualifier(valueParticipant.qualifiers.P1706[1],'id')
						    end
					    end
					end
					tbl.bHasRef,tbl.ref=processAllRefsForWDItem_model(valueParticipant['references'],tbl.iSeason,txtPropertyForParticipation)
					tbl.qid=tbl.iSeason
					tbl.pid=txtPropertyForParticipation
				break
			    end
	    	end
    	if not (tbl.iPosition) then			-- If not already set directly
		    tbl.iPosition=txtPos
		    end
--		tmphasref,tmpref=tblProcessAllRefsForItemWD(frame,valueParticipitation['references'],entityid,'P1344')
		tmphasref=0
		tmpref={}
		if ((tbl.bHasRef==1) and (tmphasref==1)) then
			tbl.ref=mergeTables(tbl.ref,tmpref)
			end
		if ((tbl.bHasRef==1) and (tmphasref==0)) then
			--do nothing
			end
		if ((tbl.bHasRef==0) and (tmphasref==1)) then
			tbl.bHasRef=1
			tbl.ref=tmpref
			end
		if ((tbl.bHasRef==0) and (tmphasref==0)) then
			tbl.bHasRef=0
			tbl.ref=tmpref
			end
	    else
		tbl.bHasRef,tbl.ref=processAllRefsForWDItem_model(valueParticipitation['references'],qid,'P1344')
	end

    -- If qualifier	
	if (tbl.bMainOrQualifier=='Q') then
		tbl.iPosition=999998
		end
	if (iPartner) then
		local iCountry=readFirstStatementId(iPartner,'P1532')
		if not iCountry then
			iCountry=readFirstStatementId(iPartner,'P27')
			end
		if (arrayFlagCountries[iCountry]) then
			iCountry=arrayFlagCountries[iCountry]
			end
		tblTeam={id=iPartner,country=iCountry}
		tbl.team=tblTeam
		end
    if (bIndividual and iPartner) then		
	    tbl.txtTeam=txtTogetherWith .. txtWikilink(iPartner,'id')
		end
	tbl.iCompetitor=qid
	if (not tbl.iPosition) or (tbl.iPosition=='') then
		tbl.iPosition='999999'
		end
	tbl.iPosition=tonumber(tbl.iPosition)
	return tbl
end

function getResults (qid)
	local allresults={}
    local claimsCompetition = mw.wikibase.getBestStatements(qid, 'P1344' )
    for key, value in pairs( claimsCompetition ) do
		resultonecompetition=getOneCompetiton(qid,value)
		table.insert(allresults,resultonecompetition)
    end 
	return allresults
end

p.showdata = function(frame)
	p.frame=frame
	p=p.data()
	return tprint(p)
end


p.showpartininfobox=function(txtThisLabel,propertyname,bCheckChildren,runfunc)
	local obj=p[propertyname]
    if (obj) then
	    if(obj[1]) then	--Safety check to check if obj is an array rather than a structure (since Lua for some reason lacks any built-in functionality to test the difference test for 1 is a quick fix)
			obj=nil
			end    	
		end
	if (obj) then
		local txt=obj.data
		if (obj.unit) then
			txt=txt .. ' ' .. obj.unit
			end
		p.iCounter=p.iCounter+1
	    myArgs[localisation.txtLabel .. p.iCounter]=txtThisLabel
		myArgs[localisation.txtContent  .. p.iCounter]=txt  .. txtUnpackReference_model(p.frame,obj.ref,obj.qid,obj.pid)
		if (runfunc) then
			myArgs[localisation.txtContent  .. p.iCounter]=myArgs[txtContent  .. p.iCounter] .. runfunc()
			end
	end
	if (bCheckChildren) then
		for key,value in pairs(p.objectIds) do
			obj=p[value][propertyname]
			if (obj) then
				local txt=obj.data
				if (obj.unit) then
					txt=txt .. ' ' .. obj.unit
					end
				p.iCounter=p.iCounter+1
			    myArgs[localisation.txtLabel .. p.iCounter]='&nbsp;–&nbsp;' .. p[value]['label']
				myArgs[localisation.txtContent  .. p.iCounter]=txt  .. txtUnpackReference_model(p.frame,obj.ref,obj.qid,obj.pid)
				if (runfunc) then
					myArgs[txtContent  .. p.iCounter]=myArgs[localisation.txtContent  .. p.iCounter] .. runfunc()
					end
			end
		end
	end	
end

--[=====[ 
--]=====]

-- Function: Loopitems
-- Purpose: To fill an infobox row with values based on the content of ,general property, that will contain an array that is looped through
-- Arguments:
--    label					- Label for the infobox row
--    generalproperty       - Property to read the data from.
--    specificproperties	- Subproperties that are used to fill the content of the infobox row. 
--    separator		   		- Separator between the different items of the list (except the last one)
--    lastseparator			- Separator between the second last and the last item of the list

p.processoneiteminlist=function(item,func,specificproperties,separator,bHideReferences)
	local ret
	if (item.from or item.to) then
		txtYears=' (' .. datetotext (item.from,item.to) .. ')'
		else
		txtYears=''
		end
	if func then
		ret = func(item) 
		else
		if not (specificproperties==nil) then
			local txtCurrent=item[specificproperties[1]]
			for j=2,#specificproperties do
				if (item[specificproperties[j]]) then
					if not txtCurrent then
						txtCurrent=''
						end
					txtCurrent=txtCurrent .. separator .. item[specificproperties[j]]
					end
			end
			ret = txtCurrent
		else
			ret= item
		end
	end
	if (bHideReferences) then
		return ret .. txtYears
		else
		return ret .. txtYears .. txtUnpackReference_model(p.frame,item.ref,item.qid,item.pid)
		end
end

p.loopitems=function(label,generalproperty,func,specificproperties,separator,lastseparator,bHideReferences)
	local listDataitems=p[generalproperty]
	local iItems=1
	if listDataitems and next(listDataitems) then
		iItems=#listDataitems
		local txt
		if (iItems>0) then
			txt=''
			for i=1,iItems do
				if(i>1) then
					if (i==iItems) then
						txt=txt..lastseparator
						else				
						txt=txt..separator
						end
					end
				local txtCurrent=p.processoneiteminlist(listDataitems[i],func,specificproperties,", ",bHideReferences)
				if (i==1) then
					txt=txt..firstToUpper(txtCurrent)
					else
					txt=txt..txtCurrent
					end
				end
			else
				txt=p.processoneiteminlist(listDataitems,func,specificproperties,", ",bHideReferences)
			end
		p.iCounter=p.iCounter+1
		if type(label) == "string" then
	    	myArgs[localisation.txtLabel .. p.iCounter]=label
	    	else
    		if (iItems>1) then
		    	myArgs[localisation.txtLabel .. p.iCounter]=label[2]
		    	else
		    	myArgs[localisation.txtLabel .. p.iCounter]=label[1]
		    	end
    		end
		myArgs[localisation.txtContent  .. p.iCounter]=txt
	end
end

p.addHeader=function(label,style,descriptionlabel)
	p.iCounter=p.iCounter+1
    myArgs[localisation.txtHeader .. p.iCounter]=label
    if style then
		myArgs[localisation.txtHeader  .. p.iCounter .. localisation.txtStyle]=style
		end
	if descriptionlabel then
		p.iCounter=p.iCounter+1
	    myArgs[localisation.txtContent .. p.iCounter]='\'\''..descriptionlabel..'\'\''
		end
end

p.addContent=function(label,content)
	p.iCounter=p.iCounter+1
	myArgs[localisation.txtLabel .. p.iCounter]=label
    myArgs[localisation.txtContent .. p.iCounter]=content
end

standarddatesearch=function (a,b)
	if (not (a)) and (not (b)) then return false end
	if not (a) then return true end
	if not (b) then return false end

	a.isempty=(not a.from) and (not a.to)
	b.isempty=(not b.from) and (not b.to)
	
	if (a.isempty and b.isempty) then return false end
	if (a.isempty) then return true end
	if (b.isempty) then return false end

	if (a.to) then
		a.last=a.to.raw
		if (a.from) then
			a.first=a.from.raw
			else
			a.first=a.last
			end
		else
		a.last=a.from.raw
		a.first=a.last
		end

	if (b.to) then
		b.last=b.to.raw
		if (b.from) then
			b.first=b.from.raw
			else
			b.first=b.last
			end
		else
		b.last=b.from.raw
		b.first=b.last
		end

	if (a.last==b.last) then 
		if (a.first==b.first) then 
			return false
			else
			return a.first < b.first
			end
		end
	return a.last < b.last
end

p.showtemporalpartininfobox=function(txtMainHeaderLabel,txtMainHeaderDescriptionLabel,txtSubHeaderLabel,obj,objfunction,bCheckChildren,tblExtraColumns)
	if (#obj>0) then
	    table.sort(obj, standarddatesearch)										-- Sort based on date
	    p.addHeader(txtMainHeaderLabel,nil,txtMainHeaderDescriptionLabel)		-- Add header
		p.iCounter=p.iCounter+1													-- Start table that will show the data
		local txt='<table style="class: biography vcard" width="100%"><tr><th width="70px">' .. localisation.txtSeasonHeader .. '</th><th>' ..txtSubHeaderLabel .. '</th>'
		
		-- If extra columns create headers for them
		if tblExtraColumns then
			for j=1,#tblExtraColumns do
				txt=txt..'<th style="width:' .. tblExtraColumns[j].width .. '">' .. tblExtraColumns[j].title .. '</td>'					
			end
		end
		txt=txt..'</tr>'
	    for i=1,#obj do
	    	txtRow='<tr><td style="text-align:left">' .. datetotext (obj[i].from,obj[i].to) .. '</td>'
	    	local txtData
	    	if (objfunction==nil) then
		    	txtData= obj[i].data
		    	else
		    	txtData= objfunction(p.frame,obj[i])
	    		end
	    	txtRow=txtRow..'<td style="text-align:left">' .. txtData .. txtUnpackReference_model(p.frame,obj[i].ref,obj[i].qid,obj[i].pid) .. '</td>'
			if (tblExtraColumns) then
				for j=1,#tblExtraColumns do
					txtRow=txtRow..'<td>'.. tblExtraColumns[j].func(obj[i]) ..'</td>'					
				end
			end
			txt=txt..txtRow .. '</tr>'
		    end
	    myArgs[txtContent .. p.iCounter]=txt .. '</table>'
	end
end

p.showovertime=function(txtTitleHeaderLabel,obj,objfunction,bCheckChildren,tblExtraColumns)
	if (#obj>0) then
	    table.sort(obj, standarddatesearch)										-- Sort based on date
		p.iCounter=p.iCounter+1													-- Start table that will show the data
		local txt='<table style="class: biography vcard" width="100%"><tr><th width="70px">' .. localisation.txtSeasonHeader .. '</th><th>' ..txtTitleHeaderLabel .. '</th>'
		
		-- If extra columns create headers for them
		if tblExtraColumns then
			for j=1,#tblExtraColumns do
				txt=txt..'<th style="width:' .. tblExtraColumns[j].width .. '">' .. tblExtraColumns[j].title .. '</td>'					
			end
		end
		txt=txt..'</tr>'
	    for i=1,#obj do
	    	txtRow='<tr><td style="text-align:left">' .. datetotext (obj[i].from,obj[i].to) .. '</td>'
	    	local txtData
	    	if (objfunction==nil) then
		    	txtData= obj[i].data
		    	else
		    	txtData= objfunction(p.frame,obj[i])
	    		end
	    	txtRow=txtRow..'<td style="text-align:left">' .. txtData .. txtUnpackReference_model(p.frame,obj[i].ref,obj[i].qid,obj[i].pid) .. '</td>'
			if (tblExtraColumns) then
				for j=1,#tblExtraColumns do
					txtRow=txtRow..'<td>'.. tblExtraColumns[j].func(obj[i]) ..'</td>'					
				end
			end
			txt=txt..txtRow .. '</tr>'
		    end
	    myArgs[txtContent .. p.iCounter]=txt .. '</table>'
	end
end


--[=====[ 
General format for the Sportdata (LUA) tables:
	p.frame				Current frame
	p.objectMainId		Qid for the article (in the usual case, sometimes it can be useful to use another id, for example for testing purposes or if another wikidata object might be a better fit)
	p.qids				Qids for other wikidata objects that should be counted together with the objectMainId. It might for example be a predecessor, whose data will be included in the resulting infobox.
	p[qids]				The sports data functions will fill a property with the same name as the qid with information for that qid. Usually it will be filled in the same way as the objectMainId fill information directly under p. Though some properties might be objectMainId-only (for example label). 
--]=====]



-- Function: Filter advanced
-- Purpose: To filter a table based on multiple properties (rather than one, which is the case for the 'filter'-function. For every property it only takes one selected value [that it matches with]) 
-- Arguments:
--    tblIn         - A (LUA) table containing structured information that will be used by any of the sports data modules.
--    txtProperty	- Property that will be filtered (will typically have a quite generic name and contain an array of data [that is the information that will be filtered])
--    arrayVariable   - Array of variables that the filter is based upon
--    arraySelectedvalue - Value to base the filter upon (items for which the value of txtVariable is selectedvalue will be chosen, i.e. the array has the same length as arrayVariable, with one match for each variable)

p.filter_advanced=function(tblIn,txtProperty,arrayVariables,arraySelectedvalue)
	local tblProperty=tblIn[txtProperty]							-- Get chosen property
	local iItems=#tblProperty										-- Count number of items
	local tblOut={}													-- Empty table that will be filled by the results of the filter query
	local iQueryVariables=#arrayVariables
	tblOut.variables=iQueryVariables
	for i=1,iItems do												-- Loop through all items
		local match=true		
		for j=1,iQueryVariables do
			local databasevalue=tblProperty[i][arrayVariables[j]]
			if (type(databasevalue)=="table") then
				match=false
				for k=1,#databasevalue do
					if (databasevalue[k]==arraySelectedvalue[j]) then match=true end	-- If the data value for the chosen variable of this item is the same as the selected value (for the filter) ...
				end
	--			tblProperty[i].data=#databasevalue .. tprint(databasevalue) .. arraySelectedvalue[j] .. tostring(match)
--				match=true
				else
				if not (databasevalue==arraySelectedvalue[j]) then match=false end		-- If the data value for the chosen variable of this item is the same as the selected value (for the filter) ...
				end
			end
		if (match) then
			table.insert(tblOut,tblProperty[i])						-- into the out table
		end
	end
	
	if (tblIn.objectIds) then										-- If any child objects
		for key,value in pairs(tblIn.objectIds) do					-- Loop through them	
			local obj=p[value]										-- Set obj to the current child object (in the loop)
			filteredobj=p.filter_advanced(obj,txtProperty,arrayVariables,arraySelectedvalue)		-- Call the filter function (recursivelly) based on the child object
			iItemsSub=#filteredobj									-- Read the (LUA) tables returned by the filter 
			for j=1,iItemsSub do									-- Loop through the returned table items 
					table.insert(tblOut,filteredobj[j])				-- Add them to the out table
				end
			end
		end
	return tblOut													-- Return the out table
end

-- Function: Filter
-- Purpose: To filter a table based on property, variable and selected value to return a table of the same format as the one supplied 
-- Arguments:
--    tblIn         - A (LUA) table containing structured information that will be used by any of the sports data modules.
--    txtProperty	- Property that will be filtered (will typically have a quite generic name and contain an array of data [that is the information that will be filtered])
--    arrayVariable   - Array of variables that the filter is based upon
--    arraySelectedvalue - Value to base the filter upon (items for which the value of txtVariable is selectedvalue will be chosen, i.e. the array has the same length as arrayVariable, with one match for each variable)

p.filter=function(tblIn,txtProperty,txtVariable,selectedvalue)
	local tblProperty=tblIn[txtProperty]							-- Get chosen property
	local iItems=#tblProperty										-- Count number of items
	local tblOut={}													-- Empty table that will be filled by the results of the filter query
	for i=1,iItems do												-- Loop through all items
		if (tblProperty[i][txtVariable]==selectedvalue) then		-- If the data value for the chosen variable of this item is the same as the selected value (for the filter) ...
			table.insert(tblOut,tblProperty[i])						-- into the out table
		end
	end
	
	if (tblIn.objectIds) then										-- If any child objects
		for key,value in pairs(tblIn.objectIds) do					-- Loop through them	
			local obj=p[value]										-- Set obj to the current child object (in the loop)
			filteredobj=p.filter(obj,txtProperty,txtVariable,selectedvalue)		-- Call the filter function (recursivelly) based on the child object
			iItemsSub=#filteredobj									-- Read the (LUA) tables returned by the filter 
			for j=1,iItemsSub do									-- Loop through the returned table items 
					table.insert(tblOut,filteredobj[j])				-- Add them to the out table
				end
			end
		end
	return tblOut													-- Return the out table
end

-- The function produce a summary of top 3 positions for the person/club, followed by a more detailed description of their competition results

p.resulttable = function(tblAllQueryResults,headerstyle)
	-- Create empty tables to be filled
	local tblCompetitionTypes={}		-- Will host all types of competition (World Championshipcs, Olympic Games, continental cup etc.) that the person/club has participated in
	local tblAllPositions={}			-- All positions (organised by competition type and position). It is a table of tables as every post in the table will be a table with the results of the person/club in that competition
	local tblPodium={}					-- All podium positions (organised by competition type)
	local maxpositions={}				-- "highest" position (as in the numerical value such as 32, not the best position)
	table.sort(tblAllQueryResults, function(a, b) return a.txtDateFrom < b.txtDateFrom end) -- Sort in calendar order (so that, when relevant, the results are presented in the right order)
	local iCompetitionTypes=0			-- Count of number of competition types
	local bPodiumHasContent=false		-- Check if the person/club has any podium positions at all (if none, the function will not return a podium table)
	local bAllResultsHasContent=false	-- Check if the person/club has any results at all
	for i=1,#tblAllQueryResults do			-- Loop through all results
		
if tblAllQueryResults[i].iPosition == nil or tblAllQueryResults[i].txtYears  then  -- temporary fix to avoid skript errors by skipping competitions where there is no valid value for iPosition or txtYears
	mw.log('ERROR: iPosition or txtYears is missing')
	mw.logObject(tblAllQueryResults[i])
else
		-- Read league/competition type
		local iCompetitionType=tblAllQueryResults[i].iLeague
		-- If no result has been processed for that competition type
		if not (tblAllPositions[iCompetitionType]) then
			-- Create with relevant information about the competition type
			local tblCompetitionType={id=iCompetitionType,title=tblAllQueryResults[i].txtLeague,icon='',rank=tblAllQueryResults[i].iCompetitionRank}
			-- For coaching result write out the gender (not done for player results as those will usually all be for the same gender)
			if (tblAllQueryResults[i].bCoached) then
				tblCompetitionType.title=tblCompetitionType.title
				tblCompetitionType.icon=txtGenderIcon(tblAllQueryResults[i].iGender)
--				tblCompetitionType.title_plain=tblCompetitionType.title_plain
				end
			table.insert(tblCompetitionTypes,tblCompetitionType)	-- Add the post of the competition type to the table over competition types
			tblAllPositions[iCompetitionType]={}	-- Create an empty position table for the competition 
			tblPodium[iCompetitionType]={0,0,0}		-- Create an empty podium
			iCompetitionTypes=iCompetitionTypes+1	-- Increase the number of competition types
			maxpositions[iCompetitionType]=0		-- Start highest position with zero (as no competitions added yet)
			end
		iPosition=tblAllQueryResults[i].iPosition		-- Read position
		if (iPosition<4) then						-- If top three position - add to podium
			tblPodium[iCompetitionType][iPosition]=tblPodium[iCompetitionType][iPosition]+1	-- Add to podium
			bPodiumHasContent=true					-- Has a podium position is now true
			end

		--Adding result to the tblCompetitionTypes table
		local tblCompetition=tblAllPositions[iCompetitionType]	-- Pick out the individual competition type (in order to add the result to it)
		if not (tblCompetition[iPosition]) then				-- If it doesn't already have any results where the club/person has ended up at position iPosition then..
			tblCompetition[iPosition]={text='',pos=iPosition} -- Create an empty table post
			if (iPosition>maxpositions[iCompetitionType]) then	-- If position is higher than current max positions..
				maxpositions[iCompetitionType]=iPosition		-- .. set it as new max position
				end
			end
		if not (tblCompetition[iPosition].text=='') then	-- If it the position text (containing the times the club/person has ended up at that position) is not empty
			tblCompetition[iPosition].text=tblCompetition[iPosition].text..', '		-- Add an ", " before adding anything else (i.e. between all elements, not before the first element)
			end

		-- If there is a linked wikipedia page, try to extract the years from its title based on regular expression (that way they years shown will match the title, rather than be based on the actual dates of the competition). This is prefereable for example for the 2020 Olympics (as they took place in 2021, due to the Covid 19 pandemic).
		if(tblAllQueryResults[i].sitelink) then
			-- Find all four-digit sequences
			local iterator = string.gmatch(tblAllQueryResults[i].sitelink, "%d%d%d%d")
			local iYear1 = iterator()  -- First match
			local iYear2 = iterator() -- Second match
			local txtEventtitle
			if (iYear1) then
				txtEventtitle=iYear1
				if (iYear2) then
					txtEventtitle=txtEventtitle .. '/' .. iYear2
				end
				else
				iYear1=string.sub(tblAllQueryResults[i].txtDateFrom,2,5)
				iYear2=string.sub(tblAllQueryResults[i].txtDateTo,2,5)
				if (iYear1==iYear2) then
					txtEventtitle=iYear1
					else
					txtEventtitle=iYear1 .. '/' .. iYear2 
					end					
				end
			tblCompetition[iPosition].text=tblCompetition[iPosition].text.. '[[' .. tblAllQueryResults[i].sitelink .. '|' .. txtEventtitle .. ']]' .. txtUnpackReference_model(p.frame,tblAllQueryResults[i].ref,tblAllQueryResults[i].qid,tblAllQueryResults[i].pid)
			tblAllQueryResults[i].txtYears='[[' .. tblAllQueryResults[i].sitelink .. '|' .. txtEventtitle .. ']]'
			else
			iYear1=string.sub(tblAllQueryResults[i].txtDateFrom,2,5)
			iYear2=string.sub(tblAllQueryResults[i].txtDateTo,2,5)
			if (iYear1==iYear2) then
				txtEventtitle=iYear1
				else
				txtEventtitle=iYear1 .. '/' .. iYear2 
				end					
			tblCompetition[iPosition].text=tblCompetition[iPosition].text.. txtEventtitle .. txtUnpackReference_model(p.frame,tblAllQueryResults[i].ref,tblAllQueryResults[i].qid,tblAllQueryResults[i].pid)
			tblAllQueryResults[i].txtYears=txtEventtitle
			end
		tblAllPositions[iCompetitionType]=tblCompetition
		bAllResultsHasContent=true
		
end -- temporary fix to avoid skript errors by skipping competitions where there is no valid value for iPosition or txtYears

		end
	table.sort(tblCompetitionTypes, function(a, b) return a.rank < b.rank end)	-- Sort competitions based on their rank (as entered in module 'sportdata rankings') so that for exampel world championships are shown above national championships
	if not (bAllResultsHasContent) then 			-- Only show header if there are any results
		p.addContent("''" .. localisation.txtNoResult .. "''" .. "[[" .. localisation.txtCategory .. ":" .. localisation.txtNoResultsAtAll .. "]]" )
		return									-- If no content there is no need to do anything else
		end
	local txtPodium
	if (bPodiumHasContent) then					-- Only show podium if there are any podium positions
		txtPodium='<table style="width:100%"><tr><td style="text-align:left">Tävling</td><td style="background:gold;width:50px">'..txtGoldHeader..'</td><td style="background:silver;width:50px">' .. txtSilverHeader .. '</td><td style="background:#CC9966;width:50px">'..txtBronzeHeader .. '</td>'	
		for i=1,iCompetitionTypes do
			iTotalMedals=tblPodium[tblCompetitionTypes[i].id][1]+tblPodium[tblCompetitionTypes[i].id][2]+tblPodium[tblCompetitionTypes[i].id][3]
			if (iTotalMedals>0) then  -- Only show competition in medal list if the person/team has won a medal
				txtRow='<tr><td style="text-align:left">'..tblCompetitionTypes[i].title..tblCompetitionTypes[i].icon..'</td>'
				for j=1,3 do
					local iPos=tblPodium[tblCompetitionTypes[i].id][j]
					if (iPos==0) then
						iPos='-'
						end
					txtRow=txtRow..'<td>'..iPos..'</td>'
					end
				txtPodium=txtPodium..txtRow..'</tr>'
				end
			end
		txtPodium=txtPodium..'</table>'
		else
		txtPodium=''
		end

		local txtAll2
		if (bPodiumHasContent) then					-- Show all results in collapsed view if had podium positions, otherwised open
			txtAll2='<table class="mw-collapsible mw-collapsed biography vcard" style="width:100%;margin: 2px 0;border: 1px solid #000;"><tr><th colspan="3" style="background:#ececec">'..localisation.txtCompleteResultsList..'</th></tr><tr><td style="width:50px;">'..localisation.txtPositionHeader..'</td><td>'..localisation.txtCompetitionHeader..'</td><td>'..localisation.txtSeasonHeader..'</td></tr>' 
			else
			txtAll2='<table class="mw-collapsible biography vcard" style="width:100%;margin: 2px 0;border: 1px solid #000;"><tr><th colspan="3" style="background:#ececec">'..localisation.txtCompleteResultsList..'</th></tr><tr><td style="width:50px;">'..localisation.txtPositionHeader..'</td><td>'..localisation.txtCompetitionHeader..'</td><td>'..localisation.txtSeasonHeader..'</td></tr>' 
			end
		
		table.sort(tblAllQueryResults, function(a, b) 
			return a.txtDateTo < b.txtDateTo
		end)

	iOldTeam=0;
	for i=1,#tblAllQueryResults do
		
if tblAllQueryResults[i].iPosition == nil or tblAllQueryResults[i].txtYears == nil then -- temporary fix to avoid skript errors by skipping competitions where there is no valid value for iPosition or txtYears
	mw.log('ERROR: iPosition or txtYears is missing')
	mw.logObject(tblAllQueryResults[i])
else
		if (tblAllQueryResults[i].team) then
			if not (tblAllQueryResults[i].team.id==iOldTeam) then
				local txtTeamname
				if (tblAllQueryResults[i].bIsClub) then
					if (tblAllQueryResults[i].team and tblAllQueryResults[i].team.country) then
						txtTeamname= p.frame:expandTemplate{ title = localisation.txtFlagIconTemplate , args={getLabelByEntity(tblAllQueryResults[i].team.country)}} .. ' ' .. txtWikilink(tblAllQueryResults[i].team.id)
						else
						txtTeamname= txtWikilink(tblAllQueryResults[i].team.id)
						end
					end
				if (tblAllQueryResults[i].bIsNationalTeam) then
					local tblTeaminfo=tblNationalTeamInfo(tblAllQueryResults[i].team.id)
					local tbl={country=tblTeaminfo}
					txtTeamname=showcountry(p.frame,tbl)
					end
				if (tblAllQueryResults[i].bCoached) then		-- Only show ,gender when coaching
					txtTeamname=txtTeamname .. txtGenderIcon(tblAllQueryResults[i].iGender)
					end
				if (not (tblAllQueryResults[i].bIsClub or tblAllQueryResults[i].bIsNationalTeam)) then
					local iCountry=tblAllQueryResults[i].team.country
					local txtFlag
					if iCountry then
						txtFlag=p.frame:expandTemplate{ title = localisation.txtTemplate .. ':' .. localisation.txtFlagIconTemplate , args={getLabelByEntity(iCountry)}}
						else
						txtFlag=''
						end
					txtTeamname=txtTogetherWith .. txtFlag .. ' ' .. txtWikilink(tblAllQueryResults[i].team.id)
					end
				txtAll2 = txtAll2.. '<tr style="background:#f0ecb0;border:1"><td colspan="3">' .. txtTeamname .. '</td></tr>'
				iOldTeam=tblAllQueryResults[i].team.id
				end
			end
		local txtPos=txtPosition(tblAllQueryResults[i].iPosition)
		local txtLeague
		if (tblAllQueryResults[i].competitiontype) then
			txtLeague=txtShowLink(tblAllQueryResults[i].competitiontype)
			if not (tblAllQueryResults[i].competitiontype.txtLabel==tblAllQueryResults[i].competitiontype.txtLabel_current) then
				localrefitem={}
				localrefitem['error']=false
				localrefitem[localisation.txtTitle]=localisation.txtCurrently .. ' ' .. tblAllQueryResults[i].competitiontype.txtLabel_current
				local reftbl={localrefitem}
				txtLeague=txtLeague .. 	txtUnpackReference_model(p.frame,reftbl)
			end
			else
				txtLeague=''
			end
		if (settings.tblSportWithManyDisciplines[tblAllQueryResults[i].iSport] and tblAllQueryResults[i].competitionClassThisEvent) then
			txtLeague=txtLeague .. ' (' .. txtShowLink(tblAllQueryResults[i].competitionClassThisEvent) .. ')'
			end
		txtLeague = txtLeague.. txtUnpackReference_model(p.frame,tblAllQueryResults[i].ref,tblAllQueryResults[i].qid,tblAllQueryResults[i].pid)
		local txtYears=tblAllQueryResults[i].txtYears
		txtAll2 = txtAll2.. '<tr>'.. txtPos .. '<td style="text-align:left">' .. txtLeague .. '</td><td>' .. txtYears ..'</td></tr>'

end -- temporary fix to avoid script errors when there is no valid value for txtYears

	end
	txtAll2=txtAll2 .. '</table>'

	p.iCounter=p.iCounter+1
	myArgs[localisation.txtContent .. p.iCounter]=txtPodium .. txtAll2
end

function datetotext (from,to)
local txt =''
if not isempty(from) then
	txt=from.year
end
if not (isempty(from) and isempty(to))  then
	txt=txt..'–'
	end
if not isempty(to) then
	txt=txt..to.year
end
if from and to and (from.year==to.year) then
	txt=from.year
	end
return txt
end

-- Loop through a table and return unique elements
function listalluniques(tblItems,txtProperty)
local uniqueSet={}
local uniqueSubset={}
for i=1,#tblItems do
	value=tblItems[i][txtProperty]
   if value then
	   if not uniqueSet[value] then
	        uniqueSet[value] = true
	        table.insert(uniqueSubset, value)
		    end
	   end
	end
	return uniqueSubset
end

function txtGenderIcon(iGender)
	if (iGender==1) then	-- Is it men's sport?
		return '[[' .. localisation.txtImage .. ':Male symbol (heavy blue).svg]]'
		end
	if (iGender==2) then	-- Is it women's sport?
		return '[[' .. localisation.txtImage .. ':Venus symbol (heavy pink).svg]]'
		end
	return ''				-- Return empty string if neither is set
end


--Kopia av p.filter, men utan filtrerandet
p.all=function(tblIn,txtProperty)
	local tblProperty=tblIn[txtProperty]							-- Get chosen property
	local iItems=#tblProperty										-- Count number of items
	local tblOut={}													-- Empty table that will be filled by the results of the filter query
	for i=1,iItems do												-- Loop through all items
		table.insert(tblOut,tblProperty[i])						-- into the out table
	end
	
	if (tblIn.objectIds) then										-- If any child objects
		for key,value in pairs(tblIn.objectIds) do					-- Loop through them	
			local obj=p[value]										-- Set obj to the current child object (in the loop)
			daughterobj=p.all(obj,txtProperty)						-- Call the function (recursivelly) based on the child object
			iItemsSub=#daughterobj									-- Read the (LUA) tables returned by the filter 
			for j=1,iItemsSub do									-- Loop through the returned table items 
					table.insert(tblOut,daughterobj[j])				-- Add them to the out table
				end
			end
		end
	return tblOut													-- Return the out table
end


p.tblInfobox_canvas=function()
	local myArgs={}
	myArgs[localisation.txtTitle]=p.label
    myArgs[localisation.txtHeaderStyle] = settings.txtStyleMain
    myArgs[localisation.txtBodyStyle]="width:"..settings.iWidth_px .. "px"
    myArgs[localisation.txtLabelStyle]="width:" .. settings.iLabelWidth_pc .. "%"
    myArgs[localisation.txtDataStyle]="width:" .. settings.iDataWidth_pc .. "%"
	return myArgs
	end

---------------------------------------------------- GENERIC SUPPORT FUNCTIONS -----------------------------------------------------------------------------------


--Remove?--
function processDates(entityid,txtSeparator)
	txtDateFrom=''
	txtDateTo=''
	local p580=mw.wikibase.getAllStatements(entityid, 'P580' )
	local hasstart=next(p580)
	p582=mw.wikibase.getAllStatements(entityid, 'P582' )
	local hasend=next(p582)
	if hasstart then
		txtDateFrom=read(p580[1],'time')
		txtYearFrom=string.sub(txtDateFrom,2,5)
		end
	if hasend then
		txtDateTo=read(p582[1],'time')
		txtYearTo=string.sub(txtDateTo,2,5)
		end
	--For sorting purposes set date from and to date to the same if one of them is lacking
	if hasend and not hasstart then
		txtDateFrom=txtDateTo
		txtYearFrom=txtYearTo
		end
	if hasstart and not hasend then
		txtDateTo=txtDateFrom
		txtYearTo=txtYearFrom
		end
	hasanydate=hasstart or hasend
	if (hasanydate) then
		txtSeason=txtYears(txtYearFrom,txtYearTo,txtSeparator)
		end
	return hasanydate,txtDateFrom,txtDateTo,txtYearFrom,txtSeason
end

sd.competitionarea=function(qidCompetition)
	iArea=useStatement(qidCompetition, 'P131' ) -- located in the administrative territorial entity
	if not (iArea) then
		iArea=useStatement(qidCompetition, 'P17' ) -- country
		end
	if not (iArea) then
		iArea=useStatement(qidCompetition, 'P30' ) -- continent
		end
	if not (iArea) then
		iArea=useStatement(qidCompetition, 'P276' ) -- place
	end
	return iArea
end

return sd
--[=====[ 
--]=====]