Another abandoned server code base... this is kind of an ancestor of taskrambler.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

302 lines
9.2 KiB

# $Id: contactlog.py,v 1.1 1998/07/31 03:59:59 connolly Exp $
# This code sort of works, at least to demonstrate python/Outlook
# integration. I got through two of the categories of stylized
# data that I keep in my Pilot Address book (caller-id info,
# and business cards; the others are email, post, etc.)
# Then I got bored.
# Especially watch out for stuff marked with @@.
#
# But I'm documenting it carefully,
# because I've been looking for these clues for
# *months* and I don't want to lose them.
# And I'm releasing it. Share and Enjoy.
# Dan Connolly
# <connolly@w3.org>
# http://www.w3.org/People/Connolly/
# Copyright © 1998 World Wide Web Consortium,
# (Massachusetts Institute of Technology, Institut National de
# Recherche en Informatique et en Automatique, Keio University). All
# Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software
# and its documentation for any purpose and without fee or
# royalty is hereby granted, per the terms and conditions in
#
# W3C IPR SOFTWARE NOTICE
# http://www.w3.org/COPYRIGHT
# September 1997
# This module depends on standard and built-in python modules, per
# Python Library Reference
# April 14, 1998
# Release 1.5.1
# http://www.python.org/doc/lib/lib.html
import string, StringIO
# It also uses the win32com package
# maintained by Mark Hammond
# http://www.python.org/windows/win32com/
#
# specifically, as documented in
# PythonWin Help
# Help file built: 04/26/98
# available in http://www.python.org/windows/win32all/win32all.exe
# Here we bind to the the actual Outlook 98 runtime,
# whose interface is documented in a
# Microsoft help file, "Microsoft Outlook Visual Basic"
#
# I found the help file via the Microsoft Knowledge Base:
#
# "OL98: How to Install Visual Basic Help"
# Last reviewed: April 1, 1998
# Article ID: Q183220
# http://support.microsoft.com/support/kb/articles/q183/2/20.asp
#
# The following lines are generated by makepy
#
#Outlook 98 Type Library
# {00062FFF-0000-0000-C000-000000000046}, lcid=0, major=8, minor=5
# Use these commands in Python code to auto generate .py support
from win32com.client import gencache
outlook = gencache.EnsureModule('{00062FFF-0000-0000-C000-000000000046}',
0, 8, 5)
# The only namespace supported in Outlook '98
# see "Microsoft Outlook Visual Basic"
MAPI = "MAPI"
# Hard-coded configuration
# probably should use sys.argv, but I didn't bother...
Inf = "C:\\winnt\\profiles\\connolly\\desktop\\980728pilot-addr.txt"
# for other folks to do testing, here are a couple lines from that file:
_test_data = \
"""Last Name First Name Job Title Company Business Phone Home Phone Business Fax Other Phone E-mail Street Address City State Zip/Postal Code Country Location Birthday User Field 1 User Field 2 User Field 3 Private Categories
"Abrahamson" "Dr. David M" "+353-1-608-1716" "cavid@cs.tcd.ie" "Trinity College" "Dublin 2, Ireland" "1996-11" "lunch 96-11-13 ISO HTML Boston lunch" "0" "Business Card"
"""
def main():
oapp = Pilot2Outlook()
#infp = open(Inf)
infp = StringIO.StringIO(_test_data)
i = 0
while 1:
row = nextrow(infp)
if not row: break
i = i + 1
if i > 500: break #@@ can't figure out how
# to break a runaway script in PythonWin,
# so I added this little hack
print "@@add:", row
oapp.addPilotAddress(row)
# outlook.Application is the Application class, per
# the type library and help file cited above.
#
# It integrates completely seamlessly into the python
# object model.
#
# Hence, for methods such as GetNamespace, see
# the help file. I found the following article quite
# helpful as well. The examples are in visual basic, but
# the translation to python is straightforward.
#
# The Microsoft Outlook 97 Automation Server Programming Model
# Last Updated: June 26, 1998
# http://www.microsoft.com/OutlookDev/Articles/Outprog.htm
#
class Pilot2Outlook(outlook.Application):
def findContact(self, filter):
contacts = self.GetNamespace(MAPI).GetDefaultFolder(outlook.OlDefaultFolders.olFolderContacts)
return contacts.Items.Find(filter)
def incomingCall(self, name, num, isotime):
# convert caller-id format: 333-555-1212
# to Outlook format: (333) 555-1212
if len(num) == 12:
num = "(%s) %s" % (num[:3], num[4:])
ji = self.CreateItem(outlook.OlItemType.olJournalItem)
ji.Type = "Phone call" # set icon?
# stuff the original caller-id data away somewhere
# I'm not confident this UserProperties code
# works... wierdness around byRef args or something.
# I could never
# find the userproperties for a journal item
# in the Outlook GUI to check one way or the other.
p = ji.UserProperties.Add("CallerIDName", outlook.OlUserPropertyType.olText)
p.Value = name
p = ji.UserProperties.Add("CallerIDNumber", outlook.OlUserPropertyType.olText)
p.Value = num
#@@ mobile, other phone numbers...
contact = self.findContact('[Business Phone] = "%s" or [Home Phone] = "%s"' % (num, num))
if contact:
if contact.FullName:
name = contact.FullName
ji.Companies = contact.CompanyName
else:
contact = self.CreateItem(outlook.OlItemType.olContactItem)
contact.FullName = name
contact.BusinessTelephoneNumber = num
contact.Save()
ji.Recipients.Add(name)
t = iso2vb(isotime)
ji.Start = t
ji.Subject = "Call from %s %s" % (name or num, t)
ji.Save()
#ji.Display() #@@
return ji, contact
def logBusinessCard(self,
family,given,title,company,work,fax,email,
street,city,state,zip,country,isodate,note):
contact = self.findContact(
'[Last Name] = "%s" and [First Name] = "%s" and [Company] = "%s"' % (family, given, company))
if not contact:
print "@@ new contact"
contact = self.CreateItem(outlook.OlItemType.olContactItem)
contact.LastName = family
contact.FirstName = given
contact.CompanyName = company
contact.JobTitle = title
contact.BusinessTelephoneNumber = work
contact.BusinessFaxNumber = fax
contact.BusinessAddressStreet = street
contact.BusinessAddressCity = city
contact.BusinessAddressState = state
contact.BusinessAddressPostalCode = zip
contact.BusinessAddressCountry = country
#@@ don't clobber email
contact.Email1Address = email
if isodate:
ji = self.CreateItem(outlook.OlItemType.olJournalItem)
ji.Start = iso2vb(isodate)
ji.Type = "Conversation" # or perhaps meeting?
ji.Recipients.Add(contact.FullName)
ji.Companies = company
ji.Body = note
ji.Subject = "Business Card %s %s" % (contact.FullName, company)
#ji.Display() #@@
#@@ include scanned image of card?
ji.Save()
else:
ji = None
if note:
if contact.Body:
contact.Body = contact.Body + '\n' + note
else:
contact.Body = note
contact.Save()
return ji, contact
def addPilotAddress(self, row):
family,given,title,company,work,home,fax,other,email, \
street,city,state,zip,country,airport,birthday, \
updated,expired,note,private,category = row
if category == "Caller-id":
ji, c = self.incomingCall(family, work, updated)
if note:
ji.Body = note
#@@ translate private to Sensitivity
ji.Save()
if given or title or company or home \
or fax or other or email or street \
or city or state or zip or country or airport \
or birthday or expired:
print "@@info besides family, work, updated, note, private:",\
row
elif category == "Business Card":
if updated[-5:] == " card":
updated = updated[:-5]
ji, c = self.logBusinessCard(family,given,title,company,
work,fax,email,
street,city,state,zip,country,updated,note)
ji.Display() #@@debugging
c.Display()
#@@ other fields: home phone, airport?
# birthday, expired, private
else:
print "@@category not yet supported:", category, family, given, company
def iso2vb(isotime):
# YYYY-MM-DDTHH:MM:SS
# raises ??? exception when parts are missing?
year = isotime[:4]
month = isotime[5:7]
day = isotime[8:10] or '1' ## @# only month given
hour = isotime[11:13]
minute = isotime[14:16]
second = isotime[17:19]
ret = month + '/' + day + '/' + year
if hour and minute:
ret = ret + ' ' + hour + ':' + minute
if second:
ret = ret + ':' + second
print "@@convdate: ", isotime, ret
return ret
def nextrow(fp):
# A simple state-machine implementation of Windows
# tab-delimited file format with quoted fields.
# I'm not sure if this really
# does CRLF's right.
# There are faster ways to do this, I'm sure...
s = 'start'
row = ()
field = ""
while 1:
c = fp.read(1)
if not c:
if field or len(row):
raise IOError, "bad end of file"
return None
if s == 'start':
if c == "\n":
row = row + (field,)
return row
elif c == "\t":
row = row + (field,)
field = ''
elif c == '"':
s = 'quoted'
else:
field = field + c
elif s == 'quoted':
if c == '"':
s = 'start'
elif c == "\\":
s = 'escaped'
else:
field = field + c
elif s == 'escaped':
field = field + c
s = 'quoted'
else:
raise RuntimeError, 'bad case'
if __name__ == '__main__': main()