<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASandbox%2FAidan9382%2FDiscussionOverview</id>
	<title>Module:Sandbox/Aidan9382/DiscussionOverview - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3ASandbox%2FAidan9382%2FDiscussionOverview"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sandbox/Aidan9382/DiscussionOverview&amp;action=history"/>
	<updated>2026-04-21T21:15:54Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Sandbox/Aidan9382/DiscussionOverview&amp;diff=145083&amp;oldid=prev</id>
		<title>imported&gt;Aidan9382: woosp</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Sandbox/Aidan9382/DiscussionOverview&amp;diff=145083&amp;oldid=prev"/>
		<updated>2022-11-11T07:35:40Z</updated>

		<summary type="html">&lt;p&gt;woosp&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--[=[&lt;br /&gt;
A module designed to provide an overall summary and some statistics on a discussion board&lt;br /&gt;
Inspired by, and partially borrowed from, [[Module:Sandbox/Smalljim/DiscussionIndexTest]]&lt;br /&gt;
--]=]&lt;br /&gt;
local Transcluder = require(&amp;quot;Module:Transcluder&amp;quot;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
table.find = function(t,o) --Used to luau, so assumed this existed. Heres a quick version&lt;br /&gt;
	for a,b in next,t do&lt;br /&gt;
		if b == o then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local lang = mw.getContentLanguage()&lt;br /&gt;
local function getTime(timestamp)&lt;br /&gt;
	return tonumber(lang:formatDate(&amp;quot;U&amp;quot;,timestamp))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function concatUsers(users)&lt;br /&gt;
	local final = &amp;quot;&amp;quot;&lt;br /&gt;
	for _,user in next,users do&lt;br /&gt;
		if string.match(user,&amp;quot;^%d+%.%d+%.%d+%.%d+$&amp;quot;) or string.match(user,&amp;quot;^%x+:[%x:]+$&amp;quot;) then --Lazy but mostly working basic IPv6 regex&lt;br /&gt;
			final = final .. &amp;quot;[[Special:Contributions/&amp;quot;..user..&amp;quot;|&amp;quot;..user..&amp;quot;]], &amp;quot;&lt;br /&gt;
		else&lt;br /&gt;
			final = final .. &amp;quot;[[:User:&amp;quot;..user..&amp;quot;|&amp;quot;..user..&amp;quot;]], &amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return string.sub(final,1,-3)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Specialised version of Sandbox/Smalljim/ParsePageTest.formatDateDiff that just does hours (better for sorting cause im not learning wikitables)&lt;br /&gt;
local function formatDateDiff( date_diff )&lt;br /&gt;
    return tonumber( math.floor(date_diff/(6*60))/10 ) .. &amp;#039; hours&amp;#039;;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Specialised version of Transcluder.getSections, using a similar design&lt;br /&gt;
local function getSectionData(text)&lt;br /&gt;
	local sections = {}&lt;br /&gt;
	text = &amp;quot;\n&amp;quot;..text..&amp;quot;\n== &amp;quot;&lt;br /&gt;
	while true do&lt;br /&gt;
		local section,content = string.match(text,&amp;quot;\n==%s*([^=]-)%s*==\n(.-)\n==[^=]&amp;quot;)&lt;br /&gt;
		if not section then&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
		text = string.sub(text,string.find(text,content,1,true)+#content,-1)&lt;br /&gt;
		sections[#sections+1] = {name=section,content=content}&lt;br /&gt;
	end&lt;br /&gt;
	return sections&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--This is a bloody mess of a mix of ideas, but it mostly works, so im dealing with it&lt;br /&gt;
local function getUserMentions(text)&lt;br /&gt;
	--Returns a list of users, and if they were considered a &amp;quot;participant&amp;quot; or someone who was just mentioned&lt;br /&gt;
	local mentions = {}&lt;br /&gt;
	--Timestamp is %d%d:%d%d, %d%d? %w+ %d%d%d%d %(UTC%)) but we allow some (minor) leniancy for those who just slightly edit their dates so that it still picks up&lt;br /&gt;
	local timestampRegex = &amp;quot;((%d%d:%d%d, %d%d? %w+,? %d%d%d%d) %(UTC%))&amp;quot;&lt;br /&gt;
	local userRegex = &amp;quot;(%[%[:?User:([^|%]]+))&amp;quot;&lt;br /&gt;
	local userTalkRegex = &amp;quot;(%[%[:?User:([^|%]]+))&amp;quot;&lt;br /&gt;
	local userContribRegex = &amp;quot;(%[%[:?Special:Contributions/([^|%]]+))&amp;quot;&lt;br /&gt;
	for line in string.gmatch(text,&amp;quot;[^\n]+&amp;quot;) do&lt;br /&gt;
		--Split by line and check all content on said line. This assumes all signatures never use newlines, which they should not be doing anyways.&lt;br /&gt;
		--Bar of entry for being labelled a &amp;quot;participant&amp;quot; is a valid timestamp along with their user/usertalk/contribs&lt;br /&gt;
		--Users can be noted as being both a participant and a mention during the data, so be smart in using this data&lt;br /&gt;
		local usersOnThisLine = {}&lt;br /&gt;
		for _,reg in next,{userRegex,userTalkRegex,userContribRegex} do&lt;br /&gt;
			local index = 1&lt;br /&gt;
			while true do&lt;br /&gt;
				local targetText = string.sub(line,index,-1)&lt;br /&gt;
				local wholeText,identifier = string.match(targetText,reg)&lt;br /&gt;
				if not wholeText then&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
				if not string.find(identifier,&amp;quot;/&amp;quot;) then --Avoid subpage nonsense&lt;br /&gt;
					mw.log(&amp;quot;Found user on reg&amp;quot;,reg,&amp;quot;name is&amp;quot;,identifier)&lt;br /&gt;
					usersOnThisLine[string.find(targetText,reg)] = identifier&lt;br /&gt;
				end&lt;br /&gt;
				index = index + string.find(targetText,reg) + #wholeText&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		--Start associating timestamps with users&lt;br /&gt;
		local index = 1&lt;br /&gt;
		local pindex = {} --Lazy coding&lt;br /&gt;
		local participants = {}&lt;br /&gt;
		while true do&lt;br /&gt;
			local targetText = string.sub(line,index,-1)&lt;br /&gt;
			local wholeText,identifier = string.match(targetText,timestampRegex)&lt;br /&gt;
			if not wholeText then&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
			--Backtrack through the text for a mention&lt;br /&gt;
			local timestampLocation = string.find(targetText,identifier)&lt;br /&gt;
			local user,where&lt;br /&gt;
			for i = timestampLocation,1,-1 do&lt;br /&gt;
				user,where = usersOnThisLine[i],i&lt;br /&gt;
				if user then&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if user then&lt;br /&gt;
				participants[#participants+1] = {user=user,when=identifier,participated=true}&lt;br /&gt;
				pindex[user] = true&lt;br /&gt;
			--else: be confused as hell&lt;br /&gt;
			end&lt;br /&gt;
			index = index + timestampLocation + #wholeText&lt;br /&gt;
		end&lt;br /&gt;
		local pings = {}&lt;br /&gt;
		for _,user in next,usersOnThisLine do&lt;br /&gt;
			if not pings[user] and not pindex[user] then --If they participated on a line, just ignore all pings&lt;br /&gt;
				pings[user] = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		--Integrate the new data&lt;br /&gt;
		for user,_ in next,pings do&lt;br /&gt;
			mentions[#mentions+1] = {user=user,participated=false}&lt;br /&gt;
		end&lt;br /&gt;
		for _,userData in next,participants do&lt;br /&gt;
			mentions[#mentions+1] = userData&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return mentions&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local page = frame.args[1] or frame.args.page&lt;br /&gt;
	assert(type(page)==&amp;quot;string&amp;quot;,&amp;quot;Invalid or no page provided&amp;quot;)&lt;br /&gt;
	&lt;br /&gt;
	local success,text = pcall(Transcluder.get,page)&lt;br /&gt;
	assert(success,text)&lt;br /&gt;
	&lt;br /&gt;
	local sections = getSectionData(text)&lt;br /&gt;
	local tableContent = &amp;#039;{| class=&amp;quot;wikitable sortable&amp;quot;\n! Section !! Initiator !! Last Comment !! Size !! Participants !! Mentions&amp;#039;&lt;br /&gt;
	for _,section in next,sections do&lt;br /&gt;
		local sanitisedName = string.gsub(string.gsub(section.name,&amp;quot;%[%[:?[^|]-|([^%]]-)]]&amp;quot;,&amp;quot;%1&amp;quot;),&amp;quot;%[%[:?([^%]]-)]]&amp;quot;,&amp;quot;%1&amp;quot;)&lt;br /&gt;
		local wikilinkAnchor = &amp;quot;[[:&amp;quot;..page..&amp;quot;#&amp;quot;..sanitisedName..&amp;quot;|&amp;quot;..sanitisedName..&amp;quot;]]&amp;quot;&lt;br /&gt;
		local membersInText = getUserMentions(section.content)&lt;br /&gt;
		local uniqueParticipants = {}&lt;br /&gt;
		for _,userData in next,membersInText do&lt;br /&gt;
			if userData.participated and not table.find(uniqueParticipants,userData.user) then&lt;br /&gt;
				uniqueParticipants[#uniqueParticipants+1] = userData.user&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local now = getTime()&lt;br /&gt;
		local orderedComments = {}&lt;br /&gt;
		for _,userData in next,membersInText do&lt;br /&gt;
			if userData.participated then&lt;br /&gt;
				local when = getTime(userData.when)&lt;br /&gt;
				if now &amp;gt; when then --Ensure comment is from the past&lt;br /&gt;
					if #orderedComments == 0 then&lt;br /&gt;
						orderedComments[#orderedComments+1] = {user=userData.user,when=when}&lt;br /&gt;
					else&lt;br /&gt;
						for i = 1,#orderedComments do&lt;br /&gt;
							local comment = orderedComments[i]&lt;br /&gt;
							if when &amp;lt; comment.when then&lt;br /&gt;
								for i2 = #orderedComments+1,i,-1 do&lt;br /&gt;
									orderedComments[i2] = orderedComments[i2-1]&lt;br /&gt;
								end&lt;br /&gt;
								orderedComments[i] = {user=userData.user,when=when}&lt;br /&gt;
								break&lt;br /&gt;
							end&lt;br /&gt;
							if i == #orderedComments then&lt;br /&gt;
								orderedComments[#orderedComments+1] = {user=userData.user,when=when} --Reached the end, latest comment&lt;br /&gt;
								break&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		mw.logObject(orderedComments)&lt;br /&gt;
		&lt;br /&gt;
		local firstComment,lastComment&lt;br /&gt;
		if #orderedComments == 0 then&lt;br /&gt;
			firstComment = &amp;quot;N/A&amp;quot;&lt;br /&gt;
			lastComment = &amp;quot;N/A&amp;quot;&lt;br /&gt;
		elseif #orderedComments == 1 then&lt;br /&gt;
			firstComment = formatDateDiff(now-orderedComments[1].when) .. &amp;quot; ago&amp;quot; .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. concatUsers({orderedComments[1].user})&lt;br /&gt;
			lastComment = &amp;quot;N/A&amp;quot;&lt;br /&gt;
		else&lt;br /&gt;
			firstComment = formatDateDiff(now-orderedComments[1].when) .. &amp;quot; ago&amp;quot; .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. concatUsers({orderedComments[1].user})&lt;br /&gt;
			lastComment = formatDateDiff(now-orderedComments[#orderedComments].when) .. &amp;quot; ago&amp;quot; .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; .. concatUsers({orderedComments[#orderedComments].user})&lt;br /&gt;
		end&lt;br /&gt;
		local participants = #uniqueParticipants .. &amp;quot;: &amp;quot; .. concatUsers(uniqueParticipants)&lt;br /&gt;
		local mentions = &amp;quot;Could be calculated but I&amp;#039;m lazy&amp;quot;&lt;br /&gt;
		local sectionContent = &amp;quot;\n|-\n| &amp;quot;..wikilinkAnchor..&amp;quot; || &amp;quot;..firstComment..&amp;quot; || &amp;quot;..lastComment..&amp;quot; || &amp;quot;..#section.content..&amp;quot; || &amp;quot;..participants..&amp;quot; || &amp;quot;..mentions&lt;br /&gt;
		tableContent = tableContent .. sectionContent&lt;br /&gt;
	end&lt;br /&gt;
	return tableContent .. &amp;quot;\n|}&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.maindev(frame)&lt;br /&gt;
	local content = p.main(frame)&lt;br /&gt;
	return content .. &amp;quot;\n\n&amp;quot; .. frame:extensionTag(&amp;quot;syntaxhighlight&amp;quot;,content,{lang=&amp;quot;html5&amp;quot;})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.getSectionData = getSectionData&lt;br /&gt;
p.getUserMentions = getUserMentions&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Aidan9382</name></author>
	</entry>
</feed>