init commit
This commit is contained in:
62
transportmanager/server/utils/cancel_order.py
Normal file
62
transportmanager/server/utils/cancel_order.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import os
|
||||
from PyPDF2 import PdfReader, PdfWriter
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import A4
|
||||
|
||||
GENERATED_FOLDER = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "generated_pdfs")
|
||||
)
|
||||
|
||||
def create_watermark_pdf(watermark_path, text="CANCELLED"):
|
||||
c = canvas.Canvas(watermark_path, pagesize=A4)
|
||||
c.setFont("Helvetica-Bold", 80)
|
||||
c.setFillGray(0.6) # Light gray text
|
||||
c.saveState()
|
||||
c.translate(300, 400)
|
||||
c.rotate(45)
|
||||
c.drawCentredString(0, 0, text)
|
||||
c.restoreState()
|
||||
c.save()
|
||||
|
||||
def apply_watermark(input_pdf_path, output_pdf_path, watermark_pdf_path):
|
||||
reader = PdfReader(input_pdf_path)
|
||||
watermark = PdfReader(watermark_pdf_path).pages[0]
|
||||
writer = PdfWriter()
|
||||
|
||||
for page in reader.pages:
|
||||
page.merge_page(watermark)
|
||||
writer.add_page(page)
|
||||
|
||||
with open(output_pdf_path, "wb") as f:
|
||||
writer.write(f)
|
||||
|
||||
def cancel_order_pdf(order_filename):
|
||||
# File paths
|
||||
input_pdf_path = os.path.join(GENERATED_FOLDER, order_filename)
|
||||
output_pdf_path = input_pdf_path
|
||||
watermark_pdf_path = os.path.join(GENERATED_FOLDER, "temp_watermark.pdf")
|
||||
print(watermark_pdf_path)
|
||||
# Check if file exists
|
||||
if not os.path.isfile(input_pdf_path):
|
||||
raise FileNotFoundError(f"Original order PDF not found: {input_pdf_path}")
|
||||
|
||||
# Create watermark and apply it
|
||||
create_watermark_pdf(watermark_pdf_path, text="CANCELLED")
|
||||
apply_watermark(input_pdf_path, output_pdf_path, watermark_pdf_path)
|
||||
|
||||
# Optionally remove temp watermark
|
||||
os.remove(watermark_pdf_path)
|
||||
|
||||
return output_pdf_path
|
||||
|
||||
# Example usage:
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python cancel_order.py <order_filename>")
|
||||
else:
|
||||
try:
|
||||
result_path = cancel_order_pdf(sys.argv[1])
|
||||
print(f"Cancelled PDF created: {result_path}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
139
transportmanager/server/utils/email.py
Normal file
139
transportmanager/server/utils/email.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import smtplib
|
||||
from email.message import EmailMessage
|
||||
import os
|
||||
|
||||
def send_email(to_email, subject, body):
|
||||
smtp_host = os.environ.get("SMTP_HOST")
|
||||
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||
smtp_user = os.environ.get("SMTP_USER")
|
||||
smtp_pass = os.environ.get("SMTP_PASS")
|
||||
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||
|
||||
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||
raise ValueError("SMTP config incomplete in environment variables.")
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender_email
|
||||
msg["To"] = to_email
|
||||
msg.set_content(body)
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_pass)
|
||||
server.send_message(msg)
|
||||
|
||||
|
||||
# Send email with attachment
|
||||
def send_email_with_attachment(to_email, subject, body, attachment_path):
|
||||
smtp_host = os.environ.get("SMTP_HOST")
|
||||
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||
smtp_user = os.environ.get("SMTP_USER")
|
||||
smtp_pass = os.environ.get("SMTP_PASS")
|
||||
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||
|
||||
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||
raise ValueError("SMTP config incomplete in environment variables.")
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender_email
|
||||
msg["To"] = to_email
|
||||
msg.set_content(body)
|
||||
|
||||
if attachment_path and os.path.isfile(attachment_path):
|
||||
with open(attachment_path, "rb") as f:
|
||||
file_data = f.read()
|
||||
file_name = os.path.basename(attachment_path)
|
||||
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||
else:
|
||||
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_pass)
|
||||
server.send_message(msg)
|
||||
|
||||
|
||||
# Send email using Gmail directly
|
||||
def send_gmail(to_email, subject, body):
|
||||
smtp_host = "smtp.gmail.com"
|
||||
smtp_port = 587
|
||||
smtp_user = 'macamete.robert@gmail.com'
|
||||
smtp_pass = 'advx yqlv jkaa czvr'
|
||||
sender_email = 'macamete.robert@gmail.com'
|
||||
|
||||
if not all([smtp_user, smtp_pass]):
|
||||
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender_email
|
||||
msg["To"] = to_email
|
||||
msg.set_content(body)
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_pass)
|
||||
server.send_message(msg)
|
||||
|
||||
|
||||
# Send email with attachment using Gmail directly
|
||||
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
|
||||
smtp_host = "smtp.gmail.com"
|
||||
smtp_port = 587
|
||||
smtp_user = os.environ.get("GMAIL_USER")
|
||||
smtp_pass = os.environ.get("GMAIL_PASS")
|
||||
sender_email = smtp_user
|
||||
|
||||
if not all([smtp_user, smtp_pass]):
|
||||
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender_email
|
||||
msg["To"] = to_email
|
||||
msg.set_content(body)
|
||||
|
||||
if attachment_path and os.path.isfile(attachment_path):
|
||||
with open(attachment_path, "rb") as f:
|
||||
file_data = f.read()
|
||||
file_name = os.path.basename(attachment_path)
|
||||
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||
else:
|
||||
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_pass)
|
||||
server.send_message(msg)
|
||||
|
||||
# Send email with attachment
|
||||
def send_custom_email_with_attachment(to_email, subject, body, attachment_path, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS):
|
||||
smtp_host = SMTP_HOST
|
||||
smtp_port = int(SMTP_PORT)
|
||||
smtp_user = SMTP_USER
|
||||
smtp_pass = SMTP_PASS
|
||||
sender_email = smtp_user
|
||||
|
||||
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||
raise ValueError("SMTP config incomplete in environment variables.")
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = sender_email
|
||||
msg["To"] = to_email
|
||||
msg.set_content(body)
|
||||
|
||||
if attachment_path and os.path.isfile(attachment_path):
|
||||
with open(attachment_path, "rb") as f:
|
||||
file_data = f.read()
|
||||
file_name = os.path.basename(attachment_path)
|
||||
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||
else:
|
||||
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||
|
||||
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||
server.starttls()
|
||||
server.login(smtp_user, smtp_pass)
|
||||
server.send_message(msg)
|
||||
28
transportmanager/server/utils/maps.py
Normal file
28
transportmanager/server/utils/maps.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from geopy.geocoders import Nominatim
|
||||
|
||||
class AdressCoordinates:
|
||||
def __init__(self, address):
|
||||
self.addess = address
|
||||
|
||||
def open_Maps_by_address(self):
|
||||
address = self.addess
|
||||
if not address:
|
||||
return
|
||||
|
||||
#try:
|
||||
geolocator = Nominatim(user_agent="flet_Maps_app")
|
||||
location = geolocator.geocode(address)
|
||||
print(location)
|
||||
if location:
|
||||
latitude = location.latitude
|
||||
longitude = location.longitude
|
||||
Maps_url = f"https://www.google.com/maps/search/?api=1&query={latitude},{longitude}"
|
||||
|
||||
return {
|
||||
'latitude' : latitude,
|
||||
'longitude' : longitude,
|
||||
'Maps_url': Maps_url
|
||||
}
|
||||
|
||||
#except Exception as ex:
|
||||
# print(ex)
|
||||
199
transportmanager/server/utils/pdf.py
Normal file
199
transportmanager/server/utils/pdf.py
Normal file
@@ -0,0 +1,199 @@
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.platypus import Table, TableStyle, Paragraph, SimpleDocTemplate, Spacer, Image
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
import io
|
||||
import os
|
||||
import logging
|
||||
|
||||
# --- helpers ---------------------------------------------------------------
|
||||
|
||||
def _resolve_logo_path(logo_path: str | None) -> str | None:
|
||||
"""Return a readable logo path or None. Tries multiple fallbacks.
|
||||
This avoids crashes when a caller passes a path that exists only in the
|
||||
client container (e.g., '/client/assets/...') while we're running in the
|
||||
server container on Fly.
|
||||
"""
|
||||
here = os.path.dirname(__file__)
|
||||
project_root = os.path.abspath(os.path.join(here, "..", ".."))
|
||||
|
||||
candidates = []
|
||||
|
||||
# 1) if caller provided a path, try it as-is
|
||||
if logo_path:
|
||||
candidates.append(logo_path)
|
||||
# also try without a leading slash inside our image tree
|
||||
stripped = logo_path.lstrip("/\\")
|
||||
candidates.append(os.path.join(project_root, stripped))
|
||||
|
||||
# 2) Known locations in this repo layout
|
||||
candidates.append(os.path.join(project_root, "client", "assets", "images", "truck_logo_black.png"))
|
||||
candidates.append(os.path.join(project_root, "assets", "images", "truck_logo_black.png"))
|
||||
|
||||
# 3) Allow override via env
|
||||
env_path = os.getenv("DEFAULT_LOGO_PATH")
|
||||
if env_path:
|
||||
candidates.insert(0, env_path)
|
||||
|
||||
for p in candidates:
|
||||
try:
|
||||
if p and os.path.isfile(p):
|
||||
return p
|
||||
except Exception:
|
||||
# some paths may be malformed on certain platforms
|
||||
continue
|
||||
return None
|
||||
|
||||
def generate_order_pdf(order, user_data, transporter_data, logo_path, save_to_disk=True):
|
||||
#print(order)
|
||||
#print(f'user data: {user_data}')
|
||||
if 'address' in user_data:
|
||||
address = user_data.get("address")
|
||||
if address:
|
||||
address = address.replace(" %",", ")
|
||||
else:
|
||||
address = ''
|
||||
else:
|
||||
address=''
|
||||
buffer = io.BytesIO()
|
||||
doc = SimpleDocTemplate(buffer, pagesize=A4)
|
||||
elements = []
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
# Resolve logo path robustly across local/dev and Fly
|
||||
logo_path = _resolve_logo_path(logo_path)
|
||||
|
||||
# Prepare texts
|
||||
user_text = f"""<b>{user_data.get("name", "")}</b><br/>
|
||||
{user_data.get("register_number", "")}<br/>
|
||||
{user_data.get("vat", "")}<br/>
|
||||
{address}<br/>
|
||||
{""}<br/>
|
||||
{user_data.get("contact_name", "")}<br/>
|
||||
{user_data.get("phone", "")}<br/>
|
||||
{user_data.get("email", "")}
|
||||
"""
|
||||
transporter_text = f"""<b>{transporter_data.get("name", "")}</b><br/>
|
||||
{transporter_data.get("contact_person", "")}<br/>
|
||||
{transporter_data.get("phone", "")}<br/>
|
||||
{transporter_data.get("email", "")}
|
||||
"""
|
||||
|
||||
# Logo (centered), tolerate missing file
|
||||
logo = None
|
||||
if logo_path:
|
||||
try:
|
||||
logo = Image(logo_path, width=120, mask='auto', height=60)
|
||||
logo.hAlign = 'CENTER'
|
||||
except Exception as e:
|
||||
logging.warning("PDF: failed to load logo at %s: %s", logo_path, e)
|
||||
logo = None
|
||||
|
||||
# Top section: transporter - logo - user
|
||||
top_section = Table([
|
||||
[
|
||||
Paragraph(transporter_text, styles['Normal']),
|
||||
logo,
|
||||
Paragraph(user_text, styles['Normal'])
|
||||
]
|
||||
], colWidths=[200, 150, 200])
|
||||
top_section.setStyle(TableStyle([
|
||||
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
||||
("ALIGN", (0, 0), (0, 0), "LEFT"),
|
||||
("ALIGN", (1, 0), (1, 0), "CENTER"),
|
||||
("ALIGN", (2, 0), (2, 0), "RIGHT"),
|
||||
("LEFTPADDING", (0, 0), (0, 0), 20),
|
||||
]))
|
||||
elements.append(top_section)
|
||||
elements.append(Spacer(1, 12))
|
||||
|
||||
# Order number and current date (centered, vertically stacked)
|
||||
from datetime import datetime
|
||||
header_info = Table([
|
||||
[Paragraph(f"<b>Loading Order</b>: {order['order_number']}", styles["Normal"])],
|
||||
[Paragraph(f"Date: {datetime.now().strftime('%d/%m/%Y')}", styles["Normal"])]
|
||||
], colWidths=[450])
|
||||
header_info.setStyle(TableStyle([
|
||||
("ALIGN", (0, 0), (-1, -1), "CENTER")
|
||||
]))
|
||||
elements.append(header_info)
|
||||
elements.append(Spacer(1, 12))
|
||||
|
||||
# Order Summary Table
|
||||
elements.append(Paragraph("Summary", styles['Heading3']))
|
||||
summary_data = [
|
||||
["Details", Paragraph("Values", styles["Normal"])],
|
||||
["Truck Reg. No.", Paragraph(str(order["track_reg_number"]), styles["Normal"])],
|
||||
["Trailer Reg. No.", Paragraph(str(order["trailer_reg_number"]), styles["Normal"])],
|
||||
["Product Description", Paragraph(str(order["products_description"]), styles["Normal"])],
|
||||
["LDM", Paragraph(str(order["ldb_quantity"]), styles["Normal"])],
|
||||
["KG", Paragraph(str(order["kg_quantity"]), styles["Normal"])],
|
||||
["Price", Paragraph(str(order["paid_price"]), styles["Normal"])],
|
||||
]
|
||||
summary_table = Table(summary_data, colWidths=[150, 350])
|
||||
summary_table.setStyle(TableStyle([
|
||||
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
|
||||
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
||||
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
||||
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
|
||||
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
||||
]))
|
||||
elements.append(summary_table)
|
||||
elements.append(Spacer(1, 24))
|
||||
|
||||
# Loading Points
|
||||
elements.append(Paragraph("Loading Sequence", styles['Heading3']))
|
||||
loading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]]
|
||||
for l in order["loading_addresses"]:
|
||||
loading_data.append([
|
||||
Paragraph(f"{l["loading_address_name"]}: {l["loading_address"]}", styles["Normal"]),
|
||||
Paragraph(f"{l['loading_date']} {l['loading_hour']}", styles["Normal"]),
|
||||
Paragraph(str(l["loading_informatins"]), styles["Normal"])
|
||||
])
|
||||
loading_table = Table(loading_data, colWidths=[200, 100, 200])
|
||||
loading_table.setStyle(TableStyle([
|
||||
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
|
||||
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
|
||||
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
||||
]))
|
||||
elements.append(loading_table)
|
||||
elements.append(Spacer(1, 24))
|
||||
|
||||
# Unloading Points
|
||||
elements.append(Paragraph("Unloading Sequence", styles['Heading3']))
|
||||
unloading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]]
|
||||
for u in order["unloading_addresses"]:
|
||||
unloading_data.append([
|
||||
Paragraph(f"{u["unloading_address_name"]}: {u["unloading_address"]}", styles["Normal"]),
|
||||
Paragraph(f"{u['unloading_date']} {u['unloading_hour']}", styles["Normal"]),
|
||||
Paragraph(str(u["unloading_informatins"]), styles["Normal"])
|
||||
])
|
||||
unloading_table = Table(unloading_data, colWidths=[200, 100, 200])
|
||||
unloading_table.setStyle(TableStyle([
|
||||
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
|
||||
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
("WORDWRAP", (0, 0), (-1, -1), "CJK"),
|
||||
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
||||
]))
|
||||
elements.append(unloading_table)
|
||||
|
||||
elements.append(Spacer(1, 24))
|
||||
|
||||
elements.append(Paragraph("Terms and Conditions", styles["Heading3"]),)
|
||||
|
||||
elements.append(
|
||||
Paragraph(str(order["terms"]), styles["Normal"])
|
||||
)
|
||||
|
||||
doc.build(elements)
|
||||
buffer.seek(0)
|
||||
if save_to_disk:
|
||||
save_path=f"generated_pdfs/order_{order['user_id']}_{order['order_number']}.pdf"
|
||||
with open(save_path, "wb") as f:
|
||||
f.write(buffer.getvalue())
|
||||
|
||||
return buffer
|
||||
Reference in New Issue
Block a user