Page Source

from utils.display import Display, FileCopy, Database, Arguments, Modal
from utils.controls import *
from utils.output import *
from utils.web_exc import WebError
from utils.static_strings import StaticString
from utils import shapes, perms, actions, misc
import queries
from config import docroot
import string, os
import time,MySQLdb

# the page has three arguments the subject (CS/CSPP), course_num (105/523), retire_date 
# (the date course was retired) the third is optional and if not specified defaults to 
# latest version of the course. A special argument 'all' to the retire_date will list 
# all versions of the courses returned by the other two criterion
# so .../subject/CSPP/retire_date/all will give all CSPP courses ever offerred.
# Note the other format cannot be used to specify this kind of a query


# Special Modes which is still DISPLAY shape but does some extra stuff
SPECIAL_DISP_MODES = ['verify','retire','unretire','renumber']
# When making the page a call to xxx_mode after the usual DISPLAY stuff
# is displayed. xxx is a SPECIAL_DISP_MODES member
# The xxx_mode is expected to add a form of some sort and the action button will
# tag a '&mode=xxx' after the '?action=nn'
# In perform_non_edit if the mode is one of the special modes,
# then a call is done to perform_xxx
# All the arguments are in self.args (parse_arguments already called) and
# form variables are in self.req.form_data
# perform_xxx should do the action, notify the overseer if necessary and return the
# URL to bounce to

ACTIVE_FLAG_LONG='9999-12-31 00:00:00.00'
ACTIVE_FLAG='9999-12-31'
ACTIVE_FLAG_SQL="""'9999-12-31'"""

class VerifyControl(Control):
    def __init__(self, permission, date_field,
		 fmt="<address>Last Verified by %(fname)s %(lname)s on %e %B, %Y.</address>"):
	"Checks if login is a member of ver_class & gives appropriate output"
	self.perm = permission
	self.date_field = date_field
	self.fmt = fmt
	
    def output_disp(self,display,container):
	output = time.strftime(self.fmt, time.localtime(display.fields[self.date_field]))
	container.append(output % display.fields);

class ActiveCourseControl(Control):
    def output_form(self,display,container):
        pass

    def output_disp(self,display,container):
        # type and str version of flag value changed to DateTime...
        if not str(display.fields['retire_date']) == ACTIVE_FLAG and not str(display.fields['retire_date']) == ACTIVE_FLAG_LONG:
            container.append("Course was retired on <i> %s </i> <br>" %
                             display.fields['retire_date'])
    
