feature/evaluating #2

Merged
MH.Dmitrii merged 28 commits from feature/evaluating into main 2026-01-09 10:17:48 +00:00
10 changed files with 73 additions and 36 deletions
Showing only changes of commit 5d15ff9f5e - Show all commits

3
run.py
View File

@@ -1,6 +1,6 @@
import server.backend.services.excel as excel import server.backend.services.excel as excel
from server.backend.services.validating_files import validating from server.backend.services.validating_files import validating
from server.backend.api import companies,contractors,storages from server.backend.api import companies,contractors,storages, nomenclature
#_______________ #_______________
from pathlib import Path from pathlib import Path
path = Path("./excel_files") path = Path("./excel_files")
@@ -21,6 +21,7 @@ match args.mode:
companies.companies() companies.companies()
contractors.contractor() contractors.contractor()
storages.storages() storages.storages()
nomenclature.nomenclature(flag=True)
case "orgs": case "orgs":
print("Режим:", args.mode) print("Режим:", args.mode)
companies.companies() companies.companies()

View File

@@ -2,6 +2,8 @@ import requests
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from base64 import b64encode from base64 import b64encode
from server.backend.schemas.pydantic import settings from server.backend.schemas.pydantic import settings
import re
from functools import lru_cache
import pandas as pd import pandas as pd
auth_str = f"{settings.USERNAME}:{settings.PASSWORD}" auth_str = f"{settings.USERNAME}:{settings.PASSWORD}"
b64_auth_str = b64encode(auth_str.encode("utf-8")).decode("utf-8") b64_auth_str = b64encode(auth_str.encode("utf-8")).decode("utf-8")
@@ -32,16 +34,32 @@ def parse_contragents(xml: str):
properties = entry.find('atom:content',NS).find( properties = entry.find('atom:content',NS).find(
'm:properties', NS) 'm:properties', NS)
rows.append({ rows.append({
'Ref_Key': properties.findtext('d:Ref_Key', default=None, namespaces=NS), 'ref_key': properties.findtext('d:Ref_Key', default=None, namespaces=NS),
'Description': properties.findtext('d:Description', default=None, namespaces=NS), 'description': properties.findtext('d:Description', default=None, namespaces=NS),
'Parent_Key': properties.findtext('d:Parent_Key', default=None, namespaces=NS) 'parent_key': properties.findtext('d:Parent_Key', default=None, namespaces=NS)
}) })
df = pd.DataFrame(rows) df = pd.DataFrame(rows)
df = df[df['Parent_Key'] == 'e0eb911c-03a0-11ef-95bd-fa163e7429d8'] df = df[df['parent_key'] == 'e0eb911c-03a0-11ef-95bd-fa163e7429d8']
df['Description'] = df['Description'].str.extract(r'^([^\s(]+)') df['description'] = df['description'].str.extract(r'^([^\s(]+)')
return df return df
except ET.ParseError: except ET.ParseError:
raise raise
def nomenclature(): @lru_cache(maxsize=1)
def nomenclature(flag=False):
xml_data = fetch_contragents() xml_data = fetch_contragents()
root = parse_contragents(xml_data) root = parse_contragents(xml_data)
if flag:
root.to_excel("./excel_files/nomenclature.xlsx")
return root
def processing(df):
df2=nomenclature()
result = df.merge(
df2[['description', 'ref_key']], #берутся столбцы из df2
left_on='arti', #столбец для сравнения в df
right_on='description', #столбец для сравнения в df2
how='left' #left join для df
).drop(columns='description') #удаление временного стобца
not_matched = result.loc[result['ref_key'].isna(), 'arti'].unique()
if len(not_matched) > 0:
raise ValueError(f'Не найдены значения: {not_matched}')
return result

View File

@@ -1,6 +1,6 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo,settings from server.backend.schemas.pydantic import ExcelInfo,settings,Translit
from server.backend.api.nomenclature import nomenclature from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str, real_sum_2:str): def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str, real_sum_2:str):
@@ -9,11 +9,12 @@ def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str, real_sum
df[real_sum_1]+=df[real_sum_2] df[real_sum_1]+=df[real_sum_2]
df = df[[real_arti, real_quantity, real_sum_1]].copy() df = df[[real_arti, real_quantity, real_sum_1]].copy()
df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #arti под regex df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['price'] = df['price'].astype(float) #Float to Int, if exists df['price'] = df['price'].astype(float) #Float to Int, if exists
df['counts'] = df['counts'].astype(int) #Float to Int, if exists df['counts'] = df['counts'].astype(int) #Float to Int, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping
df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:
@@ -22,7 +23,6 @@ def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str, real_sum
errors.append((i, e.errors())) #выводит ошибку и пишет номер строки errors.append((i, e.errors())) #выводит ошибку и пишет номер строки
if errors: if errors:
raise Exception(f"There are some errors with validation in Отчет о реализации, check it ", errors) raise Exception(f"There are some errors with validation in Отчет о реализации, check it ", errors)
return validated_rows return validated_rows
def evaluating(dfs): def evaluating(dfs):
validated_rows_1 = process_sheet(dfs["Отчет о реализации"], real_arti='2',real_quantity='8', real_sum_1='5',real_sum_2='6') # номера столбцов от озона validated_rows_1 = process_sheet(dfs["Отчет о реализации"], real_arti='2',real_quantity='8', real_sum_1='5',real_sum_2='6') # номера столбцов от озона

