"""	courseList.py:
	
	This external Zope method allows us to easily group queries visually
	by certain fields.  For instance, the course_list method groups
	all E-Campus courses first by semester, they by type, then by
	category.
	
	The courseList method inserts "pseudo-records" into the query that
	tell us when an old group is "closed" and a new group is "open".
	This makes the HTML coding a breeze.  Upon seeing an "open" record,
	we can render <TABLE> tags and create headings; when we reach a
	"close" record, we can render the accompanying </TABLE> tags.
	
	The 'groupby' argument specifies a list of field names to group by,
	ordered by priority.  The 'query' object must be pre-sorted by the
	fields to group by, in the same order of priority.
	
	Example input from SELECT candy,color FROM candies ORDER BY candy:
		candy			color
		----------------------
		Life Savers		red
		Life Savers		orange
		Life Savers		yellow
		Life Savers		...
		M&M's			blue
		M&M's			brown
		M&M's			green
		M&M's			...
		
	Example (formatted) query object output from courseList(query, ['candy']):
		open-candy: Life Savers
			color: red
			color: orange
			color: yellow
			...
		close-candy: Life Savers
		open-candy: M&M's
			color: blue
			color: brown
			color: green
			...
		close-candy: M&M's

	(Note: if more groupby fields had been entered, open and close tags
	would be nested)
	
	For the best example of this method at work, see course_list		
"""

class SimpleClass:
	pass

def courseList(self, query, groupby):
	""" Augments the given query object with pseudo-records that indicate
	when a group has been opened or closed.  Given query object must be
	pre-sorted by the specified groupby list items """
	
	# Set default to '' for each group.  This will be the "remembered" value
	mem=[]
	for i in groupby:
		mem.append('')
	
	list=[]
	stack=[]
	
	for i in query:
		# For each group-by field, if a new group begins,
		# pop the old group off the stack and add the
		# appropriate closing tags to the list.
		for x in range(len(groupby)):
			if i[groupby[x]] != mem[x]:
				# Pop old group (and subgroups) off the stack
				popper(i, stack, list, groupby, x)
				# Push new group on the stack
				pusher(i, stack, list, groupby)
				
				# Set "memory" to reflect current group
				# and erase all "memories" beneath this group
				mem[x] = i[groupby[x]]
				for y in range(x+1,len(groupby)):
					mem[y]=''
		
		# Append the course record to the list.
		list.append(i)
	
	# Insert the rest of the closing tags
	# and bring the stack down to level 0.
	popper(i, stack, list, groupby, 0)
	
	return list

def popper(i, stack, list, groupby, level):
	""" Pops groups off the stack until reacing 'level' """
	# Bring the stack down beneath the level of the new group
	while len(stack) > level:
		val = stack.pop()
		# Insert a "close" definition, ie. {close_term: 'Summer 2000'}
		c = SimpleClass()
		setattr(c, 'close_'+groupby[len(stack)], val)
		list.append(c)

def pusher(i, stack, list, groupby):
	""" Pushes a new group onto the stack """
	# Insert an "open" definition, i.e. {open_type: 'Internet Courses'}
	c = SimpleClass()
	setattr(c, 'open_'+groupby[len(stack)], i[groupby[len(stack)]])
	list.append(c);
	
	# Push the new group onto the stack.
	stack.append(i[groupby[len(stack)]])
