From ccec50703394574ad29febdc4d0ca22458c653c0 Mon Sep 17 00:00:00 2001 From: Macbook Date: Sun, 14 Dec 2025 11:52:48 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B6=D1=83=D1=80=D0=BD=D0=B0=D0=BB,=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=20=D0=BC=D1=83=D0=BB=D1=8C=D1=82=D0=B8=D0=BA?= =?UTF-8?q?=D0=BB=D0=B8=D0=BA=D0=B0=20=D0=BD=D0=B0=20=D0=B8=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D1=80=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/routers/__init__.py | 43 ++- .../__pycache__/__init__.cpython-313.pyc | Bin 8030 -> 9325 bytes .../__pycache__/toolkit.cpython-313.pyc | Bin 11467 -> 11464 bytes api/routers/toolkit.py | 2 +- api/static/js/index.js | 346 +++++++++++++++++- .../__pycache__/records.cpython-313.pyc | Bin 16486 -> 17793 bytes .../__pycache__/toolkit.cpython-313.pyc | Bin 16863 -> 16824 bytes db/handlers/records.py | 32 ++ db/handlers/toolkit.py | 2 +- 9 files changed, 415 insertions(+), 10 deletions(-) diff --git a/api/routers/__init__.py b/api/routers/__init__.py index 3f4d6f5..141bcbf 100644 --- a/api/routers/__init__.py +++ b/api/routers/__init__.py @@ -114,6 +114,9 @@ async def post_requests( if isinstance(endDate, str): endDate = datetime.strptime(endDate, "%Y-%m-%d").date() + if startDate > endDate: + startDate, endDate = endDate, startDate + jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate) if isinstance(jurnal_toolkits, list): if len(jurnal_toolkits) == 0: @@ -143,10 +146,42 @@ async def post_requests( "endDate": endDate.strftime("%Y-%m-%d"), } case "jurnal_service": - jurnal_service = await ServiceRecordsHandler.get() - if jurnal_service: - resultData["status"] = "ok" - resultData["data"] = jurnal_service + startDate = request_data.get("body").get( + "startDate", date.today() - timedelta(days=7) + ) + if isinstance(startDate, str): + startDate = datetime.strptime(startDate, "%Y-%m-%d").date() + + endDate = request_data.get("body").get("endDate", date.today()) + if isinstance(endDate, str): + endDate = datetime.strptime(endDate, "%Y-%m-%d").date() + + if startDate > endDate: + startDate, endDate = endDate, startDate + + jurnal_service = await ServiceRecordsHandler.getLogs(startDate, endDate) + if isinstance(jurnal_service, list): + if len(jurnal_service) == 0: + resultData["status"] = "ok" + resultData["data"] = { + "requests": [], + "users": [], + "categories": [], + "startDate": startDate.strftime("%Y-%m-%d"), + "endDate": endDate.strftime("%Y-%m-%d"), + } + else: + users = await UserHandler.getAll() + categories = await CategoryHandler.getAll() + resultData["status"] = "ok" + resultData["data"] = { + "requests": jurnal_service, + "users": users, + "categories": categories, + "startDate": startDate.strftime("%Y-%m-%d"), + "endDate": endDate.strftime("%Y-%m-%d"), + } + # logger.info(resultData.get("data")) case "users": users = await UserHandler.getAll() if users: diff --git a/api/routers/__pycache__/__init__.cpython-313.pyc b/api/routers/__pycache__/__init__.cpython-313.pyc index 8fe3f590bec47b3f7633cba74a560d2d95cd1ccb..8f8d2ea4fc5caf7d903f4630698b27adfef6b154 100644 GIT binary patch delta 1140 zcmZvcUuauZ9LLY^-qR#){@muKH|9^9G=G*fZAsE)O-8%4japj@UU2CJq3p^YoJOS6 z6y}&Xylz$&ktJ6=9I92+OVu1U*&POnzXpf)v#v zQcw+(-~S^ms8_HG>V%D`w{d-o>y3hV0a8P1Rls|sggZ+4)#|0jWBCw*sjg}Xi`7|< zd5#5+7ZHsIeH{>N*2bpyD%f8aiNkxFDu28tvWo9dyL#-OJSVb?{yF<);duXTf&J+J zyjSwo3x*VJpBTMXsTXS9tP;4`bDY_K7uausPh4-panCs*t`NKF|Lp9)+xB9oTd|pD zY~~05Pjg0Wu{E&xk-97S>*+7zEh*EKGP@pc?LEVjxE3)yBkhq)ZJ~a-**CNoPqZVk zc4CNq85mfwTZX;3ajDst*gt)A=zfaC(yi#JX7rR1&A0mVwb|WdzLhNgnk*X0=~iUA zw(#`0lkG^NoyhWW&Zoyk**C#ggm*-y^v73+Nmzr20WwgyGgv&l?>l=x{L;fL32AWP z!{Sb4`oK$+%;Cy3zO46WC-reyE5f-cx;9lny*>^)(DnSYK{Oj~&?()JpW!+Mx|MDW zbG-?m_s~tj$+Z;Fowm(X9Q7?Htm-noMa}xAXx|1u7py}YK4DE#Z z8J$Vc$5v_QOv!=nt_}1Yz1y8feGl)8q$aHylvV0Smff7NCf#n;IjG_#{L3y5-QRF delta 722 zcmYk4&1(}u7>DPbjLDkjqe-)|`PejVHi?PFNJ~C2&6bMNo)$HP4T{=`28vK@a}+6v z2M->KThNQ1ym%5>@IPoEZ4QRCNvKlPlNX_}S}9(f8FO%89-iO4AE)`gu&+1{?RG1o z=f-AX>ACNn}P=Dw7C>Q3>?0;L#Tyf__s0r>P`T6S-MrRE+9LaS3AjhB$qfs5b>PPt*jZNnPiBz*()~Fsk8YM-}lr zL5bDRoOMc2Uvr-4jQ-!vy3}X%-pQJ(kcb;OuLT>|Mn3|6dcw&1H(=;CMmFyUBL1#m zy%YE|v@pm=4?+JFoF7_+LH!=+XQem#H);9g;OQW*FNR)bCjT`YNyriEMRMK9Oe-?e zjwoG8d7|z+rC$8Zy7fh_H7xg{)6_Dp;fVu}z7f8KSFrvoEU(FEtOP&&NXqVo3&&qa zuYMW3()Xg!Y~PB+)N#!R2tB%#FWX?V09t@-Dp~6FG-x4G&s+%5&~Sk^P8#l0%oCu= zq>*I41)#;r7Pd2Y`n9C6m5`}#+d)f_ZO0rb+t#4z25pKoM}oA{Oo5gr&2tx7TgwAl zj}H z#4^;oAz&QmltE!3&O_47e2P0Ark>->b1IBIU_Cv7yOq*-JkbPc1;E>Ldw-gk9GVbz M_c7De`}E)a1!11E?f?J) diff --git a/api/routers/__pycache__/toolkit.cpython-313.pyc b/api/routers/__pycache__/toolkit.cpython-313.pyc index 775dbcb1a3084901b135aef3c96e8c37f75c7226..67dc3d1cd344740d597ae89ba4faa735918680ae 100644 GIT binary patch delta 51 zcmX>dc_NbgGcPX}0}y!S*lpyVpvGFH0px66tmebWSiJdzx*juQ@nm^zS;pGUw%UD+ E0C`ysI{*Lx delta 54 zcmX>Rc{-B&GcPX}0}yB*wcW@)L5<@UTWE1=k!Q;01!_Kwj3t}js_QW`mQ0q>mSwEl JY^B}D2ms&v5dZ)H diff --git a/api/routers/toolkit.py b/api/routers/toolkit.py index 7185226..9ba3b3b 100644 --- a/api/routers/toolkit.py +++ b/api/routers/toolkit.py @@ -160,7 +160,7 @@ async def manage_toolkit(reqData: dict = Depends(requestDict)): logger.info(f"Управление инструментами") response = {"status": "error"} action = reqData.get("body").get("action") - userId = reqData.get("body").get("UserId") + userId = reqData.get("body").get("userId") toolkitData = reqData.get("body").get("formData") if "category_id" in toolkitData: toolkitData["category_id"] = int(toolkitData.get("category_id")) diff --git a/api/static/js/index.js b/api/static/js/index.js index 328c724..ce3770b 100644 --- a/api/static/js/index.js +++ b/api/static/js/index.js @@ -264,7 +264,7 @@ function fillTab(tabId, tabData) { renderJurnalToolkitsTab(tabId, tabData); break; case 'jurnal_service': - renderSimpleTab(tabId, tabData, 'Сервисный журнал'); + renderJurnalServicesTab(tabId, tabData); break; case 'users': renderSimpleTab(tabId, tabData, 'Пользователи системы'); @@ -1454,10 +1454,16 @@ function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', catego }).join(''); const cards = container.querySelectorAll('.toolkit-card'); + let activeModal = null; + cards.forEach(card => { card.addEventListener('click', async event => { + if (activeModal) return; + const toolId = event.currentTarget.dataset.toolid; + activeModal = true; await showToolkitDetailsModal(toolId); + activeModal = null; }); }); } @@ -3503,11 +3509,15 @@ async function initializeToolboxTable(data, toolboxOwn, quantityMonitoring) { }); // Добавляем обработчики для изображений + let activeModal = null; document.querySelectorAll('.toolkit-image-link').forEach(link => { link.addEventListener('click', async (e) => { + if (activeModal) return; e.preventDefault(); const itemId = e.currentTarget.dataset.id; + activeModal = true; await showToolkitDetailsModal(itemId); + activeModal = null; }); }); } @@ -5371,7 +5381,7 @@ function renderRequestsTab(tabId, tabData) { ${request.action} - ${request.created_at} + ${request.created_at} ${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`} ${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'} @@ -5851,8 +5861,8 @@ function renderJurnalToolkitsTab(tabId, tabData) { ${request.action} ${statusBadge} - ${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}
${request.created_at} - ${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`}
${request.decided_at} + ${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}
${request.created_at} + ${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`}
${request.decided_at} ${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'} ${request.target_toolbox_id ? (toolboxMap[request.target_toolbox_id] || `Склад ${request.target_toolbox_id}`) : '-'} ${request.toolkit_id ? (toolkitMap[request.toolkit_id] || `Инструмент ${request.toolkit_id}`) : '-'} @@ -5889,6 +5899,334 @@ function renderJurnalToolkitsTab(tabId, tabData) { // Первоначальный рендеринг таблицы renderRequestsTable(); } + +function renderJurnalServicesTab(tabId, tabData) { + const tabContent = document.getElementById(`${tabId}-tab-content`); + const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`); + + const { requests, users, categories, startDate, endDate } = tabData; + + if (requests.length === 0) { + tabContent.innerHTML = ` + + `; + return; + } + + // Собираем списки для фильтров + const initUsers = [...new Set(requests.map(r => r.user_id))].filter(id => id !== null); + const userMap = {}; + users.forEach(user => { + userMap[user.id] = user.username; + }); + + const categoriesMap = {}; + categories.forEach(cat => { + categoriesMap[cat.id] = { title: cat.title, description: cat.description }; + }); + + // Собираем типы действий (ключи из details) + const actionTypes = [...new Set(requests.map(r => { + const details = r.details; + return Object.keys(details)[0]; // Берем первый ключ как тип действия + }))]; + + const savedFilters = loadFromStorage(tabId); + // Фильтры + let currentFilters = { + user: savedFilters?.user || 'all', + action: savedFilters?.action || 'all' + }; + + // Рендерим дополнительный контейнер с фильтрами + tabOptionalContent.innerHTML = ` +
+ +
+
+
+
+ + + + +
+
+
+ +
+
+
+
+
+ + + + +
+
+
+
+ + +
+
+
+ + Дата начала: + + +
+
+ + Дата окончания: + + +
+
+ +
+
+
+
+ `; + + const filterResetBtn = document.getElementById(`${tabId}-filter-reset-btn`); + filterResetBtn.addEventListener('click', () => { + currentFilters = { + user: 'all', + action: 'all' + }; + document.getElementById(`${tabId}-user-filter`).value = currentFilters.user; + document.getElementById(`${tabId}-action-filter`).value = currentFilters.action; + saveToStorage(tabId, currentFilters); + renderServicesTable(); + }); + + const startDateInput = document.getElementById(`${tabId}-date-from`); + const endDateInput = document.getElementById(`${tabId}-date-to`); + startDateInput.value = startDate; + endDateInput.value = endDate; + + const refreshDateBtn = document.getElementById(`${tabId}-date-update-btn`); + refreshDateBtn.addEventListener('click', async () => { + const newStartDate = startDateInput.value; + const newEndDate = endDateInput.value; + const newDateRequestData = { + tabId: tabId, + startDate: newStartDate, + endDate: newEndDate + }; + if (newStartDate && newEndDate) { + tabContent.innerHTML = ` + + `; + const newPeriodData = await apiRequest('/', newDateRequestData); + if (newPeriodData.status == 'ok') { + renderJurnalServicesTab(tabId, newPeriodData.data); + } + } + }); + + // Рендерим основной контейнер с таблицей сервисных событий + tabContent.innerHTML = ` +
+
+
+ + + + + + + + + + + + +
ДатаПользовательДействиеДетали
+
+ +
+
+ `; + + // Функция для фильтрации событий + function filterServices() { + let filtered = requests; + + // Фильтр по пользователю + if (currentFilters.user !== 'all') { + if (currentFilters.user === 'system') { + filtered = filtered.filter(r => r.user_id === null); + } else { + filtered = filtered.filter(r => r.user_id == currentFilters.user); + } + } + + // Фильтр по типу действия + if (currentFilters.action !== 'all') { + filtered = filtered.filter(r => { + const details = r.details; + return Object.keys(details)[0] === currentFilters.action; + }); + } + + return filtered; + } + + // Функция для рендеринга строк таблицы + function renderServicesTable() { + const tbody = document.getElementById(`${tabId}-services-body`); + const noServicesDiv = document.getElementById(`${tabId}-no-services`); + const filteredServices = filterServices(); + + if (filteredServices.length === 0) { + tbody.innerHTML = ''; + noServicesDiv.style.display = 'block'; + return; + } + + noServicesDiv.style.display = 'none'; + + tbody.innerHTML = filteredServices.map(service => { + const actionType = Object.keys(service.details)[0]; + const actionData = service.details[actionType]; + + // Определяем пользователя + let userName = 'Система'; + if (service.user_id) { + userName = userMap[service.user_id] || `Пользователь ${service.user_id}`; + } + + // Форматируем детали в зависимости от типа действия + let detailsHtml = ''; + + if (actionType === 'Авторизован пользователь') { + // Для авторизации + detailsHtml = ` +
${actionData}
+ `; + } else if (actionType.includes('Добавлен') || actionType.includes('Обновлен') || actionType.includes('Добавлена')) { + // Для добавления/обновления сущностей + const entityName = actionData.title || actionData.username || actionData.login || ''; + detailsHtml = ` +
${entityName}
+ ${actionData.description ? `
${actionData.description}
` : ''} + ${actionData.id ? `
ID: ${actionData.id}
` : ''} + `; + + // Для инструментов добавляем дополнительные поля + if (actionData.specifications && Object.keys(actionData.specifications).length > 0) { + detailsHtml += `
Характеристики:
`; + detailsHtml += `
`; + for (const [key, value] of Object.entries(actionData.specifications)) { + detailsHtml += `
${key}: ${value}
`; + } + detailsHtml += `
`; + } + + if (actionData.external_link) { + detailsHtml += ``; + } + + if (actionData.quantity_min || actionData.quantity_min_extra) { + detailsHtml += `
Мониторинг остатков:
`; + if (actionData.quantity_min && actionData.quantity_min_extra) { + detailsHtml += `
Минимальное количество: ${actionData.quantity_min}
`; + detailsHtml += `
Минимальное критическое количество: ${actionData.quantity_min_extra}
`; + } else if (actionData.quantity_min && !actionData.quantity_min_extra) { + detailsHtml += `
Минимальное количество: ${actionData.quantity_min}
`; + } else if (actionData.quantity_min_extra && !actionData.quantity_min) { + detailsHtml += `
Минимальное критическое количество: ${actionData.quantity_min_extra}
`; + } + } + + if (actionData.category_id) { + detailsHtml += `
Категория: ${categoriesMap[actionData.category_id].title} [${categoriesMap[actionData.category_id].description}]
`; + } + + if (actionData.image) { + detailsHtml += `
Изображения:
`; + detailsHtml += `
Основное:
`; + detailsHtml += `
Основное изображение инструмента`; + if (actionData.image.additional) { + detailsHtml += `
Дополнительные:
`; + detailsHtml += `
`; + actionData.image.additional.forEach(img => { + detailsHtml += `
Дополнительное изображение инструмента
`; + }); + detailsHtml += `
`; + } + detailsHtml += `
`; + } + } + + return ` + + + ${service.created_at} + + ${userName} + + ${actionType} + + + ${detailsHtml} + + + `; + }).join(''); + } + + // Назначаем обработчики событий для фильтров + document.getElementById(`${tabId}-user-filter`).addEventListener('change', function () { + currentFilters.user = this.value; + saveToStorage(tabId, currentFilters); + renderServicesTable(); + }); + + document.getElementById(`${tabId}-action-filter`).addEventListener('change', function () { + currentFilters.action = this.value; + saveToStorage(tabId, currentFilters); + renderServicesTable(); + }); + + // Устанавливаем сохраненные значения фильтров + if (savedFilters) { + document.getElementById(`${tabId}-user-filter`).value = currentFilters.user; + document.getElementById(`${tabId}-action-filter`).value = currentFilters.action; + } + + // Первоначальный рендеринг таблицы + renderServicesTable(); +} + document.addEventListener('DOMContentLoaded', async () => { await getCookieData(); diff --git a/db/handlers/__pycache__/records.cpython-313.pyc b/db/handlers/__pycache__/records.cpython-313.pyc index dd10b1ed710b062c1e1738731d5918064d71bb76..efdd21be511e88b547b445ab1eb5d3d3e18abfa2 100644 GIT binary patch delta 1332 zcma)*T}T{P6vyYz&aV5>)&15TcV@@$jK+_IiC{D;M6C^}4_aE{+BBQhm^B|JcU+>4 z$twAhD6#fX`p`lfNC-lqmhD3eEwscyUP=)kgrUVg6?}+DZ1Ev^$US5HK=P0a^Sl3h z?>Xn*Ip^Mg$H@33$zFH6og8aF^*1J!gFj^7m&q*5$p(qRWOO2X)=ocUtMns8EK{I- zV6}BKooa^P29Sq#J%Me^9k^l$|a19I6n#&29T(x*}@ZjGOtf z;wc8XR-I}cW)^L91noX$P_yYR>B%VGzE*KwRi?415;CSD`*o{Ko!ju=IZ zXC@D(3F!crR!rW38Z9LD*t;A(c1`GVirj!e9bGQ!?6<0JBM9Cj!K{@H^t+T9(7qxz zGfnrvRon1dX5OzoC$Rtd`S7T1h8d!Y;z2NR;G*H{ypsJz20?=x|$OkuBd& z8Jcr~Fd=zJ0mM~5d?(4SJ*yULtO%Y-he(MT<(pzJyesEQrCTwwxOhItDcr88`^>1H z$lWVU+puvyaVGEU>LpQI5Va+-Zb7VD6zdng4QnpW?fcIIQo(dOLhc@o zv}oMpQqoc>JT5C|dfppusTStTdDQz!YlO^K64cdJq*mA(wmb>(Z2n0eK^?X*O{47S zD&MgpWACC^YkCuL3vnB9hrtkflZhc(j^p+NNDIPzKzC7L9NLO7Gctt=fe%Elhm%dE z(f>Kg8eVIZt)XuNYaHw5gbnOA-KOA+CY?-yqgir3WKJx7oL&Pp&1%juLPF0uq-pvJ DEma&9 delta 813 zcmZqdW_;Gb$oH9-mx}=iX05i(6xQ3ww@_|!jw;9G?Q)kHqb7IDckpR)6omohi^4%f z)MiD65LUKGAiLCI@NYn#FRDp>4&D}=zjEpvuxlL3U zIVT&Kh;bBgq_b*r-4dPLXeP@3@;gXX-sT#U*^F%9KVQ0D~o+Hb2CfOVymclldULIv^-FcCX?SwIS|cR)Ce-7 z2}Cr5h!zmh3M6i^q+}+S6lsC@;9%+iu|S%OTtS2;i0Fb5pwKDSot){SCitB}mR0ft yh=3^Un!L`XSOcsQRrf6po80`A(wtPgqJ=>1j36WQCVRSS@@g?keqsO;U;_YqcCe2C diff --git a/db/handlers/__pycache__/toolkit.cpython-313.pyc b/db/handlers/__pycache__/toolkit.cpython-313.pyc index 047be3c2211faf0471600a9b0a55d1010926afef..01a5e170531a89fc72d977c4822f379bc78694cd 100644 GIT binary patch delta 231 zcmccL%($bOk@qt%FBbz4B<0#=&fCb#DZ!YuSzIECjhQ>haPmhEsmUAU9J!x!Ft{`M zf|X38k_eja4|9(Z9bv6jEUd&0Pl5&kc$o>mmR_`h=xz@ROzhO zXFO=i=qtl=(9F@+PoMFyF{7_M%V85$Ap3}v3$L#V!wGFi4j^^X+>r-Jon~hARbn~K z!UAG*INAf%on>eA(`7l!Aqry4GlEDZMxgXr6)rz5=CgW+eyYspfZ|%r=M-3hvgcG; PL2NA+AbayfRX-U3l~q7A delta 259 zcmdnd%y_?>5PnOldBXo7!@`zQs81_wA;K^aT$}e-H!78wL5Efunehnlk!ovK%yXwDq%LJZ#M9tHW~GL>0(BBIUyC zYshdy+mQoEoiumk0aB-#8GZFxPP4Fp*c?tgKy_zD82zkR&WehH*gA|LQlAkheb$i6 s&z$+JwV|I8^EsfnIrBMPR-o)TBUTXGoCU~cN@kess423!LDfPA0Gq>11^@s6 diff --git a/db/handlers/records.py b/db/handlers/records.py index a4129e4..190836f 100644 --- a/db/handlers/records.py +++ b/db/handlers/records.py @@ -287,3 +287,35 @@ class ServiceRecordsHandler: except Exception as e: logger.error(f"Ошибка получения записей: {str(e)}") return False + + async def getLogs(startDate: date, endDate: date): + from db import CRUD + + try: + start_dt = datetime.combine(startDate, time.min) + end_dt = datetime.combine(endDate, time.max) + + query = ( + select(ServicesRecords) + .where( + ServicesRecords.created_at.between(start_dt, end_dt), + ) + .order_by(ServicesRecords.created_at.desc()) + ) + + logger.debug("Получение записей за период %s - %s", startDate, endDate) + + records = await CRUD.read(query, True) + + logger.debug( + "%d записей за период %s - %s успешно получены", + len(records), + startDate, + endDate, + ) + + return [record.toDict() for record in records] + + except Exception: + logger.exception("Ошибка получения записей") + return [] diff --git a/db/handlers/toolkit.py b/db/handlers/toolkit.py index ab5b8cd..56b71b1 100644 --- a/db/handlers/toolkit.py +++ b/db/handlers/toolkit.py @@ -160,7 +160,7 @@ class ToolkitHandler: f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}" ) await ServiceRecordsHandler.add( - user_id, {f"Обновлен инструмент {toolkit.title}": editedToolkit.toDict()} + user_id, {f"Обновлен инструмент": editedToolkit.toDict()} ) return editedToolkit.toDict()