View File

@@ -1,18 +1,18 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo, settings from server.backend.schemas.pydantic import ExcelInfo, settings,Translit
from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, real_arti:int, real_quantity:int, real_sum_1:int): def process_sheet(df, real_arti:int, real_quantity:int, real_sum_1:int):
df = df[[real_arti, real_quantity, real_sum_1]].copy().dropna() #copy and drop all NA values df = df[[real_arti, real_quantity, real_sum_1]].copy().dropna() #copy and drop all NA values
df = df[(df != 0).all(axis=1)] #drop all 0 values df = df[(df != 0).all(axis=1)] #drop all 0 values
df = df[[real_arti, real_quantity, real_sum_1]].copy() df = df[[real_arti, real_quantity, real_sum_1]].copy()
df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #arti под regex df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['price'] = df['price'].astype(float) #Float to Int, if exists df['price'] = df['price'].astype(float) #Float to Int, if exists
df['counts'] = df['counts'].astype(int) #Float to Int, if exists df['counts'] = df['counts'].astype(int) #Float to Int, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:

View File

@@ -1,19 +1,19 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo,settings from server.backend.schemas.pydantic import ExcelInfo,settings,Translit
from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str): def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str):
df = df[[real_arti, real_quantity, real_sum_1]].copy().dropna() #copy and drop all NA values df = df[[real_arti, real_quantity, real_sum_1]].copy().dropna() #copy and drop all NA values
df = df[(df != 0).all(axis=1)] #drop all 0 values df = df[(df != 0).all(axis=1)] #drop all 0 values
df = df[[real_arti, real_quantity, real_sum_1]].copy() df = df[[real_arti, real_quantity, real_sum_1]].copy()
df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #arti под regex df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['price'] = df['price'].astype(float) #переделка к норм виду и преобразование в float df['price'] = df['price'].astype(float) #переделка к норм виду и преобразование в float
df['counts'] = df['counts'].astype(int) #Float to Int, if exists df['counts'] = df['counts'].astype(int) #Float to Int, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping
df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:

View File

@@ -1,19 +1,19 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo, settings from server.backend.schemas.pydantic import ExcelInfo, settings,Translit
from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, document_type:str): def process_sheet(df, document_type:str):
df = df[['Артикул поставщика', 'Тип документа', 'Кол-во', 'Вайлдберриз реализовал Товар (Пр)']].copy().dropna() #copy and drop all NA values df = df[['Артикул поставщика', 'Тип документа', 'Кол-во', 'Вайлдберриз реализовал Товар (Пр)']].copy().dropna() #copy and drop all NA values
df = df[(df != 0).all(axis=1)] #drop all 0 values df = df[(df != 0).all(axis=1)] #drop all 0 values
df = df[df['Тип документа'] == document_type] #фильтруем по типу документа df = df[df['Тип документа'] == document_type] #фильтруем по типу документа
df = df[['Артикул поставщика', 'Кол-во', 'Вайлдберриз реализовал Товар (Пр)']].copy() df = df[['Артикул поставщика', 'Кол-во', 'Вайлдберриз реализовал Товар (Пр)']].copy()
df.rename(columns={'Артикул поставщика': 'arti', 'Кол-во': 'counts', 'Вайлдберриз реализовал Товар (Пр)': 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={'Артикул поставщика': 'arti', 'Кол-во': 'counts', 'Вайлдберриз реализовал Товар (Пр)': 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #arti под regex df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['price'] = df['price'].astype(float) #Float to Int, if exists df['price'] = df['price'].astype(float) #Float to Int, if exists
df['counts'] = df['counts'].astype(int) #Float to Int, if exists df['counts'] = df['counts'].astype(int) #Float to Int, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'})
df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:

View File

@@ -1,5 +1,6 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo, settings from server.backend.schemas.pydantic import ExcelInfo, settings,Translit
from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str): def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str):
@@ -7,12 +8,12 @@ def process_sheet(df, real_arti:str, real_quantity:str, real_sum_1:str):
df = df[(df != 0).all(axis=1)] #drop all 0 values df = df[(df != 0).all(axis=1)] #drop all 0 values
df = df[[real_arti, real_quantity, real_sum_1]].copy() df = df[[real_arti, real_quantity, real_sum_1]].copy()
df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={real_arti: 'arti', real_quantity: 'counts', real_sum_1: 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #arti под regex df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['price'] = df['price'].str.replace(' ', '', regex=False).str.replace(',', '.', regex=False).astype(float) #переделка к норм виду и преобразование в float df['price'] = df['price'].str.replace(' ', '', regex=False).str.replace(',', '.', regex=False).astype(float) #переделка к норм виду и преобразование в float
df['counts'] = df['counts'].astype(int) #Float to Int, if exists df['counts'] = df['counts'].astype(int) #Float to Int, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping
df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:

View File

@@ -1,5 +1,6 @@
from pydantic import ValidationError from pydantic import ValidationError
from server.backend.schemas.pydantic import ExcelInfo, settings from server.backend.schemas.pydantic import ExcelInfo, settings,Translit
from server.backend.api.nomenclature import processing
import re import re
def process_sheet(df, multiply_price=1, sheet_name=''): def process_sheet(df, multiply_price=1, sheet_name=''):
@@ -7,12 +8,12 @@ def process_sheet(df, multiply_price=1, sheet_name=''):
df = df[(df != 0).all(axis=1)] #drop all 0 values df = df[(df != 0).all(axis=1)] #drop all 0 values
df['Сумма транзакции, ₽'] *= multiply_price #умножаем на -1 для возвратов df['Сумма транзакции, ₽'] *= multiply_price #умножаем на -1 для возвратов
df.rename(columns={'Ваш SKU': 'arti', 'Количество, шт.': 'counts', 'Сумма транзакции, ₽': 'price'}, inplace=True) #переименовываем для pydantic df.rename(columns={'Ваш SKU': 'arti', 'Количество, шт.': 'counts', 'Сумма транзакции, ₽': 'price'}, inplace=True) #переименовываем для pydantic
df['arti'] = df['arti'].astype(str).str.extract(f'({settings.PATTERN})', flags=re.IGNORECASE) #regex implemented df['arti'] = df['arti'].replace(Translit.TRANSLIT, regex=True)
df['arti'] = df['arti'].astype(str).str.upper().str.extract(f'({settings.PATTERN})') #arti под regex
df['price'] = df['price'].astype(float) #To float, if exists df['price'] = df['price'].astype(float) #To float, if exists
df['counts'] = df['counts'].astype(int) #To float, if exists df['counts'] = df['counts'].astype(int) #To float, if exists
df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping df = df.groupby('arti', as_index=False).agg({'counts': 'sum', 'price': 'sum'}) #groupping
df = processing(df) #vlookup for ref_keys
validated_rows, errors = [], [] validated_rows, errors = [], []
for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки for i, row in df.iterrows(): #проходит построчно по df, где i - индекс строки, row - данные строки
try: try:

View File

@@ -4,6 +4,7 @@ class ExcelInfo(BaseModel):
arti:str = Field(..., min_length=5, max_length=12, description="arti of the clothes") arti:str = Field(..., min_length=5, max_length=12, description="arti of the clothes")
counts:int = Field(..., gt=0, description="the quantity of the clothes") counts:int = Field(..., gt=0, description="the quantity of the clothes")
price:float = Field(..., gt=0, description="the price of the clothes") price:float = Field(..., gt=0, description="the price of the clothes")
ref_key:str = Field(..., description="reffering key from db")
class ExcelRealization(BaseModel): class ExcelRealization(BaseModel):
pass pass
class ExcelReturning(BaseModel): class ExcelReturning(BaseModel):
@@ -44,4 +45,19 @@ class Settings(BaseSettings):
env_file=".env", env_file=".env",
env_file_encoding="utf-8" env_file_encoding="utf-8"
) )
class Translit():
TRANSLIT = {
'А': 'A',
'В': 'B',
'Е': 'E',
'К': 'K',
'М': 'M',
'Н': 'H',
'О': 'O',
'Р': 'P',
'С': 'C',
'Т': 'T',
'Х': 'X',
}
settings = Settings() settings = Settings()

View File

@@ -64,7 +64,7 @@ class WBPurchasesHandler(BaseHandler):
if "Sheet1" not in dfs: if "Sheet1" not in dfs:
raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы") raise Exception(f"В файле {self.file_path.name} отсутствуют необходимые листы")
validated_data = wb_purchases_handler.evaluating(dfs) validated_data = wb_purchases_handler.evaluating(dfs)
print("Выкупы WB завершены, валидированных строк:", len(validated_data), "Реализация") print("Выкупы WB завершены, валидированных строк:", len(validated_data), "Реализация", validated_data)
class OZONComHandler(BaseHandler): class OZONComHandler(BaseHandler):
def process(self): def process(self):