Page Source

from utils.display import Display, Modal, Database, Arguments
from utils.controls import *
from utils.output import *
from utils.web_exc import WebError
import queries, debugging
from utils import misc, bounce
from utils.static_strings import StaticStringControl
from config import docroot
import string, os
import time

# <page>/y/q/s/n/kw
# Gives schedules for year y, qtr q (AUT/WIN/..), subject s (CS or CSPP)
# and number n (105/106),containing keyword kw/reg_exp.
# A blank argument stands for any/all, 
# i.e. blank year and WIN qtr means any year WIN qtr

# Just instantiate the given output class
# checks the number of arguments given 
# if only year given then also adds a Quarter column
# if year not given then also add a year column

class TableContainerControl(ContainerControl):
  def __init__(self, *args, **kwargs):
    self.args = args
    self.kwargs = kwargs

    # the display will always be a ScheduleDisplay Instance
  def output_disp(self, display, container):
    qtrcol = None
    yearcol = None
    if display.multiple_qtrs:
      qtrcol = 1
    if display.multiple_years:
      yearcol=1

    rv = ScheduleTable(qtrcol=qtrcol,yearcol=yearcol,*self.args,**self.kwargs)
    container.append(rv)
    return rv
  
  output_form = output_disp             # same thing

class ScheduleTable(TABLE):
    def __init__(self,qtrcol=None,yearcol=None,*args,**kwargs):
	self.qtrcol = qtrcol
        self.yearcol=yearcol
	TABLE.__init__(self,*args,**kwargs)

class IsQtrControl(IfThenElseControl):
    def __init__(self,thenc):
	IfThenElseControl.__init__(self,thenc)

    def condition(self,display): # 1 if multiple qtarters
	return display.multiple_qtrs

class IsYearControl(IfThenElseControl):
  def __init__(self,thenc):
    IfThenElseControl.__init__(self,thenc)

  def condition(self,display):
    return display.multiple_years
  
class InstrControl(Control): 
    """Output a comma separated list of links to each instructors homepage
       The information is assumed to be in the field display.instructors
       which is expected to be a list of dictionaries"""
    def output_disp(self,display,container):
        ans = []
        for dict in display.instructors:
            if dict['pseudo']:
                op = dict['lname']
            else:
                op = '<a href="%(person:'+ dict['login']+ ')s">'
                op = op % display.urls()
                op = (op+ '%(fname)s') % dict
                if dict['mname'] != '':
                    op = (op+ ' %(mname)s') % dict
                op = (op+ ' %(lname)s</a>') % dict
            ans = ans + [op]
        container.append(string.join(ans,", "))

class HeaderControl(Control):
    def output_disp(self, display, container):
	# The display object should be a ScheduleDisplay object 
	# otherwise will not work

        # migration to guava (Adam Shaw)
        container.append("<h1><font color='red'>This information is no longer updated.</font></h1>")
        container.append("<h1><font color='red'>Please follow this </font><a href='http://course-info.cs.uchicago.edu'>link</a><font color='red'> to current information.</font></h1>")
	container.append("<h2>Courses</h2>")
	if display.args['quarter']:
	    qtr = display.args['quarter']
	else:
	    qtr = ''	
	if display.args['year']:
	    year = int(display.args['year'])
	else:
	    year = 0
	quarters = [('All',''),
		    ('Autumn','AUT'),
		    ('Winter','WIN'),
		    ('Spring','SPR'),
		    ('Summer','SUM')]
	y = time.gmtime(time.time())[0] # get the current year
	# we have a UL (bulleted list) of a link and a form
	# The form has some text followed by two select thingies and a Go thingie.
	form = Container([ # initialise a Container of output objects
	         '<form method="post" enctype="multipart/form-data" action="%s">' 
		    % ("%(sect:courses)s" % display.urls()),
		 "View Schedule for",
		 SELECT(name="year",options=[(y-1,y-1),(y,y),(y+1,y+1)],default=year), # Select year
		 SELECT(options=quarters,name="quarter",default=qtr), # select quarter
		 SUBMIT_INPUT({'value':'Go'}), # Go
		 '&nbsp;<a href="%(sect:courses)s/search">Advanced Search</a> ' % display.urls(), 
		 # Search
		 '</form>'
	    ]) # end form
	# Once form object is done, this will in terms of the FORM() object
	ul = BulletColumns([
	    '<a href="%(sect:courses)s/description"> Course Descriptions </a><br />' % display.urls(),
	    form
	    ]) # end ul
	container.append(ul)

class CaveatControl(IfThenElseControl):
  """Show the if control exactly if show_caveat has been set to true earlier."""
  def condition(self, display):
    return display.show_caveat

