SA-SAJCOAI / export_utils.py
EGYADMIN's picture
Upload 2 files
79a378b verified
import base64
import os
import pandas as pd
from datetime import datetime
def export_to_excel(project_items, project_info, filename):
"""تصدير بيانات المشروع إلى ملف Excel"""
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side
# إنشاء مصنف Excel جديد
wb = openpyxl.Workbook()
# إنشاء ورقة معلومات المشروع
project_sheet = wb.active
project_sheet.title = "معلومات المشروع"
# إضافة معلومات المشروع
project_sheet['A1'] = "معلومات المشروع"
project_sheet['A1'].font = Font(bold=True, size=14)
headers = ["البند", "القيمة"]
project_sheet.append(headers)
project_data = [
["اسم المشروع", project_info['name']],
["العميل", project_info['client']],
["الموقع", project_info.get('location', '-')],
["رقم المناقصة", project_info.get('tender_number', '-')],
["القيمة التقديرية", f"{project_info['estimated_value']:,.2f} ريال"],
["الموعد النهائي", project_info['deadline']],
["مدة العقد", project_info.get('contract_duration', '-')],
["نوع التسعير", project_info['pricing_type']]
]
for row in project_data:
project_sheet.append(row)
# تنسيق الجدول
for col in range(1, 3):
for row in range(2, len(project_data) + 3):
project_sheet.cell(row=row, column=col).border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# إنشاء ورقة جدول الكميات
boq_sheet = wb.create_sheet(title="جدول الكميات")
# إضافة عناوين الأعمدة
headers = ["الكود", "الوصف", "الوحدة", "الكمية", "سعر الوحدة (ريال)", "السعر الإجمالي (ريال)", "نوع المورد"]
boq_sheet.append(headers)
# إضافة بيانات البنود
for item in project_items:
row_data = [
item['code'],
item['description'],
item['unit'],
item['quantity'],
item['unit_price'],
item['total_price'],
item.get('resource_type', '-')
]
boq_sheet.append(row_data)
# تنسيق الجدول
for col in range(1, 8):
for row in range(1, len(project_items) + 2):
boq_sheet.cell(row=row, column=col).border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# حفظ الملف
wb.save(filename)
return filename
def export_to_pdf(project_items, project_info, filename):
"""تصدير بيانات المشروع إلى ملف PDF"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
try:
import arabic_reshaper
from bidi.algorithm import get_display
ARABIC_SUPPORT = True
except ImportError:
ARABIC_SUPPORT = False
# تسجيل الخط العربي
font_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fonts", "arabic_font.ttf")
if os.path.exists(font_path):
try:
pdfmetrics.registerFont(TTFont('Arabic', font_path))
except:
# استخدام خط افتراضي إذا فشل تسجيل الخط العربي
pass
# إنشاء أنماط النص
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='Arabic', fontName='Helvetica', alignment=1)) # محاذاة للوسط
# إنشاء مستند PDF
doc = SimpleDocTemplate(filename, pagesize=A4, rightMargin=30, leftMargin=30, topMargin=30, bottomMargin=30)
elements = []
# إضافة عنوان المستند
title_text = f"تقرير المشروع: {project_info['name']}"
if ARABIC_SUPPORT:
title_text = get_display(arabic_reshaper.reshape(title_text))
title = Paragraph(title_text, styles['Arabic'])
elements.append(title)
elements.append(Spacer(1, 20))
# إضافة معلومات المشروع
project_data = [
["البند", "القيمة"],
["اسم المشروع", project_info['name']],
["العميل", project_info['client']],
["الموقع", project_info.get('location', '-')],
["رقم المناقصة", project_info.get('tender_number', '-')],
["القيمة التقديرية", f"{project_info['estimated_value']:,.2f} ريال"],
["الموعد النهائي", project_info['deadline']],
["مدة العقد", project_info.get('contract_duration', '-')],
["نوع التسعير", project_info['pricing_type']]
]
# تحويل النص العربي
if ARABIC_SUPPORT:
for i in range(len(project_data)):
for j in range(len(project_data[i])):
if isinstance(project_data[i][j], str):
project_data[i][j] = get_display(arabic_reshaper.reshape(project_data[i][j]))
# إنشاء جدول معلومات المشروع
project_table = Table(project_data)
project_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
elements.append(project_table)
elements.append(Spacer(1, 30))
# إضافة عنوان جدول الكميات
boq_title_text = "جدول الكميات"
if ARABIC_SUPPORT:
boq_title_text = get_display(arabic_reshaper.reshape(boq_title_text))
boq_title = Paragraph(boq_title_text, styles['Arabic'])
elements.append(boq_title)
elements.append(Spacer(1, 10))
# إعداد بيانات جدول الكميات
boq_data = [["الكود", "الوصف", "الوحدة", "الكمية", "سعر الوحدة", "السعر الإجمالي"]]
for item in project_items:
row = [
item['code'],
item['description'],
item['unit'],
str(item['quantity']),
f"{item['unit_price']:,.2f}",
f"{item['total_price']:,.2f}"
]
# تحويل النص العربي
if ARABIC_SUPPORT:
for i in range(len(row)):
if isinstance(row[i], str):
row[i] = get_display(arabic_reshaper.reshape(row[i]))
boq_data.append(row)
# إضافة الإجمالي
total_cost = sum(item['total_price'] for item in project_items)
total_row = ["", "", "", "", "الإجمالي", f"{total_cost:,.2f}"]
if ARABIC_SUPPORT:
total_row[4] = get_display(arabic_reshaper.reshape(total_row[4]))
total_row[5] = get_display(arabic_reshaper.reshape(total_row[5]))
boq_data.append(total_row)
# إنشاء جدول الكميات
boq_table = Table(boq_data)
boq_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, -1), (-1, -1), colors.lightgrey)
]))
elements.append(boq_table)
# بناء المستند
doc.build(elements)
return filename
def export_local_content_report(project_items, project_info, filename):
"""تصدير تقرير المحتوى المحلي إلى ملف Excel"""
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.utils import get_column_letter
# إنشاء مصنف Excel جديد
wb = openpyxl.Workbook()
# إنشاء ورقة ملخص المحتوى المحلي
summary_sheet = wb.active
summary_sheet.title = "ملخص المحتوى المحلي"
# إضافة عنوان التقرير
summary_sheet['A1'] = f"تقرير المحتوى المحلي - {project_info['name']}"
summary_sheet['A1'].font = Font(bold=True, size=14)
summary_sheet.merge_cells('A1:G1')
summary_sheet['A1'].alignment = Alignment(horizontal='center')
# إضافة معلومات المشروع
summary_sheet['A3'] = "معلومات المشروع"
summary_sheet['A3'].font = Font(bold=True)
project_data = [
["اسم المشروع", project_info['name']],
["العميل", project_info['client']],
["رقم المناقصة", project_info.get('tender_number', '-')],
["النسبة المستهدفة للمحتوى المحلي", f"{project_info.get('local_content_target', 40)}%"]
]
for i, row in enumerate(project_data):
summary_sheet[f'A{i+4}'] = row[0]
summary_sheet[f'B{i+4}'] = row[1]
# إضافة ملخص المحتوى المحلي
summary_sheet['A9'] = "ملخص المحتوى المحلي"
summary_sheet['A9'].font = Font(bold=True)
# الحصول على ملخص المحتوى المحلي
summary = project_info.get('local_content_summary', {
'total_percentage': 0,
'by_category': {
'materials': 0,
'labor': 0,
'services': 0,
'equipment': 0
}
})
summary_data = [
["النسبة الإجمالية للمحتوى المحلي", f"{summary.get('total_percentage', 0):.1f}%"],
["نسبة المواد المحلية", f"{summary.get('by_category', {}).get('materials', 0):.1f}%"],
["نسبة العمالة المحلية", f"{summary.get('by_category', {}).get('labor', 0):.1f}%"],
["نسبة الخدمات المحلية", f"{summary.get('by_category', {}).get('services', 0):.1f}%"],
["نسبة المعدات المحلية", f"{summary.get('by_category', {}).get('equipment', 0):.1f}%"]
]
for i, row in enumerate(summary_data):
summary_sheet[f'A{i+10}'] = row[0]
summary_sheet[f'B{i+10}'] = row[1]
# تنسيق الخلايا
for col in range(1, 3):
for row in range(4, 15):
cell = summary_sheet.cell(row=row, column=col)
cell.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# إضافة تقييم الامتثال
target_percentage = project_info.get('local_content_target', 40)
total_percentage = summary.get('total_percentage', 0)
summary_sheet['A16'] = "تقييم الامتثال"
summary_sheet['A16'].font = Font(bold=True)
if total_percentage >= target_percentage:
compliance_text = f"المشروع يحقق متطلبات المحتوى المحلي المستهدفة ({target_percentage}%)."
compliance_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
else:
compliance_text = f"المشروع لا يحقق متطلبات المحتوى المحلي المستهدفة ({target_percentage}%)."
compliance_fill = PatternFill(start_color="FFCCCC", end_color="FFCCCC", fill_type="solid")
summary_sheet['A17'] = compliance_text
summary_sheet['A17'].fill = compliance_fill
summary_sheet.merge_cells('A17:G17')
# إنشاء ورقة تفاصيل البنود
details_sheet = wb.create_sheet(title="تفاصيل البنود")
# إضافة عناوين الأعمدة
headers = [
"الكود", "الوصف", "نوع المورد", "مورد محلي",
"نسبة المحتوى المحلي (%)", "السعر الإجمالي (ريال)",
"المساهمة في المحتوى المحلي (ريال)"
]
for i, header in enumerate(headers, 1):
details_sheet.cell(row=1, column=i).value = header
details_sheet.cell(row=1, column=i).font = Font(bold=True)
details_sheet.cell(row=1, column=i).fill = PatternFill(start_color="DDDDDD", end_color="DDDDDD", fill_type="solid")
# إضافة بيانات البنود
for i, item in enumerate(project_items, 2):
details_sheet.cell(row=i, column=1).value = item['code']
details_sheet.cell(row=i, column=2).value = item['description']
details_sheet.cell(row=i, column=3).value = item.get('resource_type', '-')
details_sheet.cell(row=i, column=4).value = "نعم" if item.get('is_local_supplier', False) else "لا"
details_sheet.cell(row=i, column=5).value = item.get('local_content_percentage', 0)
details_sheet.cell(row=i, column=6).value = item['total_price']
details_sheet.cell(row=i, column=7).value = item['total_price'] * item.get('local_content_percentage', 0) / 100
# تنسيق الأعمدة
for col in range(1, 8):
column_letter = get_column_letter(col)
details_sheet.column_dimensions[column_letter].width = 15
details_sheet.column_dimensions['B'].width = 30 # عمود الوصف أوسع
# تنسيق الخلايا
for col in range(1, 8):
for row in range(1, len(project_items) + 2):
cell = details_sheet.cell(row=row, column=col)
cell.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# حفظ الملف
wb.save(filename)
return filename
def export_risk_report(risks, project_info, total_cost, filename):
"""تصدير تقرير المخاطر إلى ملف Excel"""
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.utils import get_column_letter
# إنشاء مصنف Excel جديد
wb = openpyxl.Workbook()
# إنشاء ورقة ملخص المخاطر
summary_sheet = wb.active
summary_sheet.title = "ملخص المخاطر"
# إضافة عنوان التقرير
summary_sheet['A1'] = f"تقرير المخاطر - {project_info['name']}"
summary_sheet['A1'].font = Font(bold=True, size=14)
summary_sheet.merge_cells('A1:G1')
summary_sheet['A1'].alignment = Alignment(horizontal='center')
# إضافة معلومات المشروع
summary_sheet['A3'] = "معلومات المشروع"
summary_sheet['A3'].font = Font(bold=True)
project_data = [
["اسم المشروع", project_info['name']],
["العميل", project_info['client']],
["رقم المناقصة", project_info.get('tender_number', '-')],
["التكلفة الإجمالية", f"{total_cost:,.2f} ريال"]
]
for i, row in enumerate(project_data):
summary_sheet[f'A{i+4}'] = row[0]
summary_sheet[f'B{i+4}'] = row[1]
# إضافة ملخص المخاطر
summary_sheet['A9'] = "ملخص المخاطر"
summary_sheet['A9'].font = Font(bold=True)
# الحصول على ملخص المخاطر
risk_summary = project_info.get('risk_summary', {
'total_risks': len(risks),
'high_risks': sum(1 for risk in risks if risk['risk_score'] >= 15),
'medium_risks': sum(1 for risk in risks if 8 <= risk['risk_score'] < 15),
'low_risks': sum(1 for risk in risks if risk['risk_score'] < 8),
'total_cost_impact': sum(risk['cost_impact'] for risk in risks),
'total_schedule_impact': sum(risk['schedule_impact'] for risk in risks),
'risk_contingency': sum(risk['probability'] / 5 * risk['cost_impact'] for risk in risks),
'risk_contingency_percentage': (sum(risk['probability'] / 5 * risk['cost_impact'] for risk in risks) / total_cost * 100) if total_cost > 0 else 0
})
summary_data = [
["إجمالي عدد المخاطر", risk_summary.get('total_risks', 0)],
["المخاطر العالية", risk_summary.get('high_risks', 0)],
["المخاطر المتوسطة", risk_summary.get('medium_risks', 0)],
["المخاطر المنخفضة", risk_summary.get('low_risks', 0)],
["إجمالي التأثير المالي", f"{risk_summary.get('total_cost_impact', 0):,.2f} ريال"],
["إجمالي التأثير على الجدول الزمني", f"{risk_summary.get('total_schedule_impact', 0)} يوم"],
["احتياطي المخاطر المقترح", f"{risk_summary.get('risk_contingency', 0):,.2f} ريال"],
["نسبة احتياطي المخاطر", f"{risk_summary.get('risk_contingency_percentage', 0):.1f}%"]
]
for i, row in enumerate(summary_data):
summary_sheet[f'A{i+10}'] = row[0]
summary_sheet[f'B{i+10}'] = row[1]
# تنسيق الخلايا
for col in range(1, 3):
for row in range(4, 18):
cell = summary_sheet.cell(row=row, column=col)
cell.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# إنشاء ورقة قائمة المخاطر
risks_sheet = wb.create_sheet(title="قائمة المخاطر")
# إضافة عناوين الأعمدة
headers = [
"معرف", "اسم المخاطرة", "الفئة", "الاحتمالية", "التأثير",
"درجة المخاطرة", "التأثير المالي", "التأثير على الجدول", "الحالة"
]
for i, header in enumerate(headers, 1):
risks_sheet.cell(row=1, column=i).value = header
risks_sheet.cell(row=1, column=i).font = Font(bold=True)
risks_sheet.cell(row=1, column=i).fill = PatternFill(start_color="DDDDDD", end_color="DDDDDD", fill_type="solid")
# ترتيب المخاطر حسب درجة المخاطرة
sorted_risks = sorted(risks, key=lambda x: x['risk_score'], reverse=True)
# إضافة بيانات المخاطر
for i, risk in enumerate(sorted_risks, 2):
risks_sheet.cell(row=i, column=1).value = risk['id']
risks_sheet.cell(row=i, column=2).value = risk['name']
risks_sheet.cell(row=i, column=3).value = risk['category']
risks_sheet.cell(row=i, column=4).value = risk['probability']
risks_sheet.cell(row=i, column=5).value = risk['impact']
risks_sheet.cell(row=i, column=6).value = risk['risk_score']
risks_sheet.cell(row=i, column=7).value = risk['cost_impact']
risks_sheet.cell(row=i, column=8).value = risk['schedule_impact']
risks_sheet.cell(row=i, column=9).value = risk['status']
# تلوين الصف حسب درجة المخاطرة
risk_score = risk['risk_score']
if risk_score >= 15:
fill_color = "FFCCCC" # أحمر فاتح للمخاطر العالية
elif risk_score >= 8:
fill_color = "FFEEBB" # أصفر للمخاطر المتوسطة
else:
fill_color = "CCFFCC" # أخضر فاتح للمخاطر المنخفضة
for col in range(1, 10):
risks_sheet.cell(row=i, column=col).fill = PatternFill(start_color=fill_color, end_color=fill_color, fill_type="solid")
# تنسيق الأعمدة
for col in range(1, 10):
column_letter = get_column_letter(col)
risks_sheet.column_dimensions[column_letter].width = 15
risks_sheet.column_dimensions['B'].width = 30 # عمود اسم المخاطرة أوسع
# تنسيق الخلايا
for col in range(1, 10):
for row in range(1, len(risks) + 2):
cell = risks_sheet.cell(row=row, column=col)
cell.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# إنشاء ورقة تفاصيل المخاطر
details_sheet = wb.create_sheet(title="تفاصيل المخاطر")
# إضافة عناوين الأعمدة
details_headers = [
"معرف", "اسم المخاطرة", "الوصف", "خطة التخفيف", "خطة الطوارئ"
]
for i, header in enumerate(details_headers, 1):
details_sheet.cell(row=1, column=i).value = header
details_sheet.cell(row=1, column=i).font = Font(bold=True)
details_sheet.cell(row=1, column=i).fill = PatternFill(start_color="DDDDDD", end_color="DDDDDD", fill_type="solid")
# إضافة تفاصيل المخاطر
for i, risk in enumerate(sorted_risks, 2):
details_sheet.cell(row=i, column=1).value = risk['id']
details_sheet.cell(row=i, column=2).value = risk['name']
details_sheet.cell(row=i, column=3).value = risk.get('description', '')
details_sheet.cell(row=i, column=4).value = risk.get('mitigation_plan', '')
details_sheet.cell(row=i, column=5).value = risk.get('contingency_plan', '')
# تنسيق الأعمدة
for col in range(1, 6):
column_letter = get_column_letter(col)
details_sheet.column_dimensions[column_letter].width = 20
details_sheet.column_dimensions['C'].width = 40 # عمود الوصف أوسع
details_sheet.column_dimensions['D'].width = 40 # عمود خطة التخفيف أوسع
details_sheet.column_dimensions['E'].width = 40 # عمود خطة الطوارئ أوسع
# تنسيق الخلايا
for col in range(1, 6):
for row in range(1, len(risks) + 2):
cell = details_sheet.cell(row=row, column=col)
cell.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
if col >= 3: # تنسيق النص في الأعمدة الطويلة
cell.alignment = Alignment(wrap_text=True)
# حفظ الملف
wb.save(filename)
return filename
def get_download_link(file_path, link_text, file_type):
"""إنشاء رابط تنزيل للملف"""
with open(file_path, "rb") as f:
file_bytes = f.read()
b64 = base64.b64encode(file_bytes).decode()
if file_type == "csv":
mime_type = "text/csv"
elif file_type == "excel":
mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
elif file_type == "pdf":
mime_type = "application/pdf"
else:
mime_type = "application/octet-stream"
href = f'<a href="data:{mime_type};base64,{b64}" download="{os.path.basename(file_path)}">{link_text}</a>'
return href