From 8655a720e6835c28b02c1b9e5d6a69646385bb6d Mon Sep 17 00:00:00 2001 From: Ed Summers Date: Sun, 20 Nov 2011 22:14:48 -0500 Subject: [PATCH] initial commit --- .gitignore | 2 + jobs/__init__.py | 0 jobs/management/__init__.py | 0 jobs/management/commands/__init__.py | 0 jobs/management/commands/load_mboxes.py | 40 +++++ jobs/management/commands/nnp.py | 10 ++ jobs/management/commands/pop.py | 27 +++ jobs/models.py | 103 +++++++++++ jobs/tests.py | 21 +++ jobs/views.py | 1 + logs/.keep | 0 manage.py | 14 ++ requirments.pip | 3 + settings.py.template | 172 +++++++++++++++++++ test-data/job-email | 219 ++++++++++++++++++++++++ urls.py | 17 ++ 16 files changed, 629 insertions(+) create mode 100644 .gitignore create mode 100644 jobs/__init__.py create mode 100644 jobs/management/__init__.py create mode 100644 jobs/management/commands/__init__.py create mode 100644 jobs/management/commands/load_mboxes.py create mode 100644 jobs/management/commands/nnp.py create mode 100755 jobs/management/commands/pop.py create mode 100644 jobs/models.py create mode 100644 jobs/tests.py create mode 100644 jobs/views.py create mode 100644 logs/.keep create mode 100755 manage.py create mode 100644 requirments.pip create mode 100644 settings.py.template create mode 100644 test-data/job-email create mode 100644 urls.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..449b550 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +mboxes/* +*.pyc diff --git a/jobs/__init__.py b/jobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/management/__init__.py b/jobs/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/management/commands/__init__.py b/jobs/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/management/commands/load_mboxes.py b/jobs/management/commands/load_mboxes.py new file mode 100644 index 0000000..acbbc47 --- /dev/null +++ b/jobs/management/commands/load_mboxes.py @@ -0,0 +1,40 @@ +import os +import re +import urllib +import mailbox + +from django.conf import settings +from django.core.management.base import BaseCommand + +from jobs.models import JobEmail + +mbox_dir = os.path.join(settings.PROJECT_DIR, "mboxes") + +class Command(BaseCommand): + + def handle(self, *args, **options): + for mbox in mboxes(): + for msg in mailbox.mbox(mbox): + print msg['content-type'] + email = JobEmail.new_from_msg(msg) + if email: + print "loaded %s" % email + +def mboxes(): + if not os.path.isdir(mbox_dir): + os.mkdir(mbox_dir) + download_mboxes() + for filename in os.listdir(mbox_dir): + if filename.endswith("mbox"): + yield os.path.join(mbox_dir, filename) + +def download_mboxes(): + print "downloading code4lib mboxes" + opener = urllib.URLopener() + url = "http://serials.infomotions.com/code4lib/etc/mboxes/code4lib-%s.mbox" + for year in range(2004, 2012): + mbox_url = url % year + mbox_file = os.path.join(mbox_dir, "code4lib-%s.mbox" % year) + print "saving %s as %s" % (mbox_url, mbox_file) + opener.retrieve(mbox_url, mbox_file) + diff --git a/jobs/management/commands/nnp.py b/jobs/management/commands/nnp.py new file mode 100644 index 0000000..1126bc6 --- /dev/null +++ b/jobs/management/commands/nnp.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand + +from jobs.models import JobEmail + +class Command(BaseCommand): + + def handle(self, *args, **options): + for email in JobEmail.objects.all(): + for n in email.proper_nouns(): + print n.lower().encode('utf-8') diff --git a/jobs/management/commands/pop.py b/jobs/management/commands/pop.py new file mode 100755 index 0000000..17097cc --- /dev/null +++ b/jobs/management/commands/pop.py @@ -0,0 +1,27 @@ +import os +import email +import poplib +import logging + +from django.conf import settings +from django.core.management.base import BaseCommand + +from jobs.models import JobEmail + +log = logging.getLogger(__name__) + +class Command(BaseCommand): + + def handle(self, *args, **options): + log.info("checking for new emails") + gmail = poplib.POP3_SSL(settings.POP_SERVER, settings.POP_PORT) + gmail.user(settings.POP_USER) + gmail.pass_(settings.POP_PASSWORD) + + num_messages = len(gmail.list()[1]) + for i in range(num_messages): + email_txt = '\n'.join(gmail.retr(i+1)[1]) + msg = email.message_from_string(email_txt) + e = JobEmail.new_from_msg(msg) + if e: + log.info("found a new job email: %s", e) diff --git a/jobs/models.py b/jobs/models.py new file mode 100644 index 0000000..39a7800 --- /dev/null +++ b/jobs/models.py @@ -0,0 +1,103 @@ +import re +import time +import codecs +import rfc822 +import datetime +import StringIO + +import nltk + +from django.db import models + +class JobEmail(models.Model): + from_name = models.CharField(max_length=255) + from_address = models.CharField(max_length=255) + from_domain = models.CharField(max_length=255) + subject = models.TextField() + body = models.TextField() + sent_time = models.DateTimeField() + message_id = models.CharField(max_length=1024) + + def __str__(self): + return "%s -%s" % (self.from_address, self.subject) + + def proper_nouns(self): + nouns = [] + for tag in self.tags(): + word = tag[0] + is_proper_noun = tag[1] == "NNP" + is_word = re.match("^[a-z]+$", tag[0], re.IGNORECASE) + + if is_proper_noun and is_word: + nouns.append(tag[0]) + elif len(nouns) > 0: + yield " ".join(nouns) + nouns = [] + + def tags(self): + words = nltk.word_tokenize(self.body) + return nltk.pos_tag(words) + + + @classmethod + def new_from_msg(klass, msg): + if not is_job(msg): + return None + + if JobEmail.objects.filter(message_id=msg['message-id']).count() == 1: + return None + + e = JobEmail() + e.from_name, e.from_address = rfc822.parseaddr(msg['from']) + e.from_name = normalize_name(e.from_name) + e.from_address = e.from_address.lower() + e.from_domain = e.from_address.split('@')[1] + e.subject = msg['subject'] + e.message_id = msg['message-id'] + e.body = get_body(msg) + + t = time.mktime(rfc822.parsedate(msg['date'])) + e.sent_time = datetime.datetime.fromtimestamp(t) + + if not e.body: + return None + + e.save() + return e + +def normalize_name(name): + if ',' in name: + parts = name.split(',') + parts = [p.strip() for p in parts] + first_name = parts.pop() + parts.insert(0, first_name) + name = ' '.join(parts) + return name + +def is_job(msg): + if not msg['subject']: + return False + subject = msg['subject'].lower() + if re.search('^re:', subject): + return False + if re.search('job', subject): + return True + if re.search('position', subject): + return True + return False + +def get_body(msg): + charset = msg.get_content_charset() + + if not charset: + return None + + try: + codec = codecs.getreader(charset) + except LookupError: + return None + + payload = StringIO.StringIO(msg.get_payload()) + reader = codec(payload) + body = "\n".join(reader.readlines()) + return body diff --git a/jobs/tests.py b/jobs/tests.py new file mode 100644 index 0000000..a39e0d4 --- /dev/null +++ b/jobs/tests.py @@ -0,0 +1,21 @@ +import email +import unittest + +from jobs.models import JobEmail + +class JobsTests(unittest.TestCase): + + def test_email(self): + msg = email.message_from_file(open("test-data/job-email")) + e = JobEmail.new_from_msg(msg) + self.assertEqual(e.from_address, "cgowing@miami.edu") + self.assertEqual(e.from_domain, 'miami.edu') + self.assertEqual(e.from_name, 'Cheryl A. Gowing') + self.assertEqual(e.subject, '[CODE4LIB] Job Posting: Head of Web & Emerging Technologies, University of Miami - revised') + self.assertTrue('collaborates' in e.body) + self.assertTrue(e.message_id, '<7933CD19EEFCC94392323A994F6F1EDF01DBB52AE8@MBX03.cgcent.miami.edu>') + + def test_tagging(self): + msg = email.message_from_file(open("test-data/job-email")) + e = JobEmail.new_from_msg(msg) + print e.tags() diff --git a/jobs/views.py b/jobs/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/jobs/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/logs/.keep b/logs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..3e4eedc --- /dev/null +++ b/manage.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +from django.core.management import execute_manager +import imp +try: + imp.find_module('settings') # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) + sys.exit(1) + +import settings + +if __name__ == "__main__": + execute_manager(settings) diff --git a/requirments.pip b/requirments.pip new file mode 100644 index 0000000..ce619c9 --- /dev/null +++ b/requirments.pip @@ -0,0 +1,3 @@ +django +PyYAML +nltk diff --git a/settings.py.template b/settings.py.template new file mode 100644 index 0000000..5583626 --- /dev/null +++ b/settings.py.template @@ -0,0 +1,172 @@ +# Django settings for jobs4lib project. + +import os + +PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) +LOG_DIR = os.path.join(PROJECT_DIR, "logs") + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ('Ed Summers', 'ehs@pobox.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'jobs.db', # Or path to database file if using sqlite3. + 'USER': '', # Not used with sqlite3. + 'PASSWORD': '', # Not used with sqlite3. + 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3. + } +} + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# URL prefix for admin static files -- CSS, JavaScript and images. +# Make sure to use a trailing slash. +# Examples: "http://foo.com/static/admin/", "/static/admin/". +ADMIN_MEDIA_PREFIX = '/static/admin/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '=z7loor%*+!h_3c_#$&w89g9#sh7t+=xb@u-yb+=(tyd4v_^dp' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +ROOT_URLCONF = 'jobs4lib.urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'jobs4lib.jobs', + # Uncomment the next line to enable the admin: + # 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', +) + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'simple': { + 'format': '%(levelname)s %(asctime)s %(message)s' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler' + }, + 'pop': { + 'level': 'INFO', + 'class': 'logging.FileHandler', + 'formatter': 'simple', + 'filename': os.path.join(LOG_DIR, 'pop.log') + }, + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + 'jobs4lib.jobs.management.commands.pop': { + 'handlers': ['pop'], + 'level': 'DEBUG', + 'propagate': True, + } + } +} + +POP_SERVER = "pop.gmail.com" +POP_PORT = 995 +POP_USER = "" +POP_PASSWORD = "" diff --git a/test-data/job-email b/test-data/job-email new file mode 100644 index 0000000..0485aff --- /dev/null +++ b/test-data/job-email @@ -0,0 +1,219 @@ +From owner-code4lib@LISTSERV.ND.EDU Mon Jan 3 09:56:21 2011 +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.2.4 (2008-01-01) on + wilson.infomotions.com +X-Spam-Level: +X-Spam-Status: No, score=-1.5 required=5.0 tests=AWL,BAYES_00, + DNS_FROM_OPENWHOIS,FH_DATE_PAST_20XX,RCVD_IN_DNSWL_LOW autolearn=no + version=3.2.4 +Received: from mail-msa2-prod-v.cc.nd.edu (mail-msa2-prod-v.cc.nd.edu [129.74.13.11]) + by wilson.infomotions.com (8.14.1/8.14.1) with ESMTP id p03EuKsA028105 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) + for ; Mon, 3 Jan 2011 09:56:21 -0500 +Received: from lists1-prod-v.cc.nd.edu (lists1-prod-v.cc.nd.edu [129.74.250.38]) + by mail-msa2-prod-v.cc.nd.edu (Switch-3.4.3/Switch-3.4.3) with ESMTP id p03E8gME004623; + Mon, 3 Jan 2011 09:54:19 -0500 +Received: by LISTSERV.ND.EDU (LISTSERV-TCP/IP release 15.5) with spool id + 2037686 for CODE4LIB@LISTSERV.ND.EDU; Mon, 3 Jan 2011 09:54:15 -0500 +Received: from mail-mx2-prod-v.cc.nd.edu (mail-mx2-prod-v.cc.nd.edu + [129.74.250.244]) by lists1-prod-v.cc.nd.edu (8.13.1/8.13.1) with + ESMTP id p03EsFEb022824 for ; Mon, 3 Jan + 2011 09:54:15 -0500 +Received: from esa-gab.cgcent.miami.edu (esa-gab.cgcent.miami.edu + [129.171.32.91]) by mail-mx2-prod-v.cc.nd.edu + (Switch-3.4.3/Switch-3.4.3) with ESMTP id p03EsDGr021988 + (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=FAIL) for + ; Mon, 3 Jan 2011 09:54:14 -0500 +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: + AnUFANZxIU2Bq/qV/2dsb2JhbACCKaEtXnOuZoVAhnaCboJcBIRlgTGEIkyLMg +Received: from htcas-tel.cgcent.miami.edu ([129.171.250.149]) by + esa-gab.cgcent.miami.edu with ESMTP/TLS/RC4-MD5; 03 Jan 2011 09:54:12 + -0500 +Received: from MBX03.cgcent.miami.edu ([129.171.250.45]) by + HTCAS-TEL.cgcent.miami.edu ([129.171.250.149]) with mapi; Mon, 3 Jan + 2011 09:54:12 -0500 +Thread-Topic: Job Posting: Head of Web & Emerging Technologies, + University of Miami - revised +Thread-Index: AQHKNTCx83n+18wG7U+2JcX9Ml7WgZOmTdRQ +References: <7933CD19EEFCC94392323A994F6F1EDF0172C19484@MBX03.cgcent.miami.edu> +Accept-Language: en-US +X-MS-Has-Attach: +X-MS-TNEF-Correlator: +acceptlanguage: en-US +MIME-Version: 1.0 +X-Source-IP: 129.171.32.91 +X-ND-MTA-Date: Mon, 03 Jan 2011 09:54:25 EST +X-ND-CM-Score: 0.00% +X-ND-CT-Class: Not-spam +X-Spamscore-Cloudmark: 0.00% +X-Spamclassification-Commtouch: not spam +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: quoted-printable +Message-ID: <7933CD19EEFCC94392323A994F6F1EDF01DBB52AE8@MBX03.cgcent.miami.edu> +Date: Mon, 3 Jan 2011 09:56:53 -0500 +Reply-To: Code for Libraries +Sender: Code for Libraries +From: "Gowing, Cheryl A." +Subject: [CODE4LIB] Job Posting: Head of Web & Emerging Technologies, University of Miami - revised +Comments: To: "syslib-l@listserv.indiana.edu" , + "lita-l@ala.org" , + "innopac@innopacusers.org" +To: CODE4LIB@LISTSERV.ND.EDU +In-Reply-To: <7933CD19EEFCC94392323A994F6F1EDF0172C19484@MBX03.cgcent.miami.edu> +Precedence: list +List-Help: , + +List-Unsubscribe: +List-Subscribe: +List-Owner: +List-Archive: +X-Source-IP: 129.74.250.38 + +Please excuse the cross-posting. This Department Head position is a rework= +ing of a previously posted vacancy. +----------- + +The University of Miami Libraries seeks a creative, innovative individual t= +o provide leadership, direction, and technical expertise in the design, dev= +elopment, and implementation of the Libraries' web presence and promote use= +r-centered interfaces, technologies, resources, and services designed to en= +hance the user experience. + +POSITION: Reporting to the Director for Information Management & Systems, t= +he Head of Web & Emerging Technologies provides leadership, direction, and = +technical expertise in the design, development, and implementation of the L= +ibraries' web presence and promotes user-centered interfaces, technologies,= + resources, and services designed to enhance the user experience. The Web= + & Emerging Technologies Department works in close collaboration with Syste= +ms and Digital Programs and Scholarship staff, stake-holders and unit-level= + managers. + +ESSENTIAL DUTIES & RESPONSIBILITIES include the following. Other duties may= + be assigned. + +Performance: + + * Provides leadership, direction and technical expertise to design, deve= +lop, maintain, and evaluate the Libraries' web and mobile presence for all = +services, content, and interfaces + * Conceptualizes and determines technologies and design in the delivery = +of user-centered library services, integrating third party and open source = +web applications and products as appropriate. + * Administers Drupal content management system and develops, evaluates, = +and implements modules, themes, and templates and other tools to manage web= + services and facilitate web authoring by non-technical staff. + * Develops and recommends policies, standards, and guidelines for web co= +ntent development, implementation, and management in collaboration with Lib= +rary and University stakeholders. + * Monitors and evaluates web services usage statistics; Oversees usabili= +ty of all user interfaces and web design and develops assessment strategies= + to provide a superior experience for all users. + * Supervises and mentors of Web & Emerging Technologies staff, which cur= +rently includes the Web Administrator, Digital Technologies Programmer, Web= + Applications Programmer, and student assistants. +Service + + * Networks, collaborates and actively participates in local, regional, n= +ational, or international organizations regarding related issues. + * Represents and promotes the University of Miami Libraries in local, st= +ate-wide, regional, national, or international organizations as appropriate= +. + * Serves as the Library representative on University web committees and = +task forces and is an ex-officio member of the Libraries' Student Advisory;= + participates in other Library and University committees, etc. as appropria= +te. + +QUALIFICATIONS + +Required: +* Master of Library Science degree from an ALA accredited program o= +r foreign equivalent. +* Three to five years demonstrated experience with web and database= + development & design in an academic environment using standards-based, use= +r-centered information architecture. +* Experience creating/maintaining web sites with relevant standard= +s and technologies, including demonstrated proficiency in PHP programming, = +CSS, SQL, and Javascript. +* Demonstrated experience with current trends and issues in public = +computing, web technologies, digital media, and quantitative and qualitativ= +e usability assessment. +* Strong leadership skills and ability to work independently, colla= +boratively, and in teams within the Library and University Web communities. +* Demonstrated project management, organizational, analytical, and = +problem solving skills. +* Demonstrated effective oral, written, and interpersonal communica= +tion skills. +* Demonstrated commitment to user-centered library service and the = +ability to work flexibly and creatively in a changing and fast-paced enviro= +nment with a culturally diverse population. +* Evidence of continued professional development, involvement, and = +contribution. + +Desired: + +* Supervisory experience + +* Working knowledge of web content management systems [Drupal prefe= +rred] and relational databases. +* Familiarity with digital media technology and providing access to= + digitized audio, video and images. + +* Experience with the implementation of APIs/Web services, and/or m= +obile interface design +* Understanding of information seeking behaviors in an academic res= +earch environment + +SALARY AND BENEFITS: Compensation will be competitive and commensurate with= + experience and qualifications. This is a non-tenure track faculty appointm= +ent at the Librarian Assistant/Associate Professor rank. The position offer= +s a comprehensive benefits package including: TIAA-CREF; medical and dental= + insurance; life, disability, and long-term care insurance available; tuiti= +on remission; 13 paid holidays; and 22 days annual vacation. Additional emp= +loyment benefits available include credit union; Employee Assistance Progra= +m; bookstore, and sporting event discounts; optional fee-based membership i= +n a state-of-the-art wellness center, and no state or local income taxes. M= +ore information on benefits can be found at: http://www.miami.edu/benefits= +/pdf/bensum-faculty06.pdf. + +APPLICATIONS AND NOMINATIONS: Review of applications will begin immediately= + and continue until the position is filled. Applications and nominations wi= +ll be accepted until a suitable candidate is selected. Applications should = +be submitted electronically and must include a letter of interest, curricul= +um vitae and the names of three references. The references will not be cont= +acted before the appropriate time. Send nominations and applications to: + +Marylen Exposito, Human Resources Manager +Otto G. Richter Library +University of Miami +P.O. Box 248214 +Coral Gables, FL 33124-0320 +e-mail: richter.recruiting@miami.edu + +UNIVERSITY: The University of Miami is one of the nation's leading research= + universities in a community of extraordinary diversity and international v= +itality. The University is privately supported, non-sectarian institution, = +located in Coral Gables, Florida, on a 260-acre subtropical campus. The Uni= +versity comprises 11 degree granting schools and colleges, including Archit= +ecture, Arts and Sciences, Business Administration, Communication, Educatio= +n, Engineering, Law, Medicine, Music, Nursing, and Marine and Atmospheric S= +cience (www.miami.edu). + +THE LIBRARY: The University of Miami Libraries (www.library.miami.edu) rank among the top 50 research libraries in North= + America with a collection of over 3.3 million volumes and 80,830 current s= +erials titles, including 76,840 electronic journals. The Otto G. Richter Li= +brary lies in the heart of the Coral Gables campus and serves as the centra= +l library for the University. Other University of Miami libraries include t= +he Paul Buisson Architecture Library, the Judi Prokop Newman Business Infor= +mation Resource Center, the Marta & Austin Weeks Music Library, and the Mar= +ine and Atmospheric Science Library. The University also has independent me= +dical and law libraries. The Libraries provide support and services for app= +roximately 10,368 undergraduates, 5,289 graduate students, and 13,464 full = +and part time faculty and staff. Excluding the independent libraries, the U= +niversity Libraries employ 56 professional and 90 support staff and are a m= +ember of ARL, ASERL, CLIR, NERL, RLG, and Lyrasis. + +The University of Miami is an Equal Opportunity Affirmative Action Employer= +. The University has a strong commitment to diversity and encourages applic= +ations from candidates of diverse cultural backgrounds. diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..7e58cc4 --- /dev/null +++ b/urls.py @@ -0,0 +1,17 @@ +from django.conf.urls.defaults import patterns, include, url + +# Uncomment the next two lines to enable the admin: +# from django.contrib import admin +# admin.autodiscover() + +urlpatterns = patterns('', + # Examples: + # url(r'^$', 'jobs4lib.views.home', name='home'), + # url(r'^jobs4lib/', include('jobs4lib.foo.urls')), + + # Uncomment the admin/doc line below to enable admin documentation: + # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line to enable the admin: + # url(r'^admin/', include(admin.site.urls)), +)