Add new CLI command for file name extraction and hash generation
- Implemented `get_filenames` command in `batch_extractor.py` to extract file names from a specified directory and generate a dated hash for each file. - Updated README.md with instructions for using the new command. - Enhanced PDF generation in `build_pdf.py` by modifying file naming conventions to include article and size. - Added example PowerShell script for batch processing tasks. - Introduced new example Excel file for user reference.
This commit is contained in:
21
README.md
21
README.md
@@ -32,6 +32,27 @@ pip install -r requirements.txt
|
||||
|
||||
Процесс разделен на три простых шага в терминале:
|
||||
|
||||
### ШАГ 0: Подготовить пакетнйы скрипт, если файлов больше чем 1.
|
||||
|
||||
Нужно сохранить все файлы с КМ в одну папку.
|
||||
|
||||
потом запустить команду, которая достанет имена файлов из этой директории:
|
||||
|
||||
```bash
|
||||
python batch_extractor.py get-filenames "C:\путь\к\архиву\" "C:\путь\к\временным файлам\filenames.xlsx"
|
||||
```
|
||||
|
||||
эта команда создаст файл структуры Префикс/Имя файла
|
||||
|
||||
затем надо открыть файл filenames_exaple.xlsx из папки resources и дополнить созданный файл формулами, которые создадут полную питон команду для экстракции КМ
|
||||
|
||||
Затем открыть файл resources\run_task_example.ps1 - и вставить в него получившиеся команды
|
||||
|
||||
### Важно!! Обратить внимание на тип файла, для pdf надо использовать команду from-pdf и аналогично для zip. Кроме того, обязательно надо заменить путь к файлам (он забит в формуле)
|
||||
|
||||
потом можно запустить скрипт пакетной обработки.
|
||||
|
||||
|
||||
### ШАГ 1: Извлечение кодов в Excel (Создание списка КМ)
|
||||
|
||||
Возьмите файл, который вы скачали из системы «Честный ЗНАК», и натравите на него утилиту-экстрактор.
|
||||
|
||||
@@ -6,6 +6,23 @@ from read_image import read_datamatrix_zxing, extract_barcodes_from_pdf
|
||||
from pathlib import Path
|
||||
import click
|
||||
import render_eps
|
||||
import hashlib
|
||||
import random
|
||||
from datetime import datetime
|
||||
|
||||
def get_dated_hash(text: str) -> str:
|
||||
"""
|
||||
Генерирует хэш с префиксом даты в формате ГГ_ММ_ДД.
|
||||
Пример вывода: 26_02_21_d6f7a6b2c1
|
||||
"""
|
||||
# 1. Получаем текущую дату в формате ГГ_ММ_ДД (например, 26_02_21)
|
||||
date_prefix = datetime.now().strftime("%y_%m_%d")
|
||||
text = f"{text}_{random.randint(0,100000000)}"
|
||||
# 2. Генерируем хэш-часть (SHA-256, 10 символов)
|
||||
hash_part = hashlib.sha256(text.encode()).hexdigest()[:10]
|
||||
|
||||
# 3. Соединяем через нижнее подчеркивание
|
||||
return f"{date_prefix}_{hash_part}"
|
||||
|
||||
def extract_eps_from_zip(zip_path: str) -> list:
|
||||
"""
|
||||
@@ -124,5 +141,15 @@ def from_pdf(input_pdf: Path, output_xlsx: Path):
|
||||
except Exception as e:
|
||||
click.secho(f"Ошибка при обработке PDF: {e}", fg="red")
|
||||
|
||||
@cli.command(help="Подготавливает excel с именами файлов в директории")
|
||||
@click.argument('input_dir', type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path))
|
||||
@click.argument('output_xlsx', type=click.Path(dir_okay=False, writable=True, path_type=Path))
|
||||
def get_filenames(input_dir: Path, output_xlsx: Path):
|
||||
# Используем .glob('*') или .iterdir()
|
||||
filenames = [f.name for f in input_dir.iterdir() if f.is_file()]
|
||||
filenames_with_hashes = [(get_dated_hash(f), f) for f in filenames]
|
||||
save_to_excel(filenames_with_hashes, str(output_xlsx))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
|
||||
39
build_pdf.py
39
build_pdf.py
@@ -17,6 +17,7 @@ from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
|
||||
from read_image import read_datamatrix_zxing
|
||||
import render_eps
|
||||
import treepoem
|
||||
import html
|
||||
|
||||
import click
|
||||
from pathlib import Path
|
||||
@@ -184,7 +185,7 @@ def place_text(c: canvas.Canvas, text: str, font_name: str,
|
||||
|
||||
while True:
|
||||
# Заменяем явные переносы на HTML-тег для ReportLab
|
||||
text_for_pdf = current_text.replace('\n', '<br/>')
|
||||
text_for_pdf = html.escape(current_text)
|
||||
p = Paragraph(text_for_pdf, style)
|
||||
|
||||
# Вычисляем реальные размеры
|
||||
@@ -262,13 +263,13 @@ def draw_label_page(c: canvas.Canvas, font_name: str, dm_image: ImageReader, lab
|
||||
eac_image_y = CHZ_image_y
|
||||
c.drawImage(load_png('resources/eac-conformity-mark-seeklogo.png'), eac_image_x, eac_image_y, width=eac_image_h, height=CHZ_image_h, mask='auto')
|
||||
|
||||
page_num_h = 2 * mm
|
||||
page_num_h = 3 * mm
|
||||
page_num_y = CHZ_image_y - page_num_h - 0.3 * mm
|
||||
|
||||
place_text(c, f'№ {label_data.page_num}', font_name,
|
||||
x=img_x, y=page_num_y, width=10 * mm, height=page_num_h,
|
||||
font_size=8, font_min = 3, font_type='bold', align='center')
|
||||
#c.drawBoundary(c, img_x, page_num_y, width=10 * mm, height=page_num_h)
|
||||
x=img_x, y=page_num_y, width=20 * mm, height=page_num_h,
|
||||
font_size=8, font_min = 3, font_type='bold', align='left')
|
||||
#c.drawBoundary(c, img_x, page_num_y, width=20 * mm, height=page_num_h)
|
||||
|
||||
# Ниже на левой панели у вас останется место под логотипы ЧЗ, EAC и №1
|
||||
# ... (резерв места от y=0 до y=10мм)
|
||||
@@ -295,28 +296,28 @@ def draw_label_page(c: canvas.Canvas, font_name: str, dm_image: ImageReader, lab
|
||||
ean_barcode_h += 0.5
|
||||
|
||||
# 5. Артикул (одна строка, жирно)
|
||||
art_h = 2 * mm
|
||||
art_h = 3 * mm
|
||||
art_y = ean_barcode_y + ean_barcode_h
|
||||
place_text(c, f"арт.: {label_data.article}", font_name,
|
||||
x=right_x, y=art_y, width=right_w, height=art_h,
|
||||
font_size=5, font_type='bold', align='center')
|
||||
font_size=7, font_type='bold', align='center')
|
||||
|
||||
# 4. Размер (одна строка, жирно)
|
||||
size_h = 2 * mm
|
||||
size_h = 3 * mm
|
||||
size_y = art_y + art_h
|
||||
place_text(c, f"размер: {label_data.size}", font_name,
|
||||
x=right_x, y=size_y, width=right_w, height=size_h,
|
||||
font_size=5, font_type='bold', align='center')
|
||||
font_size=7, font_type='bold', align='center')
|
||||
|
||||
# 3. Цвет (одна строка, жирно)
|
||||
color_h = 2 * mm
|
||||
color_h = 3 * mm
|
||||
color_y = size_y + size_h
|
||||
place_text(c, f"цвет: {label_data.color}", font_name,
|
||||
x=right_x, y=color_y, width=right_w, height=color_h,
|
||||
font_size=5, font_type='bold', align='center')
|
||||
font_size=7, font_type='bold', align='center')
|
||||
|
||||
# 2. Описание (Крупно, жирно, центр, занимает всё среднее пространство)
|
||||
desc_h = 16 * mm
|
||||
desc_h = 13 * mm
|
||||
desc_y = color_y + color_h
|
||||
place_text(c, f"{label_data.description}", font_name,
|
||||
x=right_x, y=desc_y, width=right_w, height=desc_h,
|
||||
@@ -360,6 +361,7 @@ def process_batch(base64_codes: list[str], excel_path: str, output_dir: str):
|
||||
print(f"Ошибка парсинга кода {b64[:10]}... : {e}")
|
||||
|
||||
font_name = init_pdf_font()
|
||||
total_files = len(grouped_codes)
|
||||
|
||||
# 3. Оркестрация: 1 GTIN = 1 Многостраничный PDF файл
|
||||
for search_key, raw_bytes_list in grouped_codes.items():
|
||||
@@ -379,7 +381,7 @@ def process_batch(base64_codes: list[str], excel_path: str, output_dir: str):
|
||||
label_data = LabelData.from_excel_row(target_row)
|
||||
|
||||
# 3.2. Создаем холст PDF для группы
|
||||
pdf_path = os.path.join(output_dir, f"Labels_{search_key}.pdf")
|
||||
pdf_path = os.path.join(output_dir, f"{label_data.article}_{label_data.size}_{search_key}.pdf")
|
||||
# Размер страницы 58х40 мм
|
||||
c = canvas.Canvas(pdf_path, pagesize=(58*mm, 40*mm))
|
||||
|
||||
@@ -398,9 +400,13 @@ def process_batch(base64_codes: list[str], excel_path: str, output_dir: str):
|
||||
# Рисуем страницу, передавая ПОЛНЫЙ КОД МАРКИРОВКИ вместо короткого search_key
|
||||
draw_label_page(c, font_name, dm_image, label_data, readable_km)
|
||||
|
||||
print(' ', end='\r')
|
||||
print(f'Обработана страница {label_data.page_num} из {len(raw_bytes_list)}', end='\r')
|
||||
|
||||
# Сохраняем и закрываем сгенерированный PDF документ
|
||||
c.save()
|
||||
print(f" -> Сохранен файл: {pdf_path}")
|
||||
total_files -= 1
|
||||
click.echo(f" -> Сохранен файл: {pdf_path} \n Осталось {total_files}")
|
||||
|
||||
|
||||
@click.command(help="Генерирует PDF-этикетки на основе извлеченных Base64-кодов и Excel-шаблона.")
|
||||
@@ -456,3 +462,8 @@ def cli(codes_xlsx: Path, template_xlsx: Path, output_dir: Path):
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
# Left priamry for testing reasons!
|
||||
# test_str = Path('c:/Python/CRPT/LabelExtractor/data/штучка_на_печать.xlsx')
|
||||
# template_path = Path('c:/Python/CRPT/LabelExtractor/data/Шаблон для загрузки этикеток.xlsx')
|
||||
# output_path = Path('c:/Python/CRPT/LabelExtractor/data/output/units')
|
||||
# cli(test_str,template_path, output_path)
|
||||
BIN
resources/filenames_example.xlsx
Normal file
BIN
resources/filenames_example.xlsx
Normal file
Binary file not shown.
30
resources/run_task_example.ps1
Normal file
30
resources/run_task_example.ps1
Normal file
@@ -0,0 +1,30 @@
|
||||
# 1. Устанавливаем кодировку вывода (UTF8)
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 2. Переходим в корневую директорию проекта, где лежит batch_extractor.py
|
||||
# Скрипт находится в \data\, поэтому поднимаемся на один уровень вверх
|
||||
Set-Location "$PSScriptRoot\.."
|
||||
|
||||
# 3. Активация виртуального окружения
|
||||
& "c:/Python/CRPT/LabelExtractor/.venv/Scripts/Activate.ps1"
|
||||
|
||||
Write-Host "--- Окружение активировано. Начинаю обработку файлов... ---" -ForegroundColor Green
|
||||
|
||||
# 4. Список команд
|
||||
# Используем '--' перед аргументами, если пути содержат сложные символы
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск001, р. XL 110 шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975443_quantity_110.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск001, р. XL _04639970975443_110.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск001, р. XXL 20шт _c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975450_quantity_20.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск001, р. XXL _04639970975450_20.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск002, р. M 20 шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975467_quantity_20.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск002, р. M _04639970975467_20.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск002, р. XL 90шт _c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975481_quantity_90.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск002, р. XL _04639970975481_90.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск002, р. XXL 70шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975498_quantity_70.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск002, р. XXL _04639970975498_70.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск003, р. L 35шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975511_quantity_35.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск003, р. L _04639970975511_35.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск003, р. M 20 шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975504_quantity_20.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск003, р. M _04639970975504_20.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск003, р. XL 60 шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975528_quantity_60.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск003, р. XL _04639970975528_60.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск003, р. XXL 50 шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975535_quantity_50.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск003, р. XXL _04639970975535_50.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск004, р. L 10_a8f6b321-2601-4664-bcc0-fad7b529db76_gtin_04639970975559_quantity_10.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск004, р. L _04639970975559_10.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск004, р. M 5шт_c090cb9d-02fa-412b-ac91-ed7446f48d6d_gtin_04639970975542_quantity_5.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск004, р. M _04639970975542_5.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск004, р. XL 30шт _a8f6b321-2601-4664-bcc0-fad7b529db76_gtin_04639970975566_quantity_30.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск004, р. XL _04639970975566_30.xlsx"
|
||||
python batch_extractor.py from-pdf "C:\Python\CRPT\LabelExtractor\data\тнск004, р. XXL 20 шт_a8f6b321-2601-4664-bcc0-fad7b529db76_gtin_04639970975573_quantity_20.pdf" "C:\Python\CRPT\LabelExtractor\data\f\output\тнск004, р. XXL _04639970975573_20.xlsx"
|
||||
|
||||
|
||||
Write-Host "--- Все задачи выполнены успешно! ---" -ForegroundColor Cyan
|
||||
Binary file not shown.
Reference in New Issue
Block a user