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
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()
|