mirror of
https://github.com/hoshikawa2/ai_logistic_outlook_integration.git
synced 2026-07-10 00:54:21 +00:00
first commit
This commit is contained in:
233
files/app_emails.py
Normal file
233
files/app_emails.py
Normal file
@@ -0,0 +1,233 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from flask import Flask, render_template, request, send_file, redirect, url_for, render_template_string
|
||||
import oracledb, json
|
||||
from datetime import datetime
|
||||
import io
|
||||
import oci
|
||||
import urllib.parse
|
||||
import extract_msg
|
||||
|
||||
# ============================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================
|
||||
with open("config", "r") as f:
|
||||
config_data = json.load(f)
|
||||
|
||||
WALLET_PATH = config_data["WALLET_PATH"]
|
||||
DB_ALIAS = config_data["DB_ALIAS"]
|
||||
USERNAME = config_data["USERNAME"]
|
||||
PASSWORD = config_data["PASSWORD"]
|
||||
|
||||
connection = oracledb.connect(
|
||||
user=USERNAME,
|
||||
password=PASSWORD,
|
||||
dsn=DB_ALIAS,
|
||||
config_dir=WALLET_PATH,
|
||||
wallet_location=WALLET_PATH,
|
||||
wallet_password=PASSWORD,
|
||||
)
|
||||
|
||||
# OCI client configuration (same standard as process_emails_v2_en.py)
|
||||
BUCKET_PROFILE = config_data["bucket-profile"]
|
||||
BUCKET_NAME = config_data["bucket"]
|
||||
COMPARTMENT_ID = config_data["compartment_id"]
|
||||
bucket_name = BUCKET_NAME + "/processed"
|
||||
|
||||
# Initialize OCI client and get namespace
|
||||
config = oci.config.from_file("~/.oci/config", BUCKET_PROFILE)
|
||||
object_storage = oci.object_storage.ObjectStorageClient(config)
|
||||
namespace = object_storage.get_namespace().data
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# ============================================================
|
||||
# HELPER FUNCTIONS
|
||||
# ============================================================
|
||||
def get_distinct_status():
|
||||
"""Return all distinct statuses + 'Forwarded Email' option."""
|
||||
with connection.cursor() as c:
|
||||
c.execute("SELECT DISTINCT STATUS FROM PROCESSED_EMAILS ORDER BY STATUS")
|
||||
values = [r[0] for r in c.fetchall() if r[0] and str(r[0]).strip()]
|
||||
if "Forwarded Email" not in values:
|
||||
values.append("Forwarded Email")
|
||||
return values
|
||||
|
||||
# ============================================================
|
||||
# MAIN ROUTE
|
||||
# ============================================================
|
||||
@app.route("/", methods=["GET"])
|
||||
def search_emails():
|
||||
booking = request.args.get("booking", "").strip().upper()
|
||||
alert_flag = request.args.get("alert", "")
|
||||
status = request.args.get("status", "")
|
||||
start_date = request.args.get("start", "")
|
||||
end_date = request.args.get("end", "")
|
||||
include_forwarded = request.args.get("include_forwarded") == "on"
|
||||
|
||||
sql = """
|
||||
SELECT DATE_SENT, BOOKING, EMAIL_FROM, EMAIL_TO, SUBJECT,
|
||||
STATUS, BRIEF_DESCRIPTION, ALERT, FILE_NAME, THREAD_INDEX
|
||||
FROM PROCESSED_EMAILS_REDACTED
|
||||
WHERE 1=1 \
|
||||
"""
|
||||
params = {}
|
||||
|
||||
if booking:
|
||||
sql += " AND UPPER(BOOKING) LIKE :bk"
|
||||
params["bk"] = f"%{booking}%"
|
||||
if alert_flag in ("YES", "NO"):
|
||||
sql += " AND ALERT = :alert"
|
||||
params["alert"] = alert_flag
|
||||
if status:
|
||||
if status == "Forwarded Email":
|
||||
sql += " AND (STATUS IS NULL OR TRIM(STATUS) = '')"
|
||||
else:
|
||||
sql += " AND STATUS = :status"
|
||||
params["status"] = status
|
||||
else:
|
||||
# If the checkbox is NOT checked, exclude forwarded emails (STATUS is null)
|
||||
if not include_forwarded:
|
||||
sql += " AND (STATUS IS NOT NULL)"
|
||||
|
||||
if start_date:
|
||||
sql += " AND DATE_SENT >= TO_TIMESTAMP(:start, 'YYYY-MM-DD')"
|
||||
params["start"] = start_date
|
||||
if end_date:
|
||||
sql += " AND DATE_SENT <= TO_TIMESTAMP(:end, 'YYYY-MM-DD') + INTERVAL '1' DAY"
|
||||
params["end"] = end_date
|
||||
|
||||
sql += " ORDER BY DATE_SENT ASC"
|
||||
|
||||
with connection.cursor() as c:
|
||||
c.execute(sql, params)
|
||||
rows = c.fetchall()
|
||||
cols = [d[0] for d in c.description]
|
||||
records = [dict(zip(cols, row)) for row in rows]
|
||||
|
||||
for r in records:
|
||||
if isinstance(r["DATE_SENT"], datetime):
|
||||
r["DATE_SENT"] = r["DATE_SENT"].strftime("%Y-%m-%d %H:%M:%S")
|
||||
if not r.get("STATUS") or not str(r["STATUS"]).strip():
|
||||
r["STATUS"] = "Forwarded Email"
|
||||
|
||||
status_options = get_distinct_status()
|
||||
|
||||
return render_template(
|
||||
"emails_filter.html",
|
||||
records=records,
|
||||
filters={
|
||||
"booking": booking,
|
||||
"alert": alert_flag,
|
||||
"status": status,
|
||||
"start": start_date,
|
||||
"end": end_date,
|
||||
"include_forwarded": include_forwarded
|
||||
},
|
||||
status_options=status_options
|
||||
)
|
||||
|
||||
# ============================================================
|
||||
# ROUTE: VIEW EMAIL
|
||||
# ============================================================
|
||||
@app.route("/view_email/<path:file_name>")
|
||||
def view_email(file_name):
|
||||
try:
|
||||
# Fix URL encoding
|
||||
file_name = urllib.parse.unquote(file_name).strip()
|
||||
|
||||
# Try multiple possible paths
|
||||
possible_paths = [
|
||||
file_name,
|
||||
f"processed/{file_name}",
|
||||
f"inbox/{file_name}",
|
||||
f"emails/{file_name}",
|
||||
f"emails/processed/{file_name}",
|
||||
]
|
||||
|
||||
msg_obj = None
|
||||
for path in possible_paths:
|
||||
try:
|
||||
obj = object_storage.get_object(namespace, BUCKET_NAME, path)
|
||||
msg_obj = extract_msg.Message(io.BytesIO(obj.data.content))
|
||||
print(f"[INFO] File found: {path}")
|
||||
break
|
||||
except oci.exceptions.ServiceError as e:
|
||||
if e.status == 404:
|
||||
continue
|
||||
raise
|
||||
|
||||
if not msg_obj:
|
||||
return (
|
||||
f"<h3>❌ Email not found in any tested path.</h3>"
|
||||
f"<p><b>Bucket:</b> {BUCKET_NAME}<br><b>File:</b> {file_name}</p>",
|
||||
404,
|
||||
)
|
||||
|
||||
# Extract main fields
|
||||
from_field = getattr(msg_obj, "sender", "")
|
||||
to_field = getattr(msg_obj, "to", "")
|
||||
subject = getattr(msg_obj, "subject", "")
|
||||
date = getattr(msg_obj, "date", "")
|
||||
body = getattr(msg_obj, "body", "")
|
||||
|
||||
# Basic HTML rendering for readability
|
||||
html = f"""
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{subject or "Viewed Email"}</title>
|
||||
<style>
|
||||
body {{
|
||||
font-family: Arial, sans-serif;
|
||||
background: #f4f4f9;
|
||||
padding: 30px;
|
||||
color: #333;
|
||||
}}
|
||||
.email-container {{
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
padding: 20px;
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}}
|
||||
.header {{
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
}}
|
||||
.header b {{
|
||||
color: #004b8d;
|
||||
}}
|
||||
pre {{
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<p><b>From:</b> {from_field}</p>
|
||||
<p><b>To:</b> {to_field}</p>
|
||||
<p><b>Date:</b> {date}</p>
|
||||
<p><b>Subject:</b> {subject}</p>
|
||||
</div>
|
||||
<pre>{body}</pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return render_template_string(html)
|
||||
|
||||
except Exception as e:
|
||||
return f"<h3>Error reading the email:</h3><pre>{e}</pre>", 500
|
||||
|
||||
|
||||
# ============================================================
|
||||
# MAIN ENTRY POINT
|
||||
# ============================================================
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, host="0.0.0.0", port=5015)
|
||||
Reference in New Issue
Block a user