class DescriptionDisplay(Display, Database, Arguments, Modal, StaticString):
    single_controls = [
	"<b>",	TextAreaControl(field="title",title="Title",rows=2),"</b> ",
	IfExistsControl("aka",[ "<b> (" , TextFieldControl("aka","Also Known As"), ")</b> "]),
	"<p><b>Prerequisites:</b> ", TextAreaControl(field="prerequisites",title="Prerequisites",rows=3),"\n",
	"<p><b>Catalog Description:</b> ",TextAreaControl(field="catalog_desc",
                                                           title="Catalog Description"),
        "</p>",
	IfExistsControl("description",[
		"<p><b>Long Description:</b> ", TextAreaControl(field="description", title="Description"),
		"</p>" ]),
	"Instructors:<i> ",TextAreaControl(field="instructors",
                                           title="Instructors",rows=2),"</i> <br>",
	"Quarter offered: <i> ",TextFieldControl(field="quarter",
                                                  title="Quarter"),"</i> <br>",
	IfFieldThenElseControl("ver_login",
			       VerifyControl("faculty","ver_tstamp"),
			       time.strftime(
				   "<address>Unverified as of %e %B, %Y.</address>",
				   time.localtime(time.time()))),
        ActiveCourseControl()
    ];

    multiple_container_control = SimpleContainerControl(TABLE)
    multiple_row_container_control = SimpleContainerControl(TR)
    multiple_controls = [
	FieldControl("course_info",nbsppad=1),
	LinkControl("course_info","%(script)s/%(!subject)s/%(!course_num)s/%(!retire_date)s",
		    FieldControl("title",nbsppad=1)) 
	]
    error_messages = {
        "no_course" : ("No Course",
                       """Course does not exist.
                       Check the name of the course and the retire date and try again."""),
        "retd_crse" : ("Retired Course",
                       "Course is already retired."),
        "act_crse"  : ("Active Course",
                       "Course is already active."),
        "act_ver_exists" : ("Active Version Exists",
                            "There is a currently active version of the course already."),
        "no_int"    : ("Non Integer",
                       "Course number should be a positive integer."),
        "already_retired": ("Already Retired",
                            "The same course cannot be retired twice in a day. Try retiring it tomorrow"),
        "new_crse_exists": ("New Course Exists",
                            """The course to which you want to renumber has an active version.
                            Retire that first and try again"""),
    }

    def handle_no_perms(self,page,message):
        self.generate_error_page(page,"Permission Denied",
                                 "You dont have %s permissions on this page" % message)

    def handle_no_course(self,page,message):
        self.generate_error_page(page,"Course not found", 
			   "There is no record of a %(subject)s-%(course_num)s course." % message)

    def process_arguments(self):
	self.args = self.parse_arguments(['subject',
					  'course_num',
					  ('retire_date',ACTIVE_FLAG)]) # get the URL arguments
        # Add 00 if we got a 3 digit course number
        cnum = self.args['course_num']
        if cnum and len(cnum) == 3:
            self.args['course_num'] = cnum+"00"

    def handle_non_edit(self):
        act = self.req.url.internal['mode'][0] # it is a list with one element
        if act in SPECIAL_DISP_MODES and self.permit_mode(act):
            method = getattr(self,'perform_'+act) # get the function to call
            return method() # call it
        else:
            raise WebError ("no_perms()",act) # This results in a function call to handle_no_perms

    def handle_edit(self):
        if self.permit_mode('edit'):
            self.perform_database_update()
            actions.notify(region='courses',
                           user=self.req.login,
                           data=self.args,
                           type_op="edit")
            return "" # Same page Same mode
        else:
            raise WebError("no_perms()",act)
        
    def perform_action(self):
        self.process_arguments()
        if self.req.url.internal.has_key('mode'):
            return self.handle_non_edit()
        else:
            return self.handle_edit()
        
    def SQL_Error(self,stage,e=None):
	# stage is extra string giving more information
	actions.notify(region='courses',
                       user=self.req.login,
                       type_op = "edit",
                       data = self.args,
                       message = "SQL Error at stage " + stage + ":" + str(e))
	raise WebError("SQL_Error",
                       """Contact webmaster@cs.uchicago.edu, with details as to how
to reproduce the error""")

    def permit_mode(self, mode):
	return ((mode == 'display') or      # display mode
                (mode in ['edit'] + SPECIAL_DISP_MODES and # valid mode
                 perms.may(self.req.login,mode,'courses')))

    def make_page(self,page):
	self.process_arguments()
	page.set_type("courses")
	if self.is_multiple():
            title = self.get_multiple_title()
            page.set_title(title)
	    page.add_navigation("%(sect:courses)s/add" % self.req.urls(),"Add Course")
            page.add_navigation("%(sect:courses)s/desc_verify" % self.req.urls(), "by Verification")
            page.append("<h1>%s</h1>" % title)
            page.append(self.multiple_database())
	else:
            title = self.get_single_title()
            page.set_title(title)
	    self.add_modes_to_page(page)
            page.add_navigation("%(sect:courses)s/description" % self.req.urls(), "Descriptions")
            page.append("<h1>%s</h1>" % title)
	    if self.mode == 'edit':
		self.shape = shapes.FORM
                page.append(self.single_database())
            elif self.mode == 'display':
                self.shape = shapes.DISP
                page.append(self.single_database())
            elif self.mode in SPECIAL_DISP_MODES: # any of the action oriented display modes
                page.append(self.single_database()) # add the usual stuff
                method = getattr(self,self.mode+'_mode') # get the method to call
                method(page) # call it!

    def is_multiple(self): # single page iff all three specified
	return not (self.args['subject'] and self.args['course_num'] and 
		    (self.args['retire_date'] != 'all'))
	    
    def get_single_title(self):
	return "%s %s" % (string.upper(self.args['subject']), 
					self.args['course_num']) 

    def get_multiple_title(self):
	if not self.args['subject']:
	    return "Course Descriptions"
	else:
	    return "%s Course Descriptions" % string.upper(self.args['subject']);

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

    def db_query(self):
	if self.is_multiple():
	    self.db_multiple_query()
	else:
	    self.db_single_query()

    def db_multiple_query(self):
	columns = { 'course_info' : "concat(subject,course_num)",
		    'subject' : 'subject',
		    'course_num' : 'course_num',
		    'title' : 'title',
		    'retire_date' : 'retire_date'}
	tables = ["courses"]
	order=["subject","course_num"]
	where = ""

	if self.args['subject']:
	    where = where + "AND subject=%(subject)s "
	if self.args['course_num']:
	    where = where + "AND course_num=%(course_num)s "
	if self.args['retire_date'] != 'all':
	    where = where + "AND retire_date=%(retire_date)s "
	where = where[3:] # remove the first AND from the where clause

	SQLargs = {}
	for k,v in self.args.items():
	    SQLargs[k] = queries.represent_value(v)
	where = where % SQLargs

	q = queries.Select(tables=tables, columns=columns,where= where, order = order)
	q.execute()
	self.query = q

    def db_single_query(self):
	wc = "subject=%(subject)s AND course_num=%(course_num)s AND retire_date=%(retire_date)s"
	SQLArgs = {}
	for k,v in self.args.items():
	    SQLArgs[k]=queries.represent_value(v)
	wc = wc % SQLArgs
	q = queries.Select(tables=["courses left join people on ver_login=login"],
			   columns={ 'course_num':'course_num',
				     'title' : 'courses.title',
				     'description' : 'description',
				     'catalog_desc':'catalog_desc',
				     'prerequisites' : 'prerequisites',
				     'aka' : 'aka',
				     'instructors':'instructors',
				     'quarter' : 'quarter',
				     'subject':'subject',
				     'ver_tstamp' : 'unix_timestamp(ver_date)',
				     'login' : 'login',
				     'ver_login' : 'ver_login',
				     'fname':'fname',
				     'mname' : 'mname',
				     'lname' : 'lname',
				     'retire_date' : 'retire_date'
				    },
			   where= wc
			   )
	q.execute()
	if q.count() == 0:
	    raise WebError ("no_course()",self.args)
	self.query = q

    def db_fetch(self):
	self.fields = self.query.fetch()
	return self.fields

    def course_exists(self,subject=None,course_num=None,retire_date=None):
        """returns (n,wc) where N= number of records present satisfying given condition.
        If any not supplied then taken from self.args, WC returns the where clause constructed"""
        SQLargs={}
        if subject:
            SQLargs['subject'] = queries.represent_value(subject)
        else:
            SQLargs['subject'] = queries.represent_value(self.args['subject'])

        if course_num:
            SQLargs['course_num'] = queries.represent_value(course_num)
        else:
            SQLargs['course_num'] = queries.represent_value(self.args['course_num'])

        if retire_date:
            SQLargs['retire_date'] = queries.represent_value(retire_date)
        else:
            SQLargs['retire_date'] = queries.represent_value(self.args['retire_date'])
            
        wc = "subject=%(subject)s AND course_num=%(course_num)s AND retire_date=%(retire_date)s"
        wc = wc % SQLargs
        q = queries.Select(tables=["courses"],
                           columns={'c' : 'count(*)'},
                           where = wc)
        q.execute()
        num = q.fetch()['c'] # the number of records
        # This should be either a 0 or a 1
        if (num < 0) or (num > 1):
            self.SQL_Error("DB Integrity Error. %s records with same primary fields!" % num)
        return (num,wc) # The field c returned by SQL

    def db_update(self):
        # Check if record for course exists
        try: # Start the atomic operation
            lock = queries.Lock(tables=["courses"])
            num,whereclause = self.course_exists()
            if num == 0:
                raise WebError ("no_course{}")
            # Course exists. Go ahead and update it
            upd = queries.Update(table = "courses", where = whereclause, sets=self.form_fields)
            try:
                upd.execute()
            except MySQLdb.Error,e:
                self.SQL_Error("update",e)
        finally:
            lock.unlock()
        return
    
    def verify_mode(self,page):
        """Add button to verify"""
        submit = SUBMIT_INPUT(attrs={'value':'Verify Course Description'})
        form = FORM(sub=submit,action="%(action:)s&mode=verify"%self.req.urls())
        page.append("\n\n")
        page.append(render_output(form))

    def perform_verify(self):
        "Actually verify it"
        try: # Start atomic operation
            lock = queries.Lock(tables=["courses"])
            num,whereclause = self.course_exists()
            if num == 0:
                raise WebError("no_course{}")
            # Course exists. Verify it
            upd = queries.Update(table="courses",
                                 where = whereclause,
                                 sets= {'ver_login':self.req.login,
                                        'ver_date':queries.SQL('current_date()')})
            try:
                upd.execute()
            except MySQLdb.Error,e:
                self.SQL_Error("verify",e)
        finally:
            lock.unlock()

        # notify overseer and return new URL to bounce to
        actions.notify(region='courses',
                       user=self.req.login,
                       data=self.args,
                       type_op = "verify")
        return "%(mode:display)s" % self.req.urls()

    
    def retire_mode(self,page):
        page.append(self.GetString("retire_course")) # what retire exactly does
        submit = SUBMIT_INPUT(attrs={'value':'Retire Course'})
        form = FORM(sub=submit,action="%(action:)s&mode=retire"%self.req.urls())
        page.append("\n\n")
        page.append(render_output(form))

    def perform_retire(self):
        """ Retires the course whose details are in self.args"""
        # Are we trying to retire a retired course?
        if self.args['retire_date'] != ACTIVE_FLAG: # already retired
            raise WebError("retd_crse{}")
        # The action takes place here
        self.retire_course(subject=self.args['subject'],
                           course_num=self.args['course_num'])
        # notify the overseer and return new URL
        actions.notify(region='courses',
                       user=self.req.login,
                       data = self.args,
                       type_op="retire")
        return "%(mode:display)s" % self.req.urls()

    def unretire_mode(self,page):
        page.append(self.GetString("unretire_course")) # what does it exactly do
        submit = SUBMIT_INPUT(attrs={'value':'Unretire Course'})
        form = FORM(sub=submit,action="%(action:)s&mode=unretire"%self.req.urls())
        page.append("\n\n")
        page.append(render_output(form))
 
    def perform_unretire(self):
        # Are we trying to unretire an active course?
        if self.args['retire_date'] == ACTIVE_FLAG: # already active
            raise WebError("act_crse{}")
        n,wc = self.course_exists()
        if n == 0: raise WebError ("no_course{}")
        n,wc = self.course_exists(retire_date=ACTIVE_FLAG)
        if n == 1: raise WebError ("act_ver_exists{}")
        # First check that there is no active version of this course.
        # If none exists create an active version by copying this record and setting it
        # as active.
        self.create_course_from_template(new_subject=self.args['subject'],
                                         new_course_num=self.args['course_num'])
        actions.notify(region='courses',
                       data=self.args,
                       type_op="unretire",
                       user=self.req.login)
        return "%(mode:display)s" % self.req.urls()

    def renumber_mode(self,page):
        page.append(self.GetString("renumber_course")) # what exactly does it do
        submit = SUBMIT_INPUT(attrs={'value':'Renumber Course'})
        subject = SELECT(name="new_subject",options=misc.subject_pairs)
        course_num = TEXT_INPUT(attrs={'name':'new_course_num','size':8})
        form = FORM(sub=["<b>New Number:</b> ",subject,course_num,submit],
                    action="%(action:)s&mode=renumber"%self.req.urls())
        page.append("\n\n")
        page.append(render_output(form))

    def perform_renumber(self):
        """Retire the current course if not already retired, and create a new course with details
        taken from req.form_data and the current course as the template. This amounts to a call to
        retire_course and one to create_course_from_template, in this order. If done in the wrong
        order, then renumbering a course to itself will not work!"""
        # get the data from req.form_data
        subject = self.req.form_data['new_subject']
        course_num = int(self.req.form_data['new_course_num'])
        # validate input data
        if course_num <= 0: raise WebError ("no_int{}") 
        n,wc = self.course_exists() # Does current course exist?
        if n==0: raise WebError("no_course{}")
        # Does the new course have an active version already
        n,wc = self.course_exists(subject=subject,
                                  course_num=course_num,
                                  retire_date=ACTIVE_FLAG)
        if n==1: raise WebError ("new_crse_exists{}")
        # if necessary retire the current course
        if self.args['retire_date'] == ACTIVE_FLAG:
            date = self.retire_course(self.args['subject'],self.args['course_num'])
        else:
            date = self.args['retire_date']
        # either case now date is the date on which this course is/was retired
        self.create_course_from_template(old_subject=self.args['subject'],
                                         old_course_num=self.args['course_num'],
                                         old_retire_date = date,
                                         new_subject=subject,
                                         new_course_num=course_num)
        # Again order matters else wont work if (old/new)subject and (old/new)course_num
        # are the same.
        actions.notify(region="courses",
                       type_op="renumber",
                       data=self.args,
                       message = "New Subject = %s, New Course Number = %s" % (subject,course_number),
                       user=self.req.login)
        return "%(mode:display)s" % self.req.urls() # no overseer notification needed here

    def retire_course(self,subject,course_num):
        "Reires the course specified and returns the date (TODAY) on which it was retired"
        try: # Start atomic operation
            lock = queries.Lock(tables=["courses","schedules"])
            # is the course present and active?
            num,whereclause = self.course_exists(subject=subject,
                                                 course_num=course_num,
                                                 retire_date=ACTIVE_FLAG)
            if num == 0: raise WebError("no_course{}")
            # Get todays date
            q = queries.Select(columns={'date':queries.SQL('current_date()')})
            q.execute()
            date = q.fetch()['date'] # todays date
            # Has a version of this course been retired today already?
            num,arbit=self.course_exists(subject=subject,
                                       course_num=course_num,
                                       retire_date = date)
            if num == 1: raise WebError("already_retired{}")
            # Need to execute two update queries both need to have the same date_stamp
            # Theoretically the two queries can be executed before and after 12 midnight
            # That is why we got the date stamp from SQL first
            # Retire the course
            upd1 = queries.Update(table="courses",
                                 where = whereclause,
                                 sets= {'retire_date':date})
            try:
                upd1.execute()
            except MySQLdb.Error,e:
                self.SQL_Error("retire - courses stage",e)

            # All schedules pointing to this course should also be modified
            upd2 = queries.Update(table="schedules",
                                  where=whereclause,
                                  sets={'retire_date':date})
            try:
                upd2.execute()
            except MySQLdb.Error,e:
                self.SQL_Error("retire - schedules stage",e)
        finally:
            lock.unlock()
        return date

    def create_course_from_template(self,
                                    new_subject,
                                    new_course_num,
                                    old_subject=None,
                                    old_course_num=None,
                                    old_retire_date = None):
        """Creates a new active course with given details, remaining data taken from the course
        pointed to by self.args"""
        if not old_subject:    old_subject = self.args['subject']
        if not old_course_num: old_course_num = self.args['course_num']
        if not old_retire_date:old_retire_date = self.args['retire_date']
        try: # Start atomic operation
            lock = queries.Lock(tables=["courses"])
            num,whereclause = self.course_exists(subject=old_subject,
                                                 course_num=old_course_num,
                                                 retire_date=old_retire_date) # is the course present
            if num == 0: raise WebError("no_course{}")
            # Course exists
            # Now we need to check if there is an active version of this new course
            n,arbit = self.course_exists(subject=new_subject,
                                         course_num=new_course_num,
                                         retire_date=ACTIVE_FLAG)
            if n == 1:  raise WebError ("act_ver_exists{}")
            # There is no active version. So we can go ahead and create an active version
            #First select the current record fully. While checking if it exists we set up the
            # where clause. Just reuse it. 
            q = queries.Select(tables=["courses"],where=whereclause)
            # empty columns={} means get all the columns
            q.execute()
            rec = q.fetch() # Get the record
            try:
                rec['subject'] = new_subject
                rec['course_num'] = new_course_num
                rec['retire_date'] = ACTIVE_FLAG # Change the record so that it is active
            except KeyError: # Something flawed with implementation
                self.SQL_Error("Design Flaw: Primary Key fields not present!")
            ins = queries.Insert(table="courses",rows=[rec])
            try:
                ins.execute()
            except MySQLdb.Error,e:
                # Most probably this means we are violating integrity constraints
                # Again should not happen, but who knows the ways of computers!
                self.SQL_Error("unretire - insert stage",e) 
        finally:
            lock.unlock()
        
