일정이 있는 아웃룩 메일의 경우 일정을 자동으로 생성하는 프로그램을 만들기.
1. 사용 프로그램: 파이썬
아웃룩 제어를 위해 Microsoft Windows용 파이썬 API인 win32com.client을 사용하였으며 VBA 자체 코딩은 없음.
2. 일정 자동 생성 하기 (Appointment)
1)아웃룩에서 메일을 선택하고 읽기 방법
-. win32com.client(VBA)의 Selection object 사용
import win32com.client
ol_Exp = win32com.client.Dispatch("Outlook.Application").ActiveExplorer() # 현재 선택된 폴더명.
ol_Sel_count = ol_Exp.Selection.Count # 아웃룩에서 선택된 메일의 숫자
for idx in range(1, ol_Sel_count+1): # 선택된 메일 순차적으로 읽기
ol_mail = ol_Exp.Selection.Item(idx)
mail_txt = ol_mail.Subject + '\n' + ol_mail.Body
# 메일 제목과 메일 본문을 분석을 위해서 별도 저장
# MailItem object의 상세설명은 MS의 VBA reference 참조
# https://docs.microsoft.com/en-us/office/vba/api/overview/outlook/object-model
2) 메일 문자열에서 날짜/시간에 관련된 문자 찾아서 datetime형식으로 리턴
-. 정규표현식을 이용하여 년/월/일 (000년 00월 00일, 00월/00일, 00.00/00 등) 찾기
-. 정규표현식을 이용하여 시/분 (00시00분, 00:00 등) 찾기
-. 년월일과 시분을 합쳐서 datetime 형식으로 리턴함.
import re #정규표현식 package
import datetime #날짜시간 관련 package
import dateutil.parser as dparser #문자열을 datetime형식으로 변경 pacage
# 정규 표현식을 별도의 sub로 선언
def datetime_complie_set() -> list:
date_time_com_set = []
# 년월일 파악 정규식
date_time_com_set.append(re.compile(r'\d{2,4}\s?[.년/,-]\s?\d{1,2}\s?[.,/-]\s?\d{1,2}'))
date_time_com_set.append(re.compile(r'\d{2,4}\s?[.년/,]\s?\d{1,2}\s?[월]\s?\d{1,2}\s?[일]'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[./-]\s?\d{1,2}\s?'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[월]\s?\d{1,2}\s?[일]'))
# 시분 파악 정규식
date_time_com_set.append(re.compile(r'[오AaPp][전후Mm]\s?\d{1,2}\s?[시:;]\s?\d{0,2}'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[:시]\s?\d{0,2}'))
return date_time_com_set
def return_datetime_in_str(strings: str, date_time_re_com: list, referdate: datetime.date):
#strings: str, : 분석할 문자열
#date_time_re_com: list, : 사전 정의 정규표현식
#referdate: datetime.date : 리퍼런스 날짜시간, 시분만 있고 년월일이 없을 경우 메일 수신 날짜로 일정을 저장하기 위해서 필요
#코드 상세 내용 첨부 참조
return schedule
dt_re_cpl_set = datetime_complie_set() # 정규 표현식 set
rf_dt = ol_mail.ReceivedTime # 메일 수신 날짜
schdl_dt = return_datetime_in_str(sent, dt_re_cpl_set, rf_dt.date()) # 문자열을 datatime 형식으로 리턴함.
3) 아웃룩 일정표에 기록하기
-. CreateItem(1) 사용하여 새 일정을 만듬 (0: 메일, 1:일정, 2:주소록, 3:작업 등등)
ol_appoint_item = win32com.client.Dispatch("Outlook.Application").CreateItem(1) # 새일정 만들기
ol_appoint_item.Subject = ol_mail.Subject # 일정제목
ol_appoint_item.Start = schdl_dt + datetime.timedelta(hours=9) # 일정의 시작시간
ol_appoint_item.Body = mail_body # 일정 본문
ol_appoint_item.save() # 일정 저장
3. 기타 사항 (첨부 파일)
- 오전/오후/내일/명일등의 문자 처리
- 분석한 날짜가 오늘 날짜와 차이가 많이 나는 경우 예외 처시
- 년도가 없거나 2자리 일 경우 메일 수신 년도로 대체
- 시/분 정보만 있고 년/월/일 정보가 없을 경우 처리 - 메일 수신 날짜 또는 +1 day 처리
※불필요한 송신자 정보, 회신 메일 정보 등을 지우는 전처리 프로그램 필요..
아웃룩 보낸 메일함에서 메일 선택 후 파이썬 실행!!!
import win32com.client
import datetime
import tkinter
import os
import re
import dateutil.parser as dparser
def datetime_complie_set() -> list:
date_time_com_set = []
date_time_com_set.append(re.compile(r'\d{2,4}\s?[.년/,-]\s?\d{1,2}\s?[.,/-]\s?\d{1,2}'))
date_time_com_set.append(re.compile(r'\d{2,4}\s?[.년/,]\s?\d{1,2}\s?[월]\s?\d{1,2}\s?[일]'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[./-]\s?\d{1,2}\s?'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[월]\s?\d{1,2}\s?[일]'))
date_time_com_set.append(re.compile(r'[오AaPp][전후Mm]\s?\d{1,2}\s?[시:;]\s?\d{0,2}'))
date_time_com_set.append(re.compile(r'\d{1,2}\s?[:시]\s?\d{0,2}'))
return date_time_com_set
def date_str_modi(date: str) -> str:
temp_date = date
temp_date = temp_date.replace('년', '-')
temp_date = temp_date.replace('월', '-')
temp_date = temp_date.replace('일', ' ')
while temp_date.__contains__(" "):
temp_date = temp_date.replace(" ", '')
while temp_date.__contains__('/'):
temp_date = temp_date.replace('/', '-')
while temp_date.__contains__('.'):
temp_date = temp_date.replace('.', '-')
return temp_date
def time_str_modi(time: str) -> str:
temp_time = time
temp_time = temp_time.replace('오전', 'AM')
temp_time = temp_time.replace('오후', 'PM')
temp_time = temp_time.replace('시', ':0')
temp_time = temp_time.replace('분', ':0')
temp_time = temp_time.replace('초', ' ')
return temp_time
def return_datetime_in_str(strings: str, date_time_re_com: list, referdate: datetime.date):
strings = strings.lower()
date_ymd_en_complie = date_time_re_com[0]
date_ymd_kr_complie = date_time_re_com[1]
date_md_en_complie = date_time_re_com[2]
date_md_kr_complie = date_time_re_com[3]
time_12_complie = date_time_re_com[4]
time_24_complie = date_time_re_com[5]
date_ymd_srch = date_ymd_en_complie.search(strings)
if date_ymd_srch:
pass
else:
date_ymd_srch = date_ymd_kr_complie.search(strings)
if date_ymd_srch:
year_2d_chk = re.search(r'^\d{2}[.년/]', date_ymd_srch.group())
if year_2d_chk:
date_4ymd_str = '20' + date_ymd_srch.group()
else:
date_4ymd_str = date_ymd_srch.group()
else:
date_ymd_srch = date_md_en_complie.search(strings)
if date_ymd_srch:
date_4ymd_str = str(referdate.year) + '-' + date_ymd_srch.group()
else:
date_ymd_srch = date_md_kr_complie.search(strings)
if date_ymd_srch:
date_4ymd_str = str(referdate.year) + '-' + date_ymd_srch.group()
elif strings.__contains__('내일') or strings.__contains__('명일'):
date_4ymd_str = str(referdate + datetime.timedelta(days=1))
else:
date_4ymd_str = ""
is_date_info = False
''' find time : hour, minute '''
is_time_info = True
time_srch = time_12_complie.search(strings)
if time_srch:
pass
else:
time_srch = time_24_complie.search(strings)
if time_srch:
pass
else:
is_time_info = False
if is_time_info:
time_str = time_srch.group()
else:
time_str = ""
# change data type striing to datetime
date_m = date_str_modi(date_4ymd_str)
time_m = time_str_modi(time_str)
date_and_time = date_m + ' ' + time_m
# 문자열이 datetime으로 형 변환이 될 경우
try:
schedule = dparser.parse(date_and_time, fuzzy=True)
if strings.__contains__('오후') or strings.__contains__('pm'):
schedule = schedule + datetime.timedelta(hours=12)
diff_dt = schedule - datetime.datetime.today()
if not (-5< diff_dt.days < 60): # 계산한 일정이 오늘 날짜와 차이가 많이 나는 경우 한번더 연산
strings = strings.replace(date_ymd_srch.group(), '')
date_4ymd_str, time_str = '', ''
schedule, date_4ymd_str, time_str = return_datetime_in_str(strings, date_time_re_com, referdate)
# datetime 형 변환시 에러가 발생하는 경우 한번 더 연산
except:
if date_ymd_srch:
strings = strings.replace(date_ymd_srch.group(), '')
if time_srch:
strings = strings.replace(time_srch.group(), '')
if date_ymd_srch or time_srch:
date_4ymd_str, time_str = '', ''
schedule, date_4ymd_str, time_str = return_datetime_in_str(strings, date_time_re_com, referdate)
else:
schedule = None
return schedule, date_4ymd_str, time_str
schedule_voca_set =['교육일정','기한','까지','설명회','송부','실시','연기','요청','일 시','일 정','일시','일정',
'점검회','회신','회신기한','회신일','회신일자','회의일정','공유회', '마감', '지급', '회의']
cwd = os.getcwd()
dt_re_cpl_set = datetime_complie_set()
current = datetime.datetime.today()
outlook_app = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
ol_Exp = win32com.client.Dispatch("Outlook.Application").ActiveExplorer()
ol_Sel_count = ol_Exp.Selection.Count
for idx in range(1, ol_Sel_count+1):
ol_mail = ol_Exp.Selection.Item(idx)
mail_txt = ol_mail.Subject + '\n' + ol_mail.Body
mail_body = ol_mail.Body
rf_dt = ol_mail.ReceivedTime
schdl_dt = None
for sent in mail_txt.split(sep='\n'):
# 메일의 문장에서 일정에 관련되는 단어를 찾아서 datetime으로
for vc in schedule_voca_set:
if sent.__contains__(vc):
schdl_dt, date_str, time_str = return_datetime_in_str(sent, dt_re_cpl_set, rf_dt.date())
if not schdl_dt:
sent = sent.replace(date_str[-5:], ' ')
schdl_dt, date_str, time_str = return_datetime_in_str(sent, dt_re_cpl_set, rf_dt.date())
if schdl_dt:
break # 3번재 for문
# 일정 날짜가 있다면 일정(appointment)를 새로 만들어서 저장함.
if schdl_dt:
ol_appoint_item = win32com.client.Dispatch("Outlook.Application").CreateItem(1)
ol_appoint_item.Subject = ol_mail.Subject
if schdl_dt.time().hour == 0:
ol_appoint_item.AllDayEvent = True
ol_appoint_item.Start = schdl_dt + datetime.timedelta(hours=9) # 한국 타임존 보정
mail_body = "------ This is Predicted! ------ \n일 정: " + str(schdl_dt) + \
"\n수신시간: " + str(ol_mail.ReceivedTime + datetime.timedelta(hours=0)) + \
'\n범 주: ' + ol_mail.Categories + '\n실행시간: ' + str(current) + \
'\n\n ----- 메일 본문 -----\n' + mail_body
ol_appoint_item.Body = mail_body
ol_appoint_item.save()
break # 2번재 for 문
disp_text = '제목: '+ ol_mail.Subject + '\n\n' +'범주: '+ ol_mail.Categories + '\n\n' +'약속시간: ' +str(schdl_dt)
disp_text += '\n\n메일본문\n'+ mail_body
disp_box = tkinter.Tk()
disp_box.title('('+ str(idx)+'/'+str(ol_Sel_count)+') ' + ol_mail.Subject)
disp_box.resizable(True, True)
t = tkinter.Text(disp_box, height=50, width=100)
t.insert(tkinter.END, disp_text)
t.pack()
tkinter.mainloop()
파이썬으로 하는 outlook : 메일 분류 학습(1) (0) | 2022.02.18 |
---|