Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import threading
|
3 |
+
from email.header import decode_header
|
4 |
+
import mysql.connector
|
5 |
+
from transformers import pipeline # Assuming you'll use Hugging Face pipeline
|
6 |
+
import email, imaplib, json, time
|
7 |
+
import logging
|
8 |
+
|
9 |
+
# Configure logging
|
10 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
+
# Email and database configuration
|
14 |
+
IMAP_SERVER = 'imap.gmail.com'
|
15 |
+
EMAIL_ADDRESS = '[email protected]'
|
16 |
+
PASSWORD = 'gclc wsnx kywt uvqy' # Store this securely in production
|
17 |
+
DB_CONFIG = {
|
18 |
+
'host': '0.tcp.in.ngrok.io',
|
19 |
+
'port': 11329,
|
20 |
+
'user': 'root',
|
21 |
+
'password': '', # Add the correct password
|
22 |
+
'database': 'shipment_details'
|
23 |
+
}
|
24 |
+
|
25 |
+
# JSON format for extracted shipment details
|
26 |
+
output_format = {
|
27 |
+
"origin": "",
|
28 |
+
"destination": "",
|
29 |
+
"expected_shipment_datetime": "",
|
30 |
+
"types_of_service": "",
|
31 |
+
"warehouse": "",
|
32 |
+
"description": "",
|
33 |
+
"quantities": "",
|
34 |
+
"carrier_details": ""
|
35 |
+
}
|
36 |
+
|
37 |
+
# Prompt for LLM to process shipment-related emails
|
38 |
+
prompt = """
|
39 |
+
System prompt: You will be provided with an email containing shipment details. Your task is to extract specific information based on the given instructions.
|
40 |
+
Instructions:
|
41 |
+
1. Focus only on extracting details about future shipments, ignore irrelevant information.
|
42 |
+
2. Output should be in JSON format. Missing information should be marked as null.
|
43 |
+
3. Extract the following:
|
44 |
+
- origin
|
45 |
+
- destination
|
46 |
+
- expected_shipment_datetime (format: yyyy-mm-dd hh:mm:ss)
|
47 |
+
- types_of_service (AIR, LCL, FCL)
|
48 |
+
- warehouse
|
49 |
+
- description (max 100 words)
|
50 |
+
- quantities
|
51 |
+
- carrier_details
|
52 |
+
4. The output should be formatted as follows:
|
53 |
+
{
|
54 |
+
"origin": "",
|
55 |
+
"destination": "",
|
56 |
+
"expected_shipment_datetime": "",
|
57 |
+
"types_of_service": "",
|
58 |
+
"warehouse": "",
|
59 |
+
"description": "",
|
60 |
+
"quantities": "",
|
61 |
+
"carrier_details": ""
|
62 |
+
}
|
63 |
+
"""
|
64 |
+
|
65 |
+
# Function to insert extracted shipment details into MySQL database
|
66 |
+
def insert_data(extracted_details):
|
67 |
+
try:
|
68 |
+
# Initialize MySQL database connection
|
69 |
+
mydb = mysql.connector.connect(**DB_CONFIG)
|
70 |
+
cursor = mydb.cursor()
|
71 |
+
|
72 |
+
# Skip insertion if all required fields are empty
|
73 |
+
required_fields = [
|
74 |
+
'origin', 'destination', 'expected_shipment_datetime',
|
75 |
+
'types_of_service', 'warehouse', 'description',
|
76 |
+
'quantities', 'carrier_details'
|
77 |
+
]
|
78 |
+
if all(extracted_details.get(field) in ["", None] for field in required_fields):
|
79 |
+
logger.info("Skipping insertion: All extracted values are empty.")
|
80 |
+
return
|
81 |
+
|
82 |
+
# Insert data into database
|
83 |
+
sql = """
|
84 |
+
INSERT INTO shipment_details (
|
85 |
+
origin, destination, expected_shipment_datetime, types_of_service,
|
86 |
+
warehouse, description, quantities, carrier_details,
|
87 |
+
sender, receiver, cc, bcc, subject
|
88 |
+
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
89 |
+
"""
|
90 |
+
values = (
|
91 |
+
extracted_details.get('origin'),
|
92 |
+
extracted_details.get('destination'),
|
93 |
+
extracted_details.get('expected_shipment_datetime'),
|
94 |
+
extracted_details.get('types_of_service'),
|
95 |
+
extracted_details.get('warehouse'),
|
96 |
+
extracted_details.get('description'),
|
97 |
+
extracted_details.get('quantities'),
|
98 |
+
extracted_details.get('carrier_details'),
|
99 |
+
extracted_details.get('sender'),
|
100 |
+
extracted_details.get('receiver'),
|
101 |
+
extracted_details.get('cc'),
|
102 |
+
extracted_details.get('bcc'),
|
103 |
+
extracted_details.get('subject')
|
104 |
+
)
|
105 |
+
cursor.execute(sql, values)
|
106 |
+
mydb.commit()
|
107 |
+
logger.info("Data inserted successfully.")
|
108 |
+
|
109 |
+
except mysql.connector.Error as db_err:
|
110 |
+
logger.error(f"Database error: {db_err}")
|
111 |
+
except Exception as ex:
|
112 |
+
logger.error(f"Error inserting data: {ex}")
|
113 |
+
|
114 |
+
# Function to extract shipment details using an LLM
|
115 |
+
def get_details(mail):
|
116 |
+
try:
|
117 |
+
# Initialize LLM model and tokenizer
|
118 |
+
# Uncomment below if using Hugging Face models, or load your specific model accordingly
|
119 |
+
# pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
|
120 |
+
# output = pipe(f"{prompt}\n{mail}", max_new_tokens=200)
|
121 |
+
|
122 |
+
# Assuming Llama model for completion (example)
|
123 |
+
response = { # Placeholder response for testing purposes
|
124 |
+
"origin": "New York",
|
125 |
+
"destination": "Los Angeles",
|
126 |
+
"expected_shipment_datetime": "2024-10-20 12:00:00",
|
127 |
+
"types_of_service": "AIR",
|
128 |
+
"warehouse": "Warehouse 1",
|
129 |
+
"description": "Electronics shipment",
|
130 |
+
"quantities": "10",
|
131 |
+
"carrier_details": "Carrier XYZ"
|
132 |
+
}
|
133 |
+
return json.dumps(response) # Returning mock response for testing
|
134 |
+
|
135 |
+
except Exception as ex:
|
136 |
+
logger.error(f"Error generating details from LLM: {ex}")
|
137 |
+
return None
|
138 |
+
|
139 |
+
# Function to read and process unread emails
|
140 |
+
def read_email():
|
141 |
+
logging.info('Ready to read email...')
|
142 |
+
try:
|
143 |
+
logging.info('Connecting to IMAP server...')
|
144 |
+
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
|
145 |
+
mail.login(EMAIL_ADDRESS, PASSWORD)
|
146 |
+
mail.select('inbox')
|
147 |
+
logging.info('Selected inbox')
|
148 |
+
status, messages = mail.search(None, 'UNSEEN')
|
149 |
+
message_ids = messages[0].split()
|
150 |
+
logging.info(f"Total unread emails: {len(message_ids)}")
|
151 |
+
|
152 |
+
for message_id in message_ids:
|
153 |
+
try:
|
154 |
+
status, data = mail.fetch(message_id, '(RFC822)')
|
155 |
+
raw_email = data[0][1]
|
156 |
+
email_message = email.message_from_bytes(raw_email)
|
157 |
+
|
158 |
+
# Extract metadata
|
159 |
+
sender = email_message['From']
|
160 |
+
receiver = email_message['To']
|
161 |
+
cc = email_message.get('Cc', '')
|
162 |
+
bcc = email_message.get('Bcc', '')
|
163 |
+
subject = email_message['Subject']
|
164 |
+
|
165 |
+
# Extract email body
|
166 |
+
if email_message.is_multipart():
|
167 |
+
for part in email_message.walk():
|
168 |
+
if part.get_content_type() == 'text/plain':
|
169 |
+
email_body = part.get_payload(decode=True).decode('utf-8')
|
170 |
+
break
|
171 |
+
else:
|
172 |
+
email_body = email_message.get_payload(decode=True).decode('utf-8')
|
173 |
+
|
174 |
+
# Extract and store details
|
175 |
+
extracted_details_str = get_details(email_body)
|
176 |
+
extracted_details = json.loads(extracted_details_str)
|
177 |
+
meta_data = {
|
178 |
+
'sender': sender, 'receiver': receiver, 'cc': cc, 'bcc': bcc, 'subject': subject
|
179 |
+
}
|
180 |
+
extracted_details.update(meta_data)
|
181 |
+
insert_data(extracted_details)
|
182 |
+
|
183 |
+
except Exception as e:
|
184 |
+
logger.error(f"Error processing email {message_id}: {e}")
|
185 |
+
|
186 |
+
mail.close()
|
187 |
+
mail.logout()
|
188 |
+
|
189 |
+
except Exception as e:
|
190 |
+
logger.error(f"Error reading emails: {e}")
|
191 |
+
|
192 |
+
# Email processing loop
|
193 |
+
running = False
|
194 |
+
loop_thread = None
|
195 |
+
|
196 |
+
def email_processing_loop():
|
197 |
+
global running
|
198 |
+
logger.info("Starting email processing loop...")
|
199 |
+
while running:
|
200 |
+
read_email()
|
201 |
+
time.sleep(10) # Check for new emails every 10 seconds
|
202 |
+
|
203 |
+
def start_processing():
|
204 |
+
global running, loop_thread
|
205 |
+
if not running:
|
206 |
+
running = True
|
207 |
+
loop_thread = threading.Thread(target=email_processing_loop, daemon=True)
|
208 |
+
loop_thread.start()
|
209 |
+
return "Running"
|
210 |
+
|
211 |
+
def stop_processing():
|
212 |
+
global running
|
213 |
+
if running:
|
214 |
+
running = False
|
215 |
+
return "Stopped"
|
216 |
+
|
217 |
+
def update_status():
|
218 |
+
return "Running" if running else "Stopped"
|
219 |
+
|
220 |
+
# Create Gradio interface
|
221 |
+
with gr.Blocks() as demo:
|
222 |
+
gr.Markdown("# Email Processing")
|
223 |
+
|
224 |
+
status_display = gr.Textbox(label="Email Processing Status", interactive=False)
|
225 |
+
status_display.update(value=update_status()) # Initial status display
|
226 |
+
|
227 |
+
start_button = gr.Button("Start Processing")
|
228 |
+
stop_button = gr.Button("Stop Processing")
|
229 |
+
|
230 |
+
start_button.click(start_processing, outputs=status_display)
|
231 |
+
stop_button.click(stop_processing, outputs=status_display)
|
232 |
+
|
233 |
+
# Automatically update status every 2 seconds
|
234 |
+
gr.Timer(update_status, outputs=status_display, interval=2)
|
235 |
+
|
236 |
+
if __name__ == "__main__":
|
237 |
+
logging.info('Starting project...')
|
238 |
+
demo.launch()
|