class ScheduleDisplay(Display, Database, Arguments, Modal):
    multiple_container_control = CoalesceContainerControl([
        HeaderControl(),
        CaveatControl(StaticStringControl("schedule_tentative")),
        DisplayTitleControl(head_level=2),
        TableContainerControl(attrs={ 'width' : '100%', 
                                      'border' : 0,
                                      'cellspacing' : 0,
                                      'cellpadding' : 3}) ,
                                      StaticStringControl("read_rsrch")
        ], 
        representative=3) # the active control is the 2nd one. Index starts at 0
    multiple_row_container_control = SimpleContainerControl(TR)
    # <div class="sched_entry course">
    #   <span class="sp_field_content sp_title"></span>
    #   <div class="sched_entry course_info">
    #     <span class="sp_field_content sp_course"></span>
    #     <span class="sp_field_content sp_year"></span>
    #     <span class="sp_field_content sp_quarter"></span>
    #     <div class="sched_entry instructor"></div>
    #     <div class="sched_entry timeday"></div>
    #     <div class="sched_entry room"></div>
    #     <div class="sched_entry remarks"></div>
    #     <div class="sched_entry homepage"></div>
    #   </div>
    # </div>
    multiple_controls = [
      DivControl("sched_entry course", '',
          CoalesceControl([
              SpanControl("sp_field_content sp_title", '',
                  LinkControl("title",
                      "%(sect:courses)s/info/%(!year)s/%(!quarter)s/%(!scourse)s",
                      FieldControl("title",nbsppad=1))
              ),
              DivControl("sched_entry course_info", '',
                  CoalesceControl([
                      SpanControl("sp_field_content sp_course", '', FieldControl("course",nbsppad=1)),
                      IsYearControl(SpanControl("sp_field_content sp_year", '', FieldControl("year"))),
                      IsQtrControl(SpanControl("sp_field_content sp_quarter", '', FieldControl("quarter"))),
                      DivControl("shed_entry instructor", '', InstrControl()),
                      DivControl("sched_entry timeday", '', FieldControl("timeday",nbsppad=1)),
                      DivControl("sched_entry room", '', FieldControl("room",nbsppad=1)),
                      IfExistsControl("remarks",
                          DivControl("sched_entry remarks", '',
                                  FieldControl("remarks",nbsppad=1)
                          )
                      ),
                      IfExistsControl("homepage",
                          DivControl("sched_entry homepage", '',
                                  LinkControl(field="homepage", url="%(!homepage)s", subcontrol="Course Homepage")
                          )
                      ),
                  ])
              ),
          ])
      )]

    def handle_type_check(self,page,message):
      self.generate_error_page(page,"Type Check Failed","The argument to %s was not an integer" % message)
      
    def process_arguments(self):
	"parse the arguments got either thru cmd line or POST DATA"
	self.defqtr,self.defyear = misc.current_qtr()
	self.args = self.process_search_arguments(argnames=['year','quarter','subject','c_number','keywords','room'],
					 noargs=[self.defyear,self.defqtr],space2plus=1,IntCheck=['year','c_number'])
        if type(self.args) == StringType: # either year or c_number was not a number
          raise WebError("type_check()",self.args) # should result in a call to handle_type_check
        # Throw out quarter if it's not legit
        if self.args['quarter'] not in ['WIN','SPR','SUM','AUT']:
        	self.args['quarter'] = None
        # Add 00 if we got a 3 digit course number
        cnum = self.args['c_number'] # It is an integer here
        if cnum and (cnum < 1000):
            self.args['c_number'] = cnum*100


    def add_nav_links(self,page):
      if perms.may(self.req.login,'add','courses'):
      	page.add_navigation("%(sect:courses)s/add" % self.req.urls(),"Add Course")
      if perms.may(self.req.login,'add','schedules'):
	page.add_navigation("%(sect:courses)s/add_schedule" % self.req.urls(), "Add Schedule")

    def make_page(self,page):
	self.process_arguments()
	page.set_type("courses")
	page.set_title("Course Schedule. "+(self.get_criteria() or ""))
        self.add_nav_links(page)
	# show caveat if requested quarter is more than a quarter in the future
	# or if no year specified
	Q = {}; Q['WIN']=0; Q['SPR']=1; Q['SUM']=2; Q['AUT']=3; Q[None]=3
	self.show_caveat = self.args['year'] == None or \
                           4*self.args['year']+Q[self.args['quarter']] > 4*self.defyear+Q[self.defqtr]+1
	page.append(self.multiple_database())

    def is_multiple(self): # only multiple mode
	return 1

    def get_multiple_title(self):
      return "Course Schedule"
    
    def get_criteria(self):
      ans = ""
      if not self.args:
        return ""
      dispnames = [ ('Quarter','quarter'),
                    ('Year','year'),
                    ('Subject','subject'),
                    ('Room','room'),
                    ('Course number','c_number')
                    ]
      ans = "Criteria: "
      for (user,key) in dispnames:
        if self.args[key]: # if key was part of the search criteria
          ans = ans + " " + str(self.args[key]) # some args are integers
      if self.args['keywords']: # if there was keyword criteria
        ans = ans + ". Keywords: '%s'" % self.args['keywords']
      return ans

    def db_rows(self):
	return self.query.count()

    def db_query(self):
	self.multiple_qtrs = None
        self.multiple_years = None
	tables=["schedules as s", "people as p", "courses as c", "instructorship as i"]
	columns={"course" : "concat(s.subject,' ',s.course_num,'-',s.course_sec)",
	         "scourse" : "concat(s.subject,s.course_num,'-',s.course_sec)",
		 "homepage":"s.homepage",
		 "pseudo":"pseudo",
		 "course_num":"c.course_num",
		 "course_sec":"s.course_sec",
		 "title" :"c.title",
		 "login":"login", # The instructor from instructorship if only one
         "fname":"fname",
         "mname":"mname",
		 "lname":"lname",
		 "timeday":"s.timeday",
		 "room":"s.room",
		 "remarks":"s.remarks",
		 "year":"s.year",
		 "quarter":"s.quarter",
		 "retired" : "retired",
		 "subject":"c.subject",
		 "retire_date":"s.retire_date",
		 "sched_id" : "s.sched_id",
		 "num" : "count(*)" # number of instructors 
		 }
	wc = " i.instructor=login AND s.course_num=c.course_num AND s.subject=c.subject "
	wc = wc + " AND s.retire_date=c.retire_date AND s.sched_id=i.sched_id "
	if self.args['room']: # if room specified
          wc = wc + " AND s.room rlike %(room)s "
	if self.args['year']: # if year specified
          wc = wc + " AND s.year=%(year)s "
        else:
          self.multiple_years = 1
	if self.args['quarter']: #if specific quarter requested
	    wc = wc + " AND s.quarter=%(quarter)s "
	else: 
	    self.multiple_qtrs = 1
	if self.args['subject']: # if subject specified
	    wc = wc + " AND s.subject=%(subject)s "
	if self.args['c_number']: # if course number specified
	    wc = wc + " AND s.course_num=%(c_number)s "
	if self.args['keywords']: # if keywords specified
	    wc = wc + " AND ( (lower(c.title) rlike lower(%(keywords)s)) OR "
	    wc = wc + "(lower(s.instructor) rlike lower(%(keywords)s)) OR "
	    wc = wc + "(lower(lname) rlike lower(%(keywords)s)) ) "
	# convert data into secure SQL suitable form. This should not be done earlier
	# as None becomes 'NULL'. If no condition on year was given (year=None).
	# dont want a query like "where year=NULL". Dont modify self.args as
	# other controls (HeaderControl) reads it to display default arguments
	SQLargs = {}
	for k,v in self.args.items():
	    SQLargs[k] = queries.represent_value(v)
	wc = wc % SQLargs
	groupby = "i.sched_id"; # group by this
	order=('year DESC','subject', 'course_num', 'course_sec') # year in descending order
	q = queries.Select(tables=tables,columns = columns,where=wc,order=order,groupby=groupby)
	q.execute() # if no records returned then empty table displayed
	self.query = q

    def db_fetch(self):
	self.fields = self.query.fetch()
	if not self.fields:
	    return self.fields; # finished with the data
	if self.fields['num'] > 1: # More than one instructor then get their info
	    tables=["instructorship","people"]
	    columns={ 'lname' : 'lname',
              'fname' : 'fname',
              'mname' : 'mname',
		      'login' : 'login',
		      'pseudo': 'pseudo or retired' }
	    wc = "login = instructor and sched_id=" 
	    wc = wc + queries.represent_value(self.fields['sched_id'])
	    q = queries.Select(tables=tables,columns=columns,where=wc)
	    q.execute()
	    self.instructors = q.fetchall() # get all the instructors
	else:
	    self.instructors = [{'lname' : self.fields['lname'],
                 'fname': self.fields['fname'],
                 'mname': self.fields['mname'],
				 'login': self.fields['login'],
				 'pseudo':self.fields['pseudo'] or self.fields['retired']}]
	self.fields['year'] = int(self.fields['year']) # else year is 2004L; python complains about 'long int'
	return self.fields

def new(req):
    return ScheduleDisplay(req)