###
# This class is instantiated from inline_descr.py

class InlineDescriptionDisplay(DescriptionDisplay):
    multiple_container_control = NullContainerControl()
    multiple_row_container_control = NullContainerControl()
    multiple_controls = [
        "<h2>", FieldControl("course_info",nbsppad=1), "</h2>",
	LinkControl("course_info",
                    "%(sect:courses)s/description/%(!subject)s/%(!course_num)s/%(!retire_date)s",
		    FieldControl("title",nbsppad=1)) ,
	IfExistsControl("aka",[ "<b> (" , FieldControl("aka","Also Known As"), ")</b> "]),
	"<i> ",	TextFieldControl("prerequisites","PQ"),"</i>\n",
	TextAreaControl("description","Description"),
	"<i> ",TextAreaControl("instructors","Instructors"),"</i>",
	"<i> ",TextFieldControl("quarter","Quarter"),"</i> ",
	"<p>\n",
	IfFieldThenElseControl("ver_login",
			       VerifyControl("faculty","ver_tstamp"),
			       time.strftime(
				   "<address>Unverified as of %e %B, %Y.</address>",
				   time.localtime(time.time()))),
	"</p>",
	ActiveCourseControl()
	];

    def permit_mode(self, mode):
        # No editing
	return (mode == 'display')

    def is_multiple(self):
	return 1                        # always multiple
	    
    def db_query(self):
	columns = { 'course_info' : "concat(subject,course_num)",
		    'course_num':'course_num',
                    'title' : 'courses.title',
                    'description' : 'description',
                    'prerequisites' : 'prerequisites',
                    'aka' : 'aka',
                    'instructors':'instructors',
                    'quarter' : 'quarter',
                    'subject':'subject',
                    'ver_tstamp' : 'unix_timestamp(ver_date)',
                    'login' : 'login',
                    'ver_login' : 'ver_login',
                    'fname':'fname',
                    'mname' : 'mname',
                    'lname' : 'lname',
                    'retire_date' : 'retire_date'
                    }
	tables=["courses left join people on ver_login=login"]
	order=["subject","course_num"]
	where = ""

	if self.args['subject']:
	    where = where + "AND subject=%(subject)s "
	if self.args['course_num']:
	    where = where + "AND course_num=%(course_num)s "
	if self.args['retire_date'] != 'all':
	    where = where + "AND retire_date=%(retire_date)s "
	where = where[3:] # remove the first AND from the where clause

	SQLargs = {}
	for k,v in self.args.items():
	    SQLargs[k] = queries.represent_value(v)
	where = where % SQLargs

	q = queries.Select(tables=tables, 
			   columns=columns,
			   where= where,
			   order = order
			   )
	q.execute()
	self.query = q

