PythonでZIPファイル作成後、メール添付送信

## はじめに
初学者がPythonを勉強しています。
間違っている内容や改善などご教示いただければ幸いです。

## 目的
（共通）Pythonを学習するにあたり、まずは身近にある業務の省人化を目的とする。
（今回）メールは送受信する必要があり、「自動送信、できれば添付ファイル送信できるといいよね。」
→通常業務でまだ残るZIP,PW文化。仕方ないからZIP,PW添付させよう。
　PW部は処理重複するため、一部抜粋とした

## 目標
基本的なコード理解と実際の成功体験を得る
継続的な学習へ繋げる
理解力の深掘り、さらなるコード改善に繋がればなおよし

## 流れ
1. あるフォルダの中に存在する全ファイルからZIPファイルを生成
2. ZIPファイルを添付し、メール送信文書を作成
3. 送信内容を確認後、自動送信
4. HAPPY！

:::note warn  
＊22.11.08時点 残念ながら現在はメール内容はテンプレのみ。初学者ですので。。。
:::

## 環境
Windows10
Python 3.8.3 (memo: python -V 

## 参考にさせていただいたサイト、記事など
大変参考になりました。感謝いたします。

https://qiita.com/yujikawa/items/1f72f2171d84cf858c3b

https://qiita.com/tarao1006/items/d257299e3d202fbf4460

https://qiita.com/Kta-M/items/afa94a6728416511b78b


## 全体コード
以下、全体コード

```python:sendmail_zip.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
import win32com.client

#get Date
import datetime
import time

import os
import zipfile
import shutil

#zippw
import pyminizip
import string
import secrets

#GUI : message box
from tkinter import messagebox
import tkinter as tk
import sys

# アカウント
my_account = 'hogehoge1'
my_password = 'hogehoge2'
mail_address = 'hogehoge3'

#メール送信func
def send_outlook_mail(msg,body,src_file_names):
    try:
        # Check GUI
        root = tk.Tk()
        root.withdraw()

        # Check Contents & Attachment
        ret = messagebox.askyesno('Check','-----------------------------件名-----------------------------\n\n'+str(msg['Subject'])+'\n\n-----------------------------To-----------------------------\n\n'+str(msg['To'])+'\n\n-----------------------------Bcc-----------------------------\n\n'+str(msg['Bcc'])+'\n\n-----------------------------本文-----------------------------\n\n'+body+'\n\n---------------------添付ファイル-----------------------------\n\n'+str(src_file_names))

        # もし確認内容が異なるなら、終了
        if ret == False:
            sys.exit()

        """
        引数msgをOutlookで送信
        """
        server = smtplib.SMTP('  ', portnum)
        server.ehlo()
        server.starttls()
        server.ehlo()
        # ログインしてメール送信
        server.login(my_account, my_password)

        #Send mail
        server.send_message(msg)
        print('send OK')
        server.quit()
    
    #Exception
    except Exception as E:
        print("***** エラー内容  *****")
        print("type: "+ str(type(E)))
        print("args: " + str(E.args))
        print("message: " + E.message)
        print("E: " + str(E))

        print('False')
        return False
        
def make_mime(mail_to, mail_bcc, subject, body):
    """
    引数をMIME形式に変換
    """
    msg = MIMEMultipart() #メッセージ本文
    msg['Subject'] = subject #件名
    msg['To'] = mail_to #宛先
    msg['Bcc'] = mail_bcc #宛先    
    msg['From'] = mail_address #送信元
    msg.attach(MIMEText(body))

    print('Check make_mime')
    return msg

#ファイル添付func
def func_attachment(zip_file_name,msg):

   # attachment zip file
    attach = MIMEBase('application','zip')
    with open(zip_file_name, "br") as f:
        attach.set_payload(f.read())
    encoders.encode_base64(attach)
    attach.add_header('Content-Disposition', 'attachment', filename=today + 'attachment.zi_')
    msg.attach(attach)

    print('Finish Attachment')

#PW生成
def generate_pw():

    #Complex PW
    pass_chars = string.ascii_letters + string.digits +　string.punctuation
    password = ''.join(secrets.choice(pass_chars) for x in range(10))
    return password

#ファイル一覧取得
def get_file_path(file_path):
    """
    ファイルパスから指定以下のファイルの一覧を取得する
    :param file_path:ファイルパス
    :return: generator
    """
    if os.path.isfile(file_path):
        yield file_path
    else:
        for (base_dir, _ , file_name_list) in os.walk(file_path):
            for file_name in file_name_list:
                path = os.path.join(base_dir, file_name)
                path = path.replace(os.sep, '/')
                yield path

#zipファイル作成
def zipping(file_path, save_dir,pw):
    """
    ファイル及びフォルダごとZIP化関数
    :param file_path: 圧縮対象のファイルおよびディレクトリ
    :param save_dir: 保存先（デフォルトは圧縮対象と同じ階層）
    :return:
    """
    if file_path[-1]==os.sep or file_path[-1]=="/" :
        file_path = file_path[:-1]

    if not os.path.isdir(file_path) and not os.path.isfile(file_path):
        print("Not Found : {}".format(file_path))
        raise FileNotFoundError

    if os.path.isdir(save_dir):
        save_dir = os.path.join(save_dir, os.path.basename(file_path))
    else:
        save_dir = file_path

    zip_file_name = "{}.zip".format(save_dir)
    print('zip file : {}'.format(zip_file_name))

    src_file_names = ["%s\\%s" % (file_path, name) for name in os.listdir(file_path)]

    #パスワード付きZIP File generated..
    print('Check len(src_file_names)->.....   ' + str(len(src_file_names)))

    #pfx_file_names = ['' for i in len(src_file_names)]
    #for prefix apeend ''
    pfx_file_names =[]
    for i in range(len(src_file_names)):
        pfx_file_names.append('')

    print('Check pfx_file_names->.....   ' +str(pfx_file_names))

#ZIP圧縮 pyminizip.compress_multiple(src_file_names,pfx_file_names,zip_file_name.encode('cp932'),pw.encode('cp932'),int(0))
    print('zip compressed')

    '''
    one file only -> OK!
    pyminizip.compress(str(file_name),'',str(zip_file_name),"hogehoge", int(0))
    '''

    print('Finish zipping')
    print('zip_file_name: '+ zip_file_name+ '\n')

    return zip_file_name

#メイン処理部
def my_message(i,pw):

    #Make Zipfile.
    file_path = ''

    #Path Check(~/Desktop )
    save_path = os.path.expanduser('~/Desktop')
    print('save_path->.....  '+ save_path)

    #i=1 -> Mainメッセージ, i=2 -> PW部
    if i ==1:
        #for send_outlook_mail(check attachmentfile)
        src_file_names = ["%s\\%s" % (file_path, name) for name in os.listdir(file_path)]

　　　　#ファイル名で本文を変更
        #aaa ある場合-> aaaフォーム
        #bbb ある場合-> bbbフォーム
        l_in = [s for s in src_file_names if 'aaa' in s]
        l_in = [s for s in src_file_names if 'bbb' in s]        

        # MIME形式に変換
        if 'aaa' in str(l_in):　　　　　　 
            body=' *******　This mail is AutoSend:'+ now +' ....   '

            msg = make_mime(
                mail_to='', #送信したい宛先を指定
                mail_bcc =mail_address,
                subject='',body=body )

        elif 'bbb' in str(l_in):
            body=' *******　This mail is AutoSend:'+ now +'...  '

            msg = make_mime(
                mail_to='', #送信したい宛先を指定
                mail_bcc =mail_address,
                subject='',body=body)

        else:
            body=' *******　This mail is AutoSend:'+ now +'...    '

            msg = make_mime(
                mail_to='', #送信したい宛先を指定
                mail_bcc =mail_address,
                subject='',body=body)
            
        #zip処理させる関数
        file = zipping(file_path,save_path,pw)

        #fileを添付させる関数
        func_attachment(file,msg)

    #send mail
    result =　send_outlook_mail(msg,body,src_file_names)

    #ファイルの有無確認後、あれば削除
    if os.path.isfile(save_path+hoge.zip):
        os.remove(save_path+'\\tmp.zip')

#メイン
if __name__ == '__main__':

    #PW自動生成
    pw = generate_pw()

    #Send main contents （i==1）
    my_message(1,pw)

    #PW送信（i==2）
    my_message(2,pw)

```
## 今後の課題（雑感多め）
【全体】
可続性、メンテ性を考慮した記述方式を確立（他者へ説明できるレベルで自分なりの納得）

【細かい箇所】
コメントアウトのクオリティ（誰が見てもすぐ判断がつくように。）
メール本文などを添付ファイル名称からテンプレ（DB）識別させた方が良い。
今回はOUTPUT優先。
（保留）C言語との違いもあり、さらなる理解が必要（ただし現状はOUTPUTを先行
（保留）if分のエラー処理の記述内容が明確に理解できていない所もある。
        エラー内容は、エラーを吐いた時点で学習していく）
（保留）現状はメール送信前の確認インタフェースで間違いが無いか確認しているが、他のスマートな手法は？

## 次の展開
目的に沿って、次の業務改善に繋がるプログラムを作成する
OUTPUT継続

## 最後に
長文、乱文となりましたがお付き合いありがとうございました。