Merge branch 'main' of https://github.com/MNPC-IR/Rasaddam_Backend
This commit is contained in:
2
.idea/Rasaddam_Backend.iml
generated
2
.idea/Rasaddam_Backend.iml
generated
@@ -14,7 +14,7 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$" />
|
||||||
<orderEntry type="jdk" jdkName="Python 3.10 (env) (3)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.10 (bazrasienv)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<component name="PyDocumentationSettings">
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (env) (3)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (bazrasienv)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
@@ -38,7 +38,7 @@ class SaveLog:
|
|||||||
'method': request.method,
|
'method': request.method,
|
||||||
'remote_address': self.get_client_ip(request),
|
'remote_address': self.get_client_ip(request),
|
||||||
'exec_time': _t,
|
'exec_time': _t,
|
||||||
'body_response': response.content.decode('utf-8'),
|
# 'body_response': response.content.decode('utf-8'),
|
||||||
'body_request': request.POST,
|
'body_request': request.POST,
|
||||||
'client_ip': self.get_client_ip(request),
|
'client_ip': self.get_client_ip(request),
|
||||||
'browser_info': request.META['HTTP_USER_AGENT'],
|
'browser_info': request.META['HTTP_USER_AGENT'],
|
||||||
|
|||||||
0
apps/product/services/excel/excel_processing.py
Normal file
0
apps/product/services/excel/excel_processing.py
Normal file
0
apps/warehouse/services/excel/__init__.py
Normal file
0
apps/warehouse/services/excel/__init__.py
Normal file
85
apps/warehouse/services/excel/excel_processing.py
Normal file
85
apps/warehouse/services/excel/excel_processing.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.styles import Alignment
|
||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.decorators import action, api_view, permission_classes
|
||||||
|
from rest_framework.permissions import AllowAny , IsAuthenticated
|
||||||
|
|
||||||
|
from apps.warehouse import models as warehouse_models
|
||||||
|
from apps.warehouse.web.api.v1 import serializers as warehouse_serializers
|
||||||
|
from common.helper_excel import create_header, excel_description, create_header_freez, create_value
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(["GET"])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
@csrf_exempt
|
||||||
|
def warehouse_excel(request):
|
||||||
|
|
||||||
|
excel_options = [
|
||||||
|
'ردیف',
|
||||||
|
'وضعیت',
|
||||||
|
'ثبت کننده',
|
||||||
|
'تاریخ ثبت',
|
||||||
|
'مرغدار',
|
||||||
|
'کشتارگاه',
|
||||||
|
'حجم',
|
||||||
|
'حجم کم شده از سالن مرغدار',
|
||||||
|
'وزن تقریبی کشتار(کیلوگرم)',
|
||||||
|
'حجم سفارشات دریافتی توسط کشتارگاه',
|
||||||
|
'اختلاف کشتار(حجم)',
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
output = BytesIO()
|
||||||
|
workbook = Workbook()
|
||||||
|
worksheet = workbook.active
|
||||||
|
worksheet.sheet_view.rightToLeft = True
|
||||||
|
worksheet.insert_rows(1)
|
||||||
|
cell = worksheet.cell(row=1, column=1)
|
||||||
|
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
|
||||||
|
header_list = [
|
||||||
|
'حجم',
|
||||||
|
'حجم کم شده از سالن مرغدار',
|
||||||
|
'وزن تقریبی کشتار(کیلوگرم)',
|
||||||
|
'حجم سفارشات دریافتی توسط کشتارگاه',
|
||||||
|
'اختلاف کشتار(حجم)',
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
create_header(worksheet, header_list, 5, 2, height=20)
|
||||||
|
|
||||||
|
excel_description(worksheet, 'B1', f'اختلاف کشتار ', color='red', row2='C3')
|
||||||
|
|
||||||
|
create_header_freez(worksheet, excel_options, 1, 6, 7, height=22)
|
||||||
|
|
||||||
|
l = 5
|
||||||
|
m = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
list2 = [
|
||||||
|
'مجموع==>',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
create_value(worksheet, list2, l + 3, 1, color='green')
|
||||||
|
workbook.save(output)
|
||||||
|
output.seek(0)
|
||||||
|
|
||||||
|
response = HttpResponse(
|
||||||
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
|
response[
|
||||||
|
'Content-Disposition'] = f'attachment; filename="اختلاف کشتار.xlsx"'.encode(
|
||||||
|
'utf-8')
|
||||||
|
response.write(output.getvalue())
|
||||||
|
return response
|
||||||
11
apps/warehouse/services/excel/urls.py
Normal file
11
apps/warehouse/services/excel/urls.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from apps.warehouse.services.excel import excel_processing as excel
|
||||||
|
from apps.warehouse.services.excel.excel_processing import warehouse_excel
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', include(router.urls)),
|
||||||
|
path('warehouse_excel/', warehouse_excel),
|
||||||
|
]
|
||||||
@@ -3,5 +3,7 @@ from django.urls import path, include
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('web/api/', include('apps.warehouse.web.api.v1.urls'))
|
path('web/api/', include('apps.warehouse.web.api.v1.urls')),
|
||||||
|
path('excel/', include('apps.warehouse.services.excel.urls')),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
294
common/helper_excel.py
Normal file
294
common/helper_excel.py
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
import jdatetime
|
||||||
|
import openpyxl
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.chart import LineChart, Reference, BarChart
|
||||||
|
from openpyxl.styles import PatternFill, Alignment, Font
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
blue_fill = PatternFill(start_color="277358", fill_type="solid")
|
||||||
|
Alignment_CELL = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
||||||
|
red_font = Font(color="C00000", bold=True)
|
||||||
|
GREEN_CELL = PatternFill(start_color="00B050", fill_type="solid")
|
||||||
|
RED_CELL = PatternFill(start_color="FCDFDC", fill_type="solid")
|
||||||
|
YELLOW_CELL = PatternFill(start_color="FFFF00", fill_type="solid")
|
||||||
|
ORANGE_CELL = PatternFill(start_color="FFC000", fill_type="solid")
|
||||||
|
BLUE_CELL = PatternFill(start_color="538DD5", fill_type="solid")
|
||||||
|
LIGHT_GREEN_CELL = PatternFill(start_color="92D050", fill_type="solid")
|
||||||
|
VERY_LIGHT_GREEN_CELL = PatternFill(start_color="5AFC56", fill_type="solid")
|
||||||
|
|
||||||
|
|
||||||
|
def shamsi_date(date, in_value=None):
|
||||||
|
if in_value:
|
||||||
|
sh_date = jdatetime.date.fromgregorian(
|
||||||
|
year=date.year,
|
||||||
|
month=date.month,
|
||||||
|
day=date.day
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gh_date = jdatetime.date.fromgregorian(
|
||||||
|
year=date.year,
|
||||||
|
month=date.month,
|
||||||
|
day=date.day
|
||||||
|
).strftime('%Y-%m-%d')
|
||||||
|
reversed_date = reversed(gh_date.split("-"))
|
||||||
|
separate = "-"
|
||||||
|
sh_date = separate.join(reversed_date)
|
||||||
|
return sh_date
|
||||||
|
|
||||||
|
|
||||||
|
def create_header(worksheet, list, num, row, height=None, width=None, color=None, text_color=None, border_style=None):
|
||||||
|
for col_num, option in enumerate(list, num):
|
||||||
|
cell = worksheet.cell(row=row, column=col_num, value=option)
|
||||||
|
col_letter = get_column_letter(col_num)
|
||||||
|
cell.alignment = Alignment_CELL
|
||||||
|
if color is not None:
|
||||||
|
if color == 'green':
|
||||||
|
cell.fill = GREEN_CELL
|
||||||
|
elif color == 'orange':
|
||||||
|
cell.fill = ORANGE_CELL
|
||||||
|
elif color == 'blue':
|
||||||
|
cell.fill = BLUE_CELL
|
||||||
|
else:
|
||||||
|
cell.fill = PatternFill(start_color=color, fill_type="solid")
|
||||||
|
else:
|
||||||
|
cell.fill = blue_fill
|
||||||
|
if text_color is not None:
|
||||||
|
cell.font = Font(size=9, bold=True, color=text_color)
|
||||||
|
else:
|
||||||
|
cell.font = Font(size=9, bold=True, color='D9FFFFFF')
|
||||||
|
if height is not None:
|
||||||
|
worksheet.row_dimensions[row].height = height
|
||||||
|
if width is not None:
|
||||||
|
worksheet.column_dimensions[col_letter].width = width
|
||||||
|
if border_style is not None:
|
||||||
|
cell.border = openpyxl.styles.Border(
|
||||||
|
left=openpyxl.styles.Side(style=border_style),
|
||||||
|
right=openpyxl.styles.Side(style=border_style),
|
||||||
|
top=openpyxl.styles.Side(style=border_style),
|
||||||
|
bottom=openpyxl.styles.Side(style=border_style)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_header_freez(worksheet, list, num, row, header_row, height=None, width=None, len_with=None,
|
||||||
|
different_cell=None):
|
||||||
|
for col_num, option in enumerate(list, num):
|
||||||
|
col_letter = get_column_letter(col_num)
|
||||||
|
cell = worksheet.cell(row=row, column=col_num, value=option)
|
||||||
|
cell.alignment = Alignment_CELL
|
||||||
|
cell.fill = blue_fill
|
||||||
|
cell.font = Font(size=10, bold=True, color='D9FFFFFF')
|
||||||
|
|
||||||
|
if height is not None:
|
||||||
|
worksheet.row_dimensions[row].height = height
|
||||||
|
if len(option) > worksheet.column_dimensions[col_letter].width:
|
||||||
|
worksheet.column_dimensions[col_letter].width = len(option) + 2
|
||||||
|
if width is not None:
|
||||||
|
worksheet.column_dimensions[col_letter].width = width
|
||||||
|
if len_with is not None:
|
||||||
|
if len(option) > worksheet.column_dimensions[col_letter].width:
|
||||||
|
worksheet.column_dimensions[col_letter].width = len(option) + 3
|
||||||
|
if different_cell is not None:
|
||||||
|
if option == different_cell:
|
||||||
|
cell.fill = PatternFill(start_color="C00000", fill_type="solid")
|
||||||
|
worksheet.freeze_panes = worksheet[f'A{header_row}']
|
||||||
|
max_col = worksheet.max_column
|
||||||
|
range_str = f'A{header_row - 1}:{get_column_letter(max_col)}{worksheet.max_row}'
|
||||||
|
worksheet.auto_filter.ref = range_str
|
||||||
|
|
||||||
|
|
||||||
|
def excel_description(worksheet, row1, description, size=None, color=None, my_color=None, row2=None):
|
||||||
|
worksheet[row1] = description
|
||||||
|
worksheet[row1].alignment = Alignment_CELL
|
||||||
|
if size is not None:
|
||||||
|
worksheet[row1].font = Font(size=size)
|
||||||
|
if color is not None:
|
||||||
|
worksheet[row1].font = red_font
|
||||||
|
if my_color is not None:
|
||||||
|
worksheet[row1].font = PatternFill(start_color=my_color, fill_type="solid")
|
||||||
|
|
||||||
|
if row2 is not None:
|
||||||
|
merge_range = f'{row1}:{row2}'
|
||||||
|
worksheet.merge_cells(merge_range)
|
||||||
|
|
||||||
|
|
||||||
|
def create_value(worksheet, list, l, num, border_style=None, m=None, height=None, color=None, width=None,
|
||||||
|
different_cell=None, different_value=None, item_num=None, item_color=None):
|
||||||
|
color_dict = {
|
||||||
|
'green': GREEN_CELL,
|
||||||
|
'yellow': YELLOW_CELL,
|
||||||
|
'blue': BLUE_CELL,
|
||||||
|
'red': RED_CELL,
|
||||||
|
'light_green': LIGHT_GREEN_CELL,
|
||||||
|
'very_light_green': VERY_LIGHT_GREEN_CELL
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in range(len(list)):
|
||||||
|
cell = worksheet.cell(row=l, column=item + num, value=list[item])
|
||||||
|
cell.alignment = Alignment_CELL
|
||||||
|
|
||||||
|
if border_style:
|
||||||
|
cell.border = openpyxl.styles.Border(
|
||||||
|
left=openpyxl.styles.Side(style=border_style),
|
||||||
|
right=openpyxl.styles.Side(style=border_style),
|
||||||
|
top=openpyxl.styles.Side(style=border_style),
|
||||||
|
bottom=openpyxl.styles.Side(style=border_style)
|
||||||
|
)
|
||||||
|
|
||||||
|
value = list[item]
|
||||||
|
if isinstance(value, (int, float)) and value != 0:
|
||||||
|
cell.number_format = '#,###'
|
||||||
|
else:
|
||||||
|
cell.value = value
|
||||||
|
|
||||||
|
cell.font = Font(size=10, bold=True)
|
||||||
|
|
||||||
|
if m is not None and m % 2 != 0:
|
||||||
|
cell.fill = PatternFill(start_color="D6F6FE", fill_type="solid")
|
||||||
|
|
||||||
|
if height is not None:
|
||||||
|
worksheet.row_dimensions[l + 1].height = height
|
||||||
|
|
||||||
|
if item_num is not None and item == item_num:
|
||||||
|
if item_color:
|
||||||
|
cell.fill = item_color
|
||||||
|
elif color in color_dict:
|
||||||
|
cell.fill = color_dict[color]
|
||||||
|
|
||||||
|
if different_cell is not None and list[different_cell] == different_value:
|
||||||
|
cell.fill = RED_CELL
|
||||||
|
|
||||||
|
if width is not None:
|
||||||
|
worksheet.column_dimensions[openpyxl.utils.get_column_letter(item + num)].width = width
|
||||||
|
|
||||||
|
|
||||||
|
def merge_cells(worksheet, l, s, cell1=None, cell2=None, lst=None):
|
||||||
|
if lst is not None:
|
||||||
|
for col in lst:
|
||||||
|
rng = f'{col}{l}:{col}{l + s}'
|
||||||
|
worksheet.merge_cells(rng)
|
||||||
|
worksheet[col + f'{l}'].alignment = Alignment_CELL
|
||||||
|
else:
|
||||||
|
for col in range(ord(f'{cell1}'), ord(f'{cell2}') + 1):
|
||||||
|
rng = f'{chr(col)}{l}:{chr(col)}{l + s}'
|
||||||
|
worksheet.merge_cells(rng)
|
||||||
|
worksheet[chr(col) + f'{l}'].alignment = Alignment_CELL
|
||||||
|
|
||||||
|
|
||||||
|
def add_header(worksheet):
|
||||||
|
worksheet.oddHeader.center.text = "سامانه رصدیار"
|
||||||
|
worksheet.oddHeader.center.size = 14 # تنظیم اندازه فونت
|
||||||
|
worksheet.oddHeader.center.font = "Arial,Bold" # تنظیم فونت و ضخامت
|
||||||
|
|
||||||
|
# همچنین میتوانید از هدرهای چپ و راست هم استفاده کنید
|
||||||
|
# worksheet.oddHeader.right.text = f"تاریخ: {shamsi_now_date}"
|
||||||
|
|
||||||
|
|
||||||
|
def cell_color_changer(worksheet, row, start_index, end_index, custom_color):
|
||||||
|
for item in range(start_index, end_index):
|
||||||
|
cell = worksheet.cell(row=row, column=item)
|
||||||
|
cell.fill = PatternFill(start_color=custom_color, fill_type="solid")
|
||||||
|
|
||||||
|
|
||||||
|
def start_excel():
|
||||||
|
output = BytesIO()
|
||||||
|
workbook = Workbook()
|
||||||
|
worksheet = workbook.active
|
||||||
|
worksheet.sheet_view.rightToLeft = True
|
||||||
|
worksheet.insert_rows(1)
|
||||||
|
return workbook, worksheet, output
|
||||||
|
|
||||||
|
|
||||||
|
def close_excel(name):
|
||||||
|
workbook, worksheet, output = start_excel()
|
||||||
|
workbook.save(output)
|
||||||
|
output.seek(0)
|
||||||
|
|
||||||
|
response = HttpResponse(
|
||||||
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||||
|
response[
|
||||||
|
'Content-Disposition'] = f'attachment; filename="{name}.xlsx"'.encode(
|
||||||
|
'utf-8')
|
||||||
|
response.write(output.getvalue())
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def add_chart(
|
||||||
|
worksheet,
|
||||||
|
chart_type,
|
||||||
|
data_columns,
|
||||||
|
category_column,
|
||||||
|
start_row,
|
||||||
|
end_row,
|
||||||
|
chart_position,
|
||||||
|
chart_title,
|
||||||
|
x_axis_title,
|
||||||
|
y_axis_title,
|
||||||
|
chart_width=25, # عرض نمودار پیشفرض (واحد: cm)
|
||||||
|
chart_height=15
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
افزودن نمودار به صفحه اکسل.
|
||||||
|
|
||||||
|
ورودی:
|
||||||
|
worksheet (openpyxl.Worksheet): صفحه اکسل.
|
||||||
|
chart_type (str): نوع نمودار ("line" یا "bar").
|
||||||
|
data_columns (list): لیستی از ستونهای داده.
|
||||||
|
category_column (int): ستون دستهبندیها.
|
||||||
|
start_row (int): ردیف شروع دادهها.
|
||||||
|
end_row (int): ردیف پایان دادهها.
|
||||||
|
chart_position (str): محل قرار گرفتن نمودار.
|
||||||
|
chart_title (str): عنوان نمودار.
|
||||||
|
x_axis_title (str): عنوان محور X.
|
||||||
|
y_axis_title (str): عنوان محور Y.
|
||||||
|
chart_width (float): عرض نمودار (واحد: cm).
|
||||||
|
chart_height (float): ارتفاع نمودار (واحد: cm).
|
||||||
|
"""
|
||||||
|
|
||||||
|
if chart_type == 'line':
|
||||||
|
chart = LineChart()
|
||||||
|
chart.style = 20
|
||||||
|
elif chart_type == 'bar':
|
||||||
|
chart = BarChart()
|
||||||
|
else:
|
||||||
|
raise ValueError("chart_type باید 'line' یا 'bar' باشد.")
|
||||||
|
|
||||||
|
chart.title = chart_title
|
||||||
|
chart.y_axis.title = y_axis_title
|
||||||
|
chart.x_axis.title = x_axis_title
|
||||||
|
chart.width = chart_width
|
||||||
|
chart.height = chart_height
|
||||||
|
|
||||||
|
categories = Reference(worksheet, min_col=category_column, min_row=start_row, max_row=end_row)
|
||||||
|
data = Reference(worksheet, min_col=data_columns, min_row=start_row - 1, max_row=end_row)
|
||||||
|
chart.add_data(data, titles_from_data=True)
|
||||||
|
chart.set_categories(categories)
|
||||||
|
for series in chart.series:
|
||||||
|
series.graphicalProperties.line.solidFill = "277358"
|
||||||
|
series.graphicalProperties.line.width = 30000
|
||||||
|
|
||||||
|
worksheet.add_chart(chart, chart_position)
|
||||||
|
# example
|
||||||
|
# add_chart(
|
||||||
|
# worksheet=worksheet,
|
||||||
|
# chart_type='line',
|
||||||
|
# data_columns=7, # ستون وزن وارد شده
|
||||||
|
# category_column=2, # ستون نام سردخانهها
|
||||||
|
# start_row=7,
|
||||||
|
# end_row=l + 1,
|
||||||
|
# chart_position="A12",
|
||||||
|
# chart_title="نمودار تغییرات وزن در سردخانهها",
|
||||||
|
# x_axis_title="سردخانهها",
|
||||||
|
# y_axis_title="وزن (کیلوگرم)"
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
def to_locale_str(a):
|
||||||
|
return "{:,}".format(int(a))
|
||||||
|
|
||||||
|
|
||||||
|
def convert_str_to_date(string):
|
||||||
|
return datetime.strptime(str(string), '%Y-%m-%d').date()
|
||||||
Reference in New Issue
Block a user