class VerifyDescDisplay(Display, Database, Arguments, Modal):

    multiple_container_control = SimpleContainerControl(TABLE)
    multiple_row_container_control = SimpleContainerControl(TR)
    multiple_controls = [
	FieldControl("course_info",nbsppad=1),
        CoalesceControl(subcontrols=["( Verified ",
                                     FieldControl("ver_date",nbsppad=1),
                                     ")"]),
	LinkControl("course_info","%(sect:courses)s/description/%(!subject)s/%(!course_num)s/%(!retire_date)s",
		    FieldControl("title",nbsppad=1))
	]

    def process_arguments(self):
	self.args = self.parse_arguments(['subject']) # get the URL arguments

    def make_page(self,page):
	self.process_arguments()
	page.set_type("courses")
        title = self.get_multiple_title()
        page.set_title(title)
        page.add_navigation("%(sect:courses)s/add" % self.req.urls(),"Add Course")
        page.add_navigation("%(sect:courses)s/description" % self.req.urls(), "by Course Number")
        page.append("<h1>%s</h1>" % title)
        page.append(self.multiple_database())

    def is_multiple(self): # Always multiple
	1

    def get_multiple_title(self):
	if not self.args['subject']:
	    return "Course Descriptions by Verification Date"
	else:
	    return "%s Course Descriptions by Verification Date" % string.upper(self.args['subject']);

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

    def db_query(self):
        self.db_multiple_query()

    def db_multiple_query(self):
	columns = { 'course_info' : "concat(subject,course_num)",
		    'subject' : 'subject',
		    'course_num' : 'course_num',
		    'title' : 'title',
		    'retire_date' : 'retire_date',
                    'ver_date' : 'ver_date'}
	tables = ["courses"]
	order=["ver_date","subject","course_num"]

	if self.args['subject']:
	    where =  "subject=%(subject)s"
        else:
            where = ""

	SQLargs = {}
	for k,v in self.args.items():
	    SQLargs[k] = queries.represent_value(v)
	where = where % SQLargs

	q = queries.Select(tables=tables, columns=columns,where= where, order = order)
	q.execute()
	self.query = q	

    def db_fetch(self):
	self.fields = self.query.fetch()
	return self.fields

def new(req):
    return DescriptionDisplay(req)

def new_inline(req):
    return InlineDescriptionDisplay(req)

def new_verify(req):
    return VerifyDescDisplay(req)