From 7d0d62f402d7347a65d83d08cf231c22b827c7d0 Mon Sep 17 00:00:00 2001 From: shahzeydyfah Date: Sun, 2 Nov 2025 19:13:34 +0330 Subject: [PATCH] feat(config):add domain --- app/repositories/excel.py | 441 +++++++++++++++++++++++++++++++++--- app/repositories/project.py | 8 + app/urls/excel.py | 10 + app/urls/project.py | 12 +- config.yml | 2 + 5 files changed, 432 insertions(+), 41 deletions(-) diff --git a/app/repositories/excel.py b/app/repositories/excel.py index 27fe8ef..efe1d76 100644 --- a/app/repositories/excel.py +++ b/app/repositories/excel.py @@ -6,11 +6,15 @@ import os from app.config import TaskEnv from app.models.design_progress import DesignProgressModel, RecordModel, SubRecord, Record, UpdateDesignProgress +from app.models.monito_physical_progress import LastRecord, PhysicalProgressModel +from app.models.monitor_rial_progress import RialProgressModel from app.models.project import ProjectModel, ProjectSts from core.service.mongo_db import use_collection import jdatetime from app.repositories.project import ProjectRepository from app.repositories.design_progress import DesignProgressRepository +from app.repositories.monitor_physical_progress import PhysicalProgressRepository +from app.repositories.monitor_rial_progress import RialProgressRepository class ExcelRepository(): async def addDesignSheet(self): @@ -732,42 +736,405 @@ class ExcelRepository(): return True - # def createDate(self, date): - # if '/' in date: - # date = date.replace('/', '') - # year = int(date[:4]) - # month = int(date[4:6]) - # if month == 0: - # month = 1 - # print(month,'month') - # day = int(date[6:8]) - # if day ==0: - # day=1 - # print(day,'day') - # jalali_date = jdatetime.date(year, month, day) - # gregorian_date = jalali_date.togregorian() - # created_at = datetime.datetime.combine(gregorian_date, datetime.time.min) - # return created_at + def createDate(self, date): + if '/' in date: + date = date.replace('/', '') + year = int(date[:4]) + month = int(date[4:6]) + if month == 0: + month = 1 + print(month,'month') + day = int(date[6:8]) + if day ==0: + day=1 + print(day,'day') + jalali_date = jdatetime.date(year, month, day) + gregorian_date = jalali_date.togregorian() + created_at = datetime.datetime.combine(gregorian_date, datetime.time.min) + return created_at - # async def createRecord(self, my_row, _time_f, _time_e, progress_id): - # send = SubRecord( - # date_at=self.createDate(my_row[f'{_time_e}/send/date_at']), - # number=my_row[f'{_time_e}/send/number'], - # description=None + def findMonth(self,month): + match month: + case '01': + return 'فروردین' + case '02': + return 'اردیبهشت' + case '03': + return 'خرداد' + case '04': + return 'تیر' + case '05': + return 'مرداد' + case '06': + return 'شهریور' + case '07': + return 'مهر' + case '08': + return 'آبان' + case '09': + return 'آذر' + case '10': + return 'دی' + case '11': + return 'بهمن' + case '12': + return 'اسفند' + + + async def createRecord(self, my_row, _time_f, _time_e, progress_id): + send = SubRecord( + date_at=self.createDate(my_row[f'{_time_e}/send/date_at']), + number=my_row[f'{_time_e}/send/number'], + description=None + ) + record = RecordModel( + design_progress_id=progress_id, + title=_time_f, + send=send, + ) + if f'{_time_e}/receive/date_at' in my_row and my_row[f'{_time_e}/receive/date_at'] != "": + print(my_row[f'{_time_e}/receive/date_at']) + receive = SubRecord( + date_at=self.createDate(my_row[f'{_time_e}/receive/date_at']), + number=my_row[f'{_time_e}/receive/number'], + description=None + ) + record.receive = receive + record.init_id().cu_at() + await use_collection("design_progress_record").insert_one(record.mongo_dict()) + return record + + # async def progressMonitor(self): + # base_dir = os.path.dirname(os.path.abspath(__file__)) + # project_root = os.path.abspath(os.path.join(base_dir, "../../excel")) + # excel_path = os.path.join(project_root, "progress_monitor0.xlsx") + # print("Excel file path:", excel_path) + # df = pd.read_excel(excel_path, engine="openpyxl", dtype={'code': str,'date_create_project':str,'date_estimate_complete':str}) + # df = df.fillna("") + # value_vars = [col for col in df.columns if "/" in col] + # id_vars = [col for col in df.columns if col not in value_vars] + # + # # تبدیل Wide به Long + # df_long = df.melt( + # id_vars=id_vars, + # value_vars=value_vars, + # var_name="metric_date", + # value_name="value" # ) - # record = RecordModel( - # design_progress_id=progress_id, - # title=_time_f, - # send=send, - # ) - # if f'{_time_e}/receive/date_at' in my_row and my_row[f'{_time_e}/receive/date_at'] != "": - # print(my_row[f'{_time_e}/receive/date_at']) - # receive = SubRecord( - # date_at=self.createDate(my_row[f'{_time_e}/receive/date_at']), - # number=my_row[f'{_time_e}/receive/number'], - # description=None - # ) - # record.receive = receive - # record.init_id().cu_at() - # await use_collection("design_progress_record").insert_one(record.mongo_dict()) - # return record + # + # # جدا کردن تاریخ و متریک از اسم ستون + # df_long["date"] = df_long["metric_date"].str.split("/").str[-1] + # df_long["metric"] = df_long["metric_date"].str.rsplit("/", n=1).str[0] + # projects = await use_collection(TaskEnv.CLN_PROJECT).find({}).to_list(None) + # for index, row in df_long.iterrows(): + # my_row = row.to_dict() + # print(f"سطر {index}: {row.to_dict()}") + # print("code",my_row['code']) + # # for p in projects: + # # if str(my_row["code"]) in str(p['design']["code"]) and p['counter'] == int(str(my_row["code"])): + # # print('projects', p) + # # return + # + # # sts = 2 + # # if my_row["status"] == 'متوقف': + # # sts = 3 + # # elif my_row["status"] == 'خاتمه یافته': + # # sts=4 + # # print('sts',sts) + # # await use_collection('project').update_one({"_id": p["_id"]}, {"$set": { + # # 'design.sts': sts, 'design.reason_stop': my_row["reason_stop"],'design.description':my_row['description'] + # # }}) + # # sts_class = ProjectSts( + # # project_id=p["_id"], + # # type='design', + # # sts=sts, + # # description=my_row["reason_stop"] + # # ).init_id().cu_at() + # # await use_collection(TaskEnv.CLN_PROJECT_STS).insert_one(sts_class.mongo_dict()) + # # break + # # print("code", my_row['code']) + # return True + # # { + # # "project_id": "c1dbe5bb-3175-42dc-a091-fd74e6d10f9e", + # # "program": "12", + # # "real": "56", + # # "medium_progress": 56, + # # "deviation": 44, + # # "date_at": "2025-03-21T00:00:00", + # # "month": "فروردین", + # # "year": "1404" + # # } + # + # # { + # # "project_id": "c1dbe5bb-3175-42dc-a091-fd74e6d10f9e", + # # "program": "10", + # # "real": "10", + # # "medium_progress": 33, + # # "deviation": 0, + # # "date_at": "2025-04-21T00:00:00", + # # "last_month": -46, + # # "month": "اردیبهشت", + # # "year": "1404" + # # } + # # { + # # "project_id": "c1dbe5bb-3175-42dc-a091-fd74e6d10f9e", + # # "program": "20", + # # "real": "15", + # # "medium_progress": 27, + # # "deviation": -5, + # # "date_at": "2025-05-22T00:00:00", + # # "last_month": 5, + # # "month": "خرداد", + # # "year": "1404" + # # } + # # { + # # "project_id": "c1dbe5bb-3175-42dc-a091-fd74e6d10f9e", + # # "program": "20", + # # "real": "15", + # # "medium_progress": 24, + # # "deviation": -5, + # # "date_at": "2025-06-22T00:00:00", + # # "last_month": 0, + # # "last_three_month": -41, + # # "month": "تیر", + # # "year": "1404" + # # } + async def progressMonitorPhysical(self): + base_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.abspath(os.path.join(base_dir, "../../excel")) + excel_path = os.path.join(project_root, "progress_monitor.xlsx") + print("Excel file path:", excel_path) + + # خواندن با سه سطر عنوان + df = pd.read_excel( + excel_path, + header=[0, 1], # سه ردیف اول عنوان + engine="openpyxl", + sheet_name="physical", + dtype={'code': str} + ).fillna("") + + # 1) پیدا کردن کلید دقیق ستون‌های شناسه (سطح اول = code/state/title) + id_first = {'code', 'state', 'title'} + id_map = {} + for col in df.columns: # col یک تاپل مثل ('code','Unnamed: 2_level_1') یا ('1401/01/01','program') + if col[0] in id_first and col[0] not in id_map: + id_map[col[0]] = col # مثلاً id_map['code'] = ('code','Unnamed: 2_level_1') + + # اگر یکی از شناسه‌ها پیدا نشد، بهتر است خطا/هشدار بدهیم + missing_ids = id_first - set(id_map.keys()) + if missing_ids: + raise ValueError(f"شناسه‌های مورد انتظار در هدر پیدا نشدند: {missing_ids}. هدر اکسل را بررسی کنید.") + + # 2) تبدیل code به رشته (جلوی تبدیل عددی ناخواسته را می‌گیرد) + df[id_map['code']] = df[id_map['code']].astype(str) + + # 3) ستون‌های متریک: هر ستونی که جزو شناسه‌ها نباشد + metric_columns = [col for col in df.columns if col[0] not in id_first] + + # 4) گروه‌بندی خروجی بر اساس (code, date) با setdefault (بدون KeyError) + grouped = {} # {(code,date): {...}} + + for _, row in df.iterrows(): + code_val = str(row[id_map['code']]).strip() + state_val = str(row[id_map['state']]).strip() + title_val = str(row[id_map['title']]).strip() + + for (date, metric) in metric_columns: + # مقدار سلول + value = row[(date, metric)] + # رد کردن مقدارهای خالی یا NaN + if value == "" or (isinstance(value, float) and pd.isna(value)): + continue + + key = (code_val, str(date)) # تاریخ را هم رشته می‌کنیم تا یکنواخت باشد + + # اگر قبلاً نساخته‌ایم، بساز + obj = grouped.setdefault(key, { + "code": code_val, + "state": state_val, + "title": title_val, + "date": str(date), + }) + print(obj['code']) + + # گذاشتن متریک (مثلاً program یا real) + if value == "-%" or value == "-": + value = 0 + value = float(value) * 100 + value = int(value) + obj[metric] = value + + # 5) خروجی نهایی به صورت لیست + results = list(grouped.values()) + projects = await use_collection(TaskEnv.CLN_PROJECT).find({}).to_list(None) + for res in results: + for p in projects: + if str(res["code"]) in str(p['design']["code"]) and p['counter'] == int(str(res["code"])): + date_at=self.createDate(res['date']) + print('date_at',date_at) + date = res['date'].replace('/', '') + year = str(date[:4]) + month = str(date[4:6]) + _inp = LastRecord(project_id=p['_id'],date_at=date_at) + print('_inp',_inp) + last = await PhysicalProgressRepository().last_record(_inp) + if 'program' not in res: + res['program'] = 0 + if 'real' not in res: + res['real'] = 0 + mydata = PhysicalProgressModel( + project_id=p['_id'], + program=res['program'], + real=res['real'], + date_at=date_at, + deviation=res['program']-res['real'], + year=year, + month=self.findMonth(month), + ) + if 'avg' in last['avg']: + mydata.medium_progress = last['avg']['avg'] + if 'real' in last['month']: + mydata.last_month = res['real'] - last['month']['real'] + if 'real' in last['three_month']: + mydata.last_three_month = res['real'] - last['three_month']['real'] + mydata.init_id().cu_at() + _get = await use_collection('monitor_physical').insert_one(mydata.mongo_dict()) + # await ProjectRepository().set_sts_two_for_monitor(_inp.project_id) + break + + return results + async def progressMonitorRial(self): + base_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.abspath(os.path.join(base_dir, "../../excel")) + excel_path = os.path.join(project_root, "progress_monitor.xlsx") + # خواندن با سه سطر عنوان + df = pd.read_excel( + excel_path, + header=[0, 1], # سه ردیف اول عنوان + engine="openpyxl", + sheet_name="rial", + dtype={'code': str} + ).fillna("") + + # 1) پیدا کردن کلید دقیق ستون‌های شناسه (سطح اول = code/state/title) + id_first = {'code', 'state', 'title'} + id_map = {} + for col in df.columns: # col یک تاپل مثل ('code','Unnamed: 2_level_1') یا ('1401/01/01','program') + if col[0] in id_first and col[0] not in id_map: + id_map[col[0]] = col # مثلاً id_map['code'] = ('code','Unnamed: 2_level_1') + + # اگر یکی از شناسه‌ها پیدا نشد، بهتر است خطا/هشدار بدهیم + missing_ids = id_first - set(id_map.keys()) + if missing_ids: + raise ValueError(f"شناسه‌های مورد انتظار در هدر پیدا نشدند: {missing_ids}. هدر اکسل را بررسی کنید.") + + # 2) تبدیل code به رشته (جلوی تبدیل عددی ناخواسته را می‌گیرد) + df[id_map['code']] = df[id_map['code']].astype(str) + + # 3) ستون‌های متریک: هر ستونی که جزو شناسه‌ها نباشد + metric_columns = [col for col in df.columns if col[0] not in id_first] + + # 4) گروه‌بندی خروجی بر اساس (code, date) با setdefault (بدون KeyError) + grouped = {} # {(code,date): {...}} + + for _, row in df.iterrows(): + print('row',row) + code_val = str(row[id_map['code']]).strip() + state_val = str(row[id_map['state']]).strip() + title_val = str(row[id_map['title']]).strip() + + for (date, metric) in metric_columns: + # مقدار سلول + value = row[(date, metric)] + # رد کردن مقدارهای خالی یا NaN + if value == "" or (isinstance(value, float) and pd.isna(value)): + continue + + key = (code_val, str(date)) # تاریخ را هم رشته می‌کنیم تا یکنواخت باشد + + # اگر قبلاً نساخته‌ایم، بساز + obj = grouped.setdefault(key, { + "code": code_val, + "state": state_val, + "title": title_val, + "date": str(date), + }) + # گذاشتن متریک (مثلاً program یا real) + if value == "-%" or value == "-": + value = 0 + value = float(value) * 100 + value = int(value) + obj[metric] = value + print('obj',obj) + + # 5) خروجی نهایی به صورت لیست + results = list(grouped.values()) + projects = await use_collection(TaskEnv.CLN_PROJECT).find({}).to_list(None) + for res in results: + print('res',res) + print('code',res["code"]) + for p in projects: + if str(res["code"]) in str(p['design']["code"]) and p['counter'] == int(str(res["code"])): + date_at=self.createDate(res['date']) + print('date_at',date_at) + date = res['date'].replace('/', '') + year = str(date[:4]) + month = str(date[4:6]) + _inp = LastRecord(project_id=p['_id'],date_at=date_at) + print('_inp',_inp) + last = await RialProgressRepository().last_record(_inp) + # print('last',last) + if 'program' not in res: + res['program'] = 0 + if 'real' not in res: + res['real'] = 0 + mydata = RialProgressModel( + project_id=p['_id'], + program=res['program'], + real=res['real'], + date_at=date_at, + ) + if 'real' in last: + mydata.last_month = res['real'] - last['real'] + mydata = mydata.init_id().cu_at().mongo_dict() + mydata['month'] = month + mydata['year'] = year + mydata['date'] = res['date'] + print('mydata',mydata) + _get = await use_collection('monitor_rial').insert_one(mydata) + # await ProjectRepository().set_sts_two_for_monitor(_inp.project_id) + break + + return results + + async def last_record(self, _inp: LastRecord): + send = {'month': {}, 'three_month': {}, 'before':{}} + if _inp.id: + find_record = await use_collection('monitor_physical_test').find_one({"_id":_inp.id}) + if _inp.id: + before = await use_collection('monitor_physical_test').find({ + 'project_id': _inp.project_id, + 'created_at': {'$lt': find_record['created_at']}}).sort("$natural", -1).to_list(None) + else: + before = await use_collection('monitor_physical_test').find({ + 'project_id': _inp.project_id, + }).sort("$natural", -1).to_list(None) + if before: + send['before'] = before[0] + last = await PhysicalProgressRepository().search_last('monitor_physical_test', _inp, 35, 25) + if last: + send['month'] = last + three_month = await PhysicalProgressRepository().search_last('monitor_physical_test', _inp, 95, 85) + if three_month: + send['three_month'] = three_month + _avg = await use_collection('monitor_physical_test').aggregate([ + {'$match':{'project_id':_inp.project_id, "date_at": {"$lt": _inp.date_at}}}, + {'$group': {'_id': None, 'avg': {'$avg': "$real"}, 'sum':{'$sum': "$real"}, 'count':{'$sum': 1}}} + ]).to_list(None) + if _avg: + send['avg'] = _avg[0] + else: + send['avg'] = {} + return send + diff --git a/app/repositories/project.py b/app/repositories/project.py index be22ebb..cb69cbc 100644 --- a/app/repositories/project.py +++ b/app/repositories/project.py @@ -208,6 +208,14 @@ class ProjectRepository(BaseRepository[ProjectModel]): return True + async def getList(self, state: str='null'): + if not state == 'null': + projects = await use_collection(TaskEnv.CLN_PROJECT).find({"sts": 1,"design.state":state }).to_list(None) + else: + projects = await use_collection(TaskEnv.CLN_PROJECT).find({"sts": 1}).to_list(None) + + return projects + async def fetch_delete(self, _input: TableEngineRequestType) -> TableEngineExport[T]: return await NewTableEngine.mongo( query_class=use_collection(self.COLLECTION), diff --git a/app/urls/excel.py b/app/urls/excel.py index a0120f7..9dfb247 100644 --- a/app/urls/excel.py +++ b/app/urls/excel.py @@ -89,4 +89,14 @@ def addProgressTender(): def changeStatus(): pass +@router.get('/progress/monitor/physical', tags=_tag_base) +@shrink(repo.progressMonitorPhysical) +def progressMonitor(): + pass + +@router.get('/progress/monitor/rial', tags=_tag_base) +@shrink(repo.progressMonitorRial) +def progressMonitorRial(): + pass + app.include_router(router) \ No newline at end of file diff --git a/app/urls/project.py b/app/urls/project.py index b063813..c01d118 100644 --- a/app/urls/project.py +++ b/app/urls/project.py @@ -36,6 +36,10 @@ def list_project_monitor(): @shrink(repo.list_project) def list_project(): pass +@router.get('/list/state/{state}', response_model=List[ThisModel], tags=_tag_base) +@shrink(repo.getList) +def getList(state:str): + pass @router.put('/design/sts/{_id}', response_model=ThisModel, tags=_tag_base) @shrink(repo.update_design_sts) @@ -93,10 +97,10 @@ def fetch_delete(body: TableEngineRequestType = Body(...)): # pass -@router.post('/get_all', tags=_tag_base) -@shrink(repo.get_all) -def get_all(): - pass +# @router.post('/get_all', tags=_tag_base) +# @shrink(repo.get_all) +# def get_all(): +# pass app.include_router(router) \ No newline at end of file diff --git a/config.yml b/config.yml index 3db1cff..157d13c 100644 --- a/config.yml +++ b/config.yml @@ -31,6 +31,8 @@ cors: - 'http://ygram.rooyart.ir' - 'https://ygram.rooyart.ir' - 'http://172.16.1.194' + - 'http://pm.khanesaziceco.ir' + - 'https://pm.khanesaziceco.ir' # Modules