mirror of
https://github.com/hoshikawa2/ai_logistic_outlook_integration.git
synced 2026-07-09 16:44:21 +00:00
233 lines
7.7 KiB
Python
233 lines
7.7 KiB
Python
# -*- 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) |