Page Source

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

class PhotoVideoControl(SingleControl):
    def output_form(self,display,container):
        pass

    def output_disp(self,display,container):
        q = queries.Select(tables=["recorded_events", "recorded_events_login"],
                columns={'id' : 'id',
                'screengrab_small_url' : 'screengrab_small_url',
                'event_date' : 'event_date',
                'title' : 'title'},
                where="""
                recorded_events_login.login = \"%(login)s\" AND
                recorded_events_login.recorded_event_id = recorded_events.id
                """ % display.fields)
        q.execute()
        videos = q.fetchall()
        container.append("""
        <div class="photovideo">
        """)
        if display.fields['photos']:
            debugging.log("display: %s" % display.fields.keys())
            if display.fields['mname'] != '':
                display.fields['alt_name'] = "%(fname)s %(mname)s %(lname)s" % display.fields
            else:
                display.fields['alt_name'] = "%(fname)s %(lname)s" % display.fields
            for photo in display.fields['photos']:
                r = display.req.urls({'login' : display.fields['login'],
                        'fmt' : photo['format'],
                        'alt' : display.fields['alt_name']})
                container.append("""
                <div class="photo">
                <img alt="%(!alt)s" src="%(native_secure:file:photos)s/%(!login)s.%(!fmt)s"/>
                </div>
                """ % r)
        if len(videos) > 0:
            container.append("""
            <div class="videos">
            <div class="videos-title">Videos</div>
            """)
            video_list = []
            for v in videos:
                t = v['event_date'].tuple()
                v['event_date'] = time.strftime("%B %e, %Y", t)
                container.append("""
                <div class="video">
                  <div class="video-screengrab">
                    <a href="/videos/%(id)s">
                     <img alt="%(title)s)" class="video-img-screengrab" src="%(screengrab_small_url)s"/>
                    </a>
                  </div>
                  <div class="video-info">
                    <div class="video-title"><a href="/videos/%(id)s">%(title)s</a></div>
                    <div class="video-date">%(event_date)s</div>
                  </div>
                </div>
                """ % v)
            container.append("""
            </div>
            """)
        container.append("""
        </div>
        """)

class GroupAffiliationControl(SingleControl):
    def output_form(self,display,container):
        pass
    
    def output_disp(self,display,container):
        q = queries.Select(tables=["group_affiliation_login", "group_affiliation"],
                columns={'group_affiliation_name' : 'group_affiliation_name'},
                where="""
                group_affiliation_login.login = "%(login)s" AND
                group_affiliation_login.group_affiliation_id = group_affiliation.id
                """ % display.fields)
        q.execute()
        groups = q.fetchall()
        if len(groups) > 0:
            group_list = []
            for g in groups:
                group_list.append(g['group_affiliation_name'])
            container.append("<h2>Interests</h2>\n<blockquote>")
            container.append(string.join(group_list,", "))
            container.append("\n</blockquote>")

class PersonControl(MultipleControl):
  """Represent a person in the bullet-list of people.  Uses the
  'grouping' field and a NestContainer parent to create the titles for
  different groupings of people."""
  def output_disp(self, display, nest_container):
    # First see if we're beginning a new grouping
    if display.fields['grouping'] != display.last_grouping:
      display.last_grouping = display.fields['grouping']
      nest_container.append(
        NestContainer.Out("""<h3>%s</h3>\n""" % display.last_grouping))
  
    # Now just make the column normally
    nest_container.append("""<a href="%(person!login)s">%(!name)s</a>"""
                          % display.req.urls(display.fields) )

  output_form = output_disp

class MayEditPeopleControl(IfThenElseControl):
  """A little hack: titles should only be editable by people that
  actually have permission (e.g., in the permission table) to edit people.
  So people can't edit their own titles."""
  def __init__(self, admincontrols=[], othercontrols=[]):
    IfThenElseControl.__init__(self, admincontrols, othercontrols)

  def condition_form(self, display):
    return perms.may(display.req.login, 'edit', 'people')

class PeopleDisplay(Display, Database, Documents, FileCopy, Modal, StaticString):
  def get_multiple_title(self):
    return "Members of the Department of Computer Science"

  multiple_container_control = SimpleContainerControl(NestContainer, Container, BulletColumns)
  multiple_row_container_control = NullContainerControl()
  multiple_controls = [ PersonControl() ]

  def get_single_title(self):
    if self.fields['mname']:
      return "%s %s %s" % (self.fields['fname'],
                           self.fields['mname'],
                           self.fields['lname'])
    else:
      return "%s %s" % (self.fields['fname'],
                        self.fields['lname'])

  single_controls = [
    # we only display the photo here.. in the form, it appears at the bottom.
    IfShapeControl(dispcontrols=PhotoVideoControl()),
    "<h1>", TextFieldControl("fname", title="First Name"),
    " ", TextFieldControl("mname", title="Middle Name"),
    " ", TextFieldControl("lname", title="Last Name"), "</h1>",
    "<blockquote>",

    MayEditPeopleControl(admincontrols=
                         [ CommentControl("""\
                         Titles are used to categorize people on the main
                         <a href="%(sect:people)s">people page</a>.  As such, it is crucial that
                         the titles be entered correctly.  The system used to make these
                         categorizations is described on the
                         <a href="%(sect:people)s/groupings">groupings page</a>."""),
                           TitlesControl() ],
                         othercontrols=NoEditControl(None, title="Titles")),
    RetiredControl(),
    "</blockquote>\n",
    GroupAffiliationControl(),
    "<h2>Contact Information</h2>\n<blockquote>",
    TextAreaControl("address", title="Address", cols=30, auto_wrap=0),
    IfExistsControl("office",["<br />\nOffice: ", TextFieldControl("office", title="Office")]),
    IfExistsControl("phone",["<br />\nPhone: ", TextFieldControl("phone", title="Phone")]),
    IfExistsControl("fax",["<br />\nFax: ", TextFieldControl("fax", title="Fax")]),
    "<br />\n", EmailControl("email", title="Email"),
    "</blockquote>\n",
    IfExistsControl("homepage",
                    [ "<h2>Personal Homepage</h2><blockquote>",
                      URLControl("homepage", title="Homepage"),
                      "</blockquote>\n" ] ),
    IfExistsControl("research",
                    [ "<h2>Research</h2>\n<blockquote>",
                      TextAreaControl("research", title="Research", rows=8),
                      "</blockquote>\n" ] ),
    IfExistsControl("education",
                    [ "<h2>Education</h2>\n<blockquote>",
                      TextAreaControl("education", title="Education", rows=8, auto_wrap=0),
                      "</blockquote>\n" ] ),
    IfExistsControl("additional",
                    [ "<h2>Additional</h2>\n<blockquote>",
                      TextAreaControl("additional", title="Additional Info", rows=8),
                      "</blockquote>\n" ] ),
    IfExistsControl("projects",
                    [ "<h2>Projects</h2>\n",
                      ProjectsControl() ] ),
    IfExistsControl("labs",
                    [ "<h2>Laboratories</h2>\n",
                      LabsControl() ] ),
    IfExistsControl("bibliographies",
                    [ """<h2>List of Publications</h2>\n<blockquote>Available in """,
                      DocumentControl(category="bibliographies", title="Bibliography"),
                      ".</blockquote>\n" ] ),
    # We want to show this control down here in the FORM shape (because it's
    # really big onscreen).
    IfShapeControl(formcontrols=PhotoControl(title="Photo")), 
    # MayEditPeopleControl(admincontrols=IfShapeControl(formcontrols=PhotoControl(title="Photo"))),
    IfExistsControl("tech_reports",
                    [ "<h2>Technical Reports</h2>\n<blockquote>",
                      PersonTechreportControl(),
                      "</blockquote>\n" ] ),
    ]


  
  ####
  # The two front-line functions, which get called from dispatch
  #
  def perform_action(self):
    if self.permit_mode('edit'):
      self.perform_database_update()
      actions.notify('people', """\
User '%s' edited the record for person '%s'.
""" % (self.req.login, self.get_login_argument()))
    else:
      raise WebError("""Permission Denied""",
                     """You don't have permission to edit this page""")
                     
  def make_page(self, page):
    # Get some starting info from the DB
    self.prep_database()

    # Fix up the page
    self.add_page_links(page)

    # check the shape we should be in, based on the mode.
    if self.get_mode() in ('edit', 'add'):
      self.shape = shapes.FORM
      
    if self.is_multiple():
      # Plural page has extra decorations
      page.append("<h1>%s</h1>" % self.get_multiple_title())
      self.make_page_top(page)
      self.last_grouping = None           # PersonControl needs this
      page.append(self.multiple_database())
    else:
      page.append(self.single_database())

  def permit_mode(self, mode):
    login_arg = self.get_login_argument()
    return ((mode == 'display') or      # display mode
            (mode == 'edit' and         # permitted to edit
             perms.may(self.req.login,'edit','people')) or
            (mode == 'edit' and         # self-edit
             not self.is_multiple() and
             login_arg and
             login_arg == self.req.login) or
            (mode == 'add' and          # permitted to add
             perms.may(self.req.login,'add','people')))

  def get_login_argument(self):
    if not hasattr(self, 'login_arg'):
      if self.req.url.arguments:
        self.login_arg = self.req.url.arguments[0]
        # Legacy URL support
        if self.req.url.internal.has_key('person'):
          self.login_arg = self.req.url.internal.get('person')[0]
        if self.req.url.internal.has_key('login'):
          self.login_arg = self.req.url.internal.get('login')[0]
      else:
        # No argument
        self.login_arg = None
    return self.login_arg

  ####
  # db_query and db_fetch -- get info
  #
  def db_query(self):
    if self.is_multiple():
      q = queries.Select(tables=['people', 'titles', 'groupings', 'grouping_priorities'],
                         columns = { 'name' : queries.SQL('concat(fname, " ", lname)'),
                                     'login' : 'people.login',
                                     'grouping' : 'grouping_priorities.name' },
                         where="""\
             people.login = titles.login and
             (titles.title like groupings.title or groupings.title is NULL) and
             (titles.context like groupings.context or groupings.context is NULL) and
             groupings.id = grouping_priorities.id and
             not people.retired and not people.pseudo""",
                         order=['grouping_priorities.priority', 'lname', 'fname', 'mname'],
                         distinct=1)
    else:
      # Single query
      login = self.get_login_argument()
      q = queries.Select(tables=['people'],
                         where="""\
             people.login = %s and not people.pseudo""" % queries.represent_value(login) )
    q.execute()
    if q.count() == 0:
      raise WebError("Person Not Found", "The specified person has no record on this website.")
    self.query = q

  def is_multiple(self):
    if self.req.url.arguments:
      return None
    return 1

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

  def db_fetch_titles(self, login):
    q = queries.Select(tables="titles",
                       where="login = %s" % queries.represent_value(login),
                       order="priority")
    q.execute()
    titles = []
    for d in q.fetchall():
      titles.append((d['title'], d['context'], d['priority']))
    self.fields['titles'] = titles

  def db_fetch_projects(self, login):
    q = queries.Select(tables=("projects", "project_members"),
                       where="""login = %s and
                       projects.project_id = project_members.project_id"""
                       % queries.represent_value(login),
                       columns={'name' : 'projects.project_title',
                                'id' : 'projects.project_id' },
                       order="projects.project_title")
    q.execute()
    projects = []
    for d in q.fetchall():
      projects.append((d['name'], d['id']))
    self.fields['projects'] = projects

  def db_fetch_labs(self, login):
    q = queries.Select(tables=("labs", "lab_members"),
                       where="""login = %s and
                       labs.lab_id = lab_members.lab_id""" % queries.represent_value(login),
                       columns={'name' : 'labs.lab_name',
                                'id' : 'labs.lab_id' },
                       order="labs.lab_name")
    q.execute()
    labs = []
    for d in q.fetchall():
      labs.append((d['name'], d['id']))
    self.fields['labs'] = labs

  def db_fetch_tech_reports(self, login):
    # We select one row for each author of each tech report that this person
    # has authored.  Ex:
    # TR-26-82 'The foo of bar' .. .. Dustin Mitchell
    # TR-26-82 'The foo of bar' .. .. Virna Gupta
    # TR-27-16 'The bar of baz' .. .. Brendan Strejcek
    # TR-27-16 'The bar of baz' .. .. Dustin Mitchell
    q = queries.Select(tables=("tech_reports as reports",
                               "tech_report_authors as authors",
                               """tech_report_authors as author left join people as sponsors
                               on reports.sponsor = sponsors.login"""),
                       where="""author.login = %s and
                       author.rep_id = reports.rep_id and
                       reports.rep_id = authors.rep_id and
                       reports.obsolete != 1""" 
                       % queries.represent_value(login),
                       columns={'title' : 'reports.title',
                                'obsolete' : 'reports.obsolete',
                                'rep_id' : 'reports.rep_id',
                                'foobar' : 'if(LOCATE("-",reports.rep_id,4) = 6,INSERT(reports.rep_id,4,0,"19"),reports.rep_id)',
                                'sponsor' : 'concat(sponsors.fname, " ", sponsors.lname)',
                                'pub_date' : 'date_format(reports.pub_date, "%e %M, %Y")',
                                'a_login' : 'authors.login',
                                'a_name' : 'authors.name',
                                'a_prio' : 'authors.priority' },
                       order="foobar desc")




    q.execute()
    trs = []
    last_rep_id = None
    for d in q.fetchall():
      # If this begins a new techreport...
      if last_rep_id != d['rep_id']:
        # Insert a new record, with this single author in it
        last_rep_id = d['rep_id']
        trs.append( { 'title' : d['title'],
                      'rep_id' : d['rep_id'],
                      'pub_date' : d['pub_date'],
                      'sponsor' : d['sponsor'],
                      'authors' : [ ( d['a_login'], d['a_name'], d['a_prio'] ) ] } )
      else:
        # Append this author to the last-added techreport
        trs[-1]['authors'].append( ( d['a_login'], d['a_name'], d['a_prio'] ) )
    self.fields['tech_reports'] = trs

  def db_fetch(self):
    self.fields = self.query.fetch()
    if not self.is_multiple():
      login = self.fields['login']
      self.db_fetch_titles(login)
      self.db_fetch_projects(login)
      self.db_fetch_labs(login)
      self.db_fetch_documents('bibliographies', login)
      self.db_fetch_documents('photos', login)
      self.db_fetch_tech_reports(login)
    return self.fields


  ####
  # db_update -- make changes
  #
  def db_update_titles(self, login):
    # We lock down the titles table, delete everything for this person,
    # then re-add those we've received in self.form_fields.
    # self.form_fields['titles'] is a dictionary from
    # priority-number to (title, context).
    
    # Lock the table, so it doesn't change between select & insert.
    lock = queries.Lock(tables="titles", locktype="WRITE")
    lock.lock()

    try:
      # Delete all of the old rows for this user
      d = queries.Delete(table='titles',
                         where='login = %s' % queries.represent_value(login))
      d.execute()

      # make a list of new rows that should be inserted
      rows = []
      for priority, tc in self.form_fields['titles'].items():
        title, context = tc
        if title or context:
          rows.append({'login' : login,
                       'priority' : priority,
                       'title' : title,
                       'context' : context})

      # And now insert the rows
      if rows:
        i = queries.Insert(table='titles', rows=rows)
        i.execute()
      
    finally:
      lock.unlock()
  
  def db_update_membership(self, table, id_column_name, field, login):
    # We lock down the given table, delete everything for this person,
    # then re-add those we've received in self.form_fields.
    # self.form_fields[field] is a list of ("%ss" % id_column_name).

    # Lock the table, so it doesn't change between select & insert.
    lock = queries.Lock(tables=table, locktype="WRITE")
    lock.lock()

    try:
      # Delete all of the old rows for this user
      d = queries.Delete(table=table,
                         where='login = %s' % queries.represent_value(login))
      d.execute()

      # make a list of new rows that should be inserted
      rows = []
      for id in self.form_fields[field]:
        rows.append({'login' : login,
                     id_column_name : id})

      # And now insert the rows
      if rows:
        i = queries.Insert(table=table, rows=rows)
        i.execute()
      
    finally:
      lock.unlock()

  def db_update_projects(self, login):
    self.db_update_membership('project_members', 'project_id', 'projects', login)
  
  def db_update_labs(self, login):
    self.db_update_membership('lab_members', 'lab_id', 'labs', login)
  
  def db_update(self):
    login = self.get_login_argument()
    form_fields = self.form_fields

    # First take care of the simple fields
    u = queries.Update(table="people", where="login = %s" % queries.represent_value(login))
    for field in ('fname', 'mname', 'lname', 'address', 'office',
                  'phone', 'fax', 'email', 'homepage', 'research',
                  'education', 'additional', 'retired'):
      u[field] = form_fields[field]
    u.execute()
      
    # More of the little hack:
    if perms.may(self.req.login, 'edit', 'people'):
      self.db_update_titles(login)
    self.db_update_projects(login)
    self.db_update_labs(login)
    self.db_update_documents('bibliographies', login)
    self.db_update_documents('photos', login)

  #####
  # Utility functions
    
  def add_page_links(self, page):
    # Prep the page: page type, navigation, etc.
    page.set_type("people")
    page.set_title("Department Members")
    self.add_modes_to_page(page)

    # Multiple:
    if self.is_multiple():
      # Everyone logged in should get this link.
      if self.req.login:
        page.add_navigation('%(sect:people)s/groupings' % self.req.urls, 'Groupings')
        page.add_navigation('%(sect:people)s/alphabetical' % self.req.urls, 'Alphabetical')
    # Single:
    else:
      page.set_title('%s %s %s' % (self.fields['fname'],
                                   self.fields['mname'],
                                   self.fields['lname']))
      page.add_navigation('%(sect:people)s' % self.req.urls, 'People')

    # Only those who can add people should get this link.
    if self.permit_mode('add'):
      page.add_navigation('%(sect:people)s/add' % self.req.urls, 'Add Person')

  def make_page_top(self, page):
    if self.get_mode() == 'edit':
      page.append("""The grouping of people by category on this page is
      controlled from the <a href="%(sect:people)s/groupings">groupings
      page</a>.""" % self.req.urls)
    else:
      page.append(self.GetString(key="whoswho",display=self,title_fn=page.set_title))

def new(req):
  return PeopleDisplay(req)