This commit is contained in:
2026-04-12 01:35:19 +02:00
parent 91a8b85f38
commit d04e02020c
70 changed files with 8634 additions and 207 deletions

View File

@@ -0,0 +1,122 @@
# Generowanie projektów graficznych z zamówień
Jesteś silnikiem generowania projektów graficznych dla zamówień w orderPRO. Twoje zadanie to znaleźć zamówienia wymagające generacji, zinterpretować dane klienta i uruchomić odpowiedni skrypt.
## Przepływ
### 1. Połącz się z bazą danych
Wczytaj dane z pliku `.env` w katalogu projektu:
- `DB_HOST_REMOTE` — host bazy (użyj remote, nie localhost)
- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`, `DB_PORT`
Użyj `"C:/xampp/mysql/bin/mysql.exe"` do zapytań (z flagą `--default-character-set=utf8mb4`).
### 2. Pobierz aktywne mapowania
```sql
SELECT id, product_name_pattern, script_name, output_dir
FROM project_mappings
WHERE is_active = 1;
```
Jeśli brak mapowań — poinformuj użytkownika i zakończ.
### 3. Znajdź zamówienia do generacji (z dopasowaniem do mapowań)
Schemat bazy danych — kluczowe kolumny:
- `orders.status_code` — aktualny kod statusu wewnętrznego orderPRO
- `order_statuses.code` — kody statusów, wiązanie: `orders.status_code = order_statuses.code`
- `order_statuses.group_id``order_status_groups.id` — grupy statusów
- `order_items.original_name` — nazwa produktu
- `order_items.personalization` — dane personalizacji (plain text, linie oddzielone \n)
- `order_items.project_generated` — 0 = nie wygenerowano, 1 = wygenerowano
- `order_addresses.address_type` — typ adresu ('customer', 'delivery', 'invoice')
- `order_addresses.name` — pełne imię i nazwisko kupującego
Jedno zapytanie łączy zamówienia z mapowaniami (JOIN eliminuje produkty bez mapowania):
```sql
SELECT
oi.id AS item_id,
oi.original_name AS product_name,
oi.personalization,
oi.quantity,
o.id AS order_id,
o.internal_order_number,
o.status_code AS status_code,
oa.name AS buyer_name,
pm.script_name,
pm.output_dir
FROM order_items oi
JOIN orders o ON oi.order_id = o.id
JOIN order_statuses os ON o.status_code = os.code
JOIN order_status_groups osg ON os.group_id = osg.id
JOIN project_mappings pm ON pm.is_active = 1
AND oi.original_name LIKE CONCAT('%', pm.product_name_pattern, '%')
LEFT JOIN order_addresses oa ON o.id = oa.order_id AND oa.address_type = 'customer'
WHERE osg.id = 2
AND oi.project_generated = 0
AND oi.personalization IS NOT NULL
AND oi.personalization <> ''
ORDER BY o.id;
```
Jeśli brak wyników — poinformuj użytkownika że nie ma zamówień do generacji pasujących do aktywnych mapowań.
### 5. Zinterpretuj dane klienta (AI normalizacja)
Dla każdego pasującego produktu:
- Przeczytaj pole `personalization` z `order_items`
- Zinterpretuj dane kontekstowo. Typowe dane to:
- **Imię żeńskie i męskie** — mogą być w formacie "Kasia i Tomek", "Imiona: Kasia, Tomek", "Imię żeńskie: Kasia\nImię męskie: Tomek", "Imiona młodej pary: Kasia i Tomek", itp.
- **Data uroczystości** — format DD.MM.YYYY, może być opisana jako "Data: 30.04.2026" lub "Data uroczystości: 30.04.2026" lub po prostu "30.04.2026"
- **Życzenia** — opcjonalny tekst (np. "Na zdrowie!", "Dziękujemy!"). Jeśli brak — zostaw domyślne z szablonu
- Ignoruj pola takie jak "Kolor tekstu", "Zakrętka" — te dotyczą produkcji, nie projektu
- **Nazwa klienta** (do nazwy pliku wyjściowego) — z `order_addresses.name` (pole `buyer_name` w zapytaniu)
### 6. Przedstaw plan i czekaj na potwierdzenie
Wyświetl tabelę:
```
Zamówienie | Produkt | Imię żeńskie | Imię męskie | Data | Życzenia | Klient (nazwa pliku)
OP000123 | Buteleczka... | Kinga | Łukasz | 30.04.2026 | (domyślne) | Kinga Klimczak
```
Zapytaj: "Wygenerować projekty? (tak/nie)"
### 7. Uruchom generowanie
Przed generowaniem sprawdź czy Photoshop jest uruchomiony:
```bash
tasklist //FI "IMAGENAME eq Photoshop.exe" 2>/dev/null | grep -i photoshop
```
Jeśli Photoshop nie jest uruchomiony — poinformuj użytkownika i poczekaj aż go uruchomi.
Dla każdego potwierdzonego produktu:
1. Uruchom skrypt: `python tools/generowanie/{script_name} --imie_zenskie "X" --imie_meskie "Y" --data "DD.MM.YYYY" --klient "Imię Nazwisko"` (dodaj `--zyczenia` jeśli klient podał niestandardowe)
2. Jeśli mapowanie ma `output_dir` — skrypt powinien zapisać tam (jeśli obsługuje)
### 8. Oznacz w bazie
Po udanym generowaniu:
```sql
UPDATE order_items
SET project_generated = 1, project_generated_at = NOW()
WHERE id = {item_id};
```
### 9. Raport końcowy
Wyświetl podsumowanie:
- Ile projektów wygenerowano
- Ile pominięto (brak mapowania, brak personalizacji)
- Ewentualne błędy
## Ważne zasady
- **Zawsze pytaj o potwierdzenie** przed generowaniem
- **Photoshop musi być uruchomiony** — sprawdź przed generowaniem
- **Nie oznaczaj jako wygenerowane** jeśli skrypt zwrócił błąd
- **Jeśli nie potrafisz zinterpretować personalizacji** — wyświetl surowe dane i zapytaj użytkownika
- Skrypty generujące znajdują się w `tools/generowanie/` w katalogu projektu orderPRO

View File

@@ -0,0 +1,162 @@
# Kreator skryptu generowania projektu graficznego
Tworzysz nowy skrypt Python do generowania projektow graficznych z szablonu PSD. Skrypt bedzie uzywal Photoshop API (photoshop-python-api) do podmiany tekstow w Smart Objects z zachowaniem pozycji warstw.
## Wymagania
- Photoshop musi byc uruchomiony — sprawdz: `tasklist //FI "IMAGENAME eq Photoshop.exe" 2>/dev/null | grep -i photoshop`
- Python z pakietem `photoshop-python-api` musi byc zainstalowany
## Przepływ
### 1. Pobierz sciezke do PSD
Uzytkownik poda sciezke do pliku PSD szablonu. Zweryfikuj ze plik istnieje.
### 2. Eksploruj strukture warstw
Otworz PSD w Photoshopie przez Python i rekurencyjnie przejdz wszystkie warstwy:
```python
import photoshop.api as ps
app = ps.Application()
doc = app.open(PSD_PATH)
def explore(container, indent=0):
try:
for al in container.artLayers:
extra = ''
try: extra = f' text="{al.textItem.contents}"'
except: pass
print(f'{" "*indent}art: "{al.name}" kind={al.kind}{extra}')
except: pass
try:
for ls in container.layerSets:
print(f'{" "*indent}set: "{ls.name}"')
explore(ls, indent+1)
except: pass
explore(doc)
```
Jezeli warstwa jest Smart Objectem (kind=5 lub 7) — wejdz do niego:
```python
app.activeDocument.activeLayer = smart_layer
desc = ps.ActionDescriptor()
ref = ps.ActionReference()
ref.putEnumerated(app.stringIDToTypeID('layer'), app.stringIDToTypeID('ordinal'), app.stringIDToTypeID('targetEnum'))
desc.putReference(app.stringIDToTypeID('null'), ref)
app.executeAction(app.stringIDToTypeID('placedLayerEditContents'), desc)
```
Eksploruj zawartosc Smart Object rekurencyjnie (moze byc wiele poziomow zagniezdzenia).
### 3. Wyswietl drzewo warstw
Pokaz uzytkownikowi pelna strukture warstw w czytelnym formacie drzewa, np.:
```
Projekt (Smart Object)
└── Dekoracje (set)
└── Tekst (set)
└── Dane (set)
├── "imie" kind=2 text="Jan"
├── "data" kind=2 text="01.01.2026"
└── "zyczenia" kind=2 text="Wszystkiego najlepszego"
```
kind=2 to warstwy tekstowe — te sa kandydatami na zmienne.
### 4. Ustal zmienne
Zapytaj uzytkownika:
- Ktore warstwy tekstowe maja byc parametrami skryptu?
- Jakie nazwy parametrow CLI? (np. --imie, --data, --zyczenia)
- Ktore parametry sa wymagane, ktore opcjonalne (opcjonalne = zostaw domyslny tekst z szablonu)?
### 5. Uproszczenie warstw (opcjonalnie)
Zaproponuj uproszczenie nazw warstw w PSD:
- Przemianuj warstwy na czytelne nazwy (np. "Warstwa 5" -> "Tekst", "Kasia" -> "imie_zenskie")
- Przemianuj grupy (np. "Warstwa 4" -> "Dane")
- Zapytaj uzytkownika o akceptacje
Jezeli uzytkownik zaakceptuje — wykonaj rename warstw w PSD i zapisz szablon.
### 6. Ustal sciezki
Zapytaj uzytkownika:
- Nazwa skryptu (np. `kubki_wzor1.py`)
- Katalog wyjsciowy `_gotowe/` — domyslnie podfolder `_gotowe` w katalogu szablonu PSD
- Nazwa Smart Object glownego (jezeli PSD ma wiele — ktory jest szablonem)
### 7. Wygeneruj skrypt
Uzyj wzorca z `tools/generowanie/buteleczki_wzor1.py` jako referencji. Kluczowe elementy:
```python
import argparse
import os
import photoshop.api as ps
# Sciezki
SZABLON_PATH = r"..."
GOTOWE_DIR = r"..."
SMART_OBJECT_LAYER = "Projekt"
def open_smart_object(app):
desc = ps.ActionDescriptor()
ref = ps.ActionReference()
ref.putEnumerated(app.stringIDToTypeID('layer'), app.stringIDToTypeID('ordinal'), app.stringIDToTypeID('targetEnum'))
desc.putReference(app.stringIDToTypeID('null'), ref)
app.executeAction(app.stringIDToTypeID('placedLayerEditContents'), desc)
def change_text_preserve_position(layer, new_text):
"""Zmienia tekst warstwy z zachowaniem oryginalnej pozycji."""
bounds_before = [float(b) for b in layer.bounds]
layer.textItem.contents = new_text
bounds_after = [float(b) for b in layer.bounds]
dx = bounds_before[0] - bounds_after[0]
dy = bounds_before[1] - bounds_after[1]
if dx != 0 or dy != 0:
layer.translate(dx, dy)
def generate(**kwargs):
# ... otworz PSD, wejdz do SO, podmien teksty, zapisz kopie
pass
def main():
parser = argparse.ArgumentParser(...)
# ... argumenty na podstawie ustalonych zmiennych
args = parser.parse_args()
generate(...)
if __name__ == "__main__":
main()
```
Wazne:
- **Zawsze** uzywaj `change_text_preserve_position()` zamiast bezposredniego `textItem.contents`
- Nawigacja do warstw przez `layerSets['nazwa'].artLayers['nazwa']`
- Po edycji Smart Object: `so_doc.save()` + `so_doc.close()`
- Zapisz jako kopie: `app.activeDocument.saveAs(output_path, psd_opts, True)`
- Zamknij oryginal bez zapisu: `app.activeDocument.close(ps.SaveOptions.DoNotSaveChanges)`
- Parametr `--klient` zawsze wymagany (nazwa pliku wyjsciowego)
### 8. Przetestuj
Po wygenerowaniu skryptu:
1. Uruchom go z przykladowymi danymi
2. Sprawdz czy PSD zostal wygenerowany
3. Popros uzytkownika o weryfikacje wizualna
### 9. Dodaj mapowanie
Zaproponuj uzytkownikowi dodanie mapowania w panelu (Ustawienia > Mapowanie projektow) — wzorzec nazwy produktu -> nowy skrypt.
## Wazne zasady
- Zamykaj PSD po zakonczeniu (nie zostawiaj otwartych dokumentow)
- Przy bledzie — zamknij wszystkie otwarte dokumenty bez zapisu
- Skrypty zapisuj w `tools/generowanie/` w katalogu projektu orderPRO
- Testuj z realnymi danymi przed zakonczeniem