Skip to content

Instantly share code, notes, and snippets.

@Przemocny
Created November 9, 2025 19:28
Show Gist options
  • Select an option

  • Save Przemocny/0b19cc52f771b71d62d47e5197ecd625 to your computer and use it in GitHub Desktop.

Select an option

Save Przemocny/0b19cc52f771b71d62d47e5197ecd625 to your computer and use it in GitHub Desktop.
LPD - Direct: Simple Process Language.md

📘 LPD-Direct: Specyfikacja Języka Procesowego

1. Wprowadzenie i Filozofia

LPD-Direct (Logiczny Przepływ Danych) to deklaratywny język pseudokodu służący do projektowania, dokumentowania i automatyzacji procesów biznesowych oraz technologicznych.

Jego głównym celem jest eliminacja bariery komunikacyjnej między ekspertami biznesowymi (wiedzącymi dlaczego coś robimy) a zespołami technicznymi (wiedzącymi jak to zrobić). Składnia wymusza łączenie instrukcji technicznych z ich uzasadnieniem biznesowym.

Fundamenty LPD-Direct:

  • Czytelność: Kod musi być zrozumiały dla osoby nietechnicznej.
  • Kontekst: Każda akcja musi mieć jawnie zdefiniowany cel biznesowy.
  • Pełna Kontrola: Język obsługuje nie tylko sukces ("happy path"), ale też błędy, decyzje ludzkie i sztuczną inteligencję.
  • Jednoznaczność: Każda konstrukcja ma dokładnie jedną interpretację.

2. Struktura Skryptu

Każdy proces opisany w LPD-Direct składa się z trzech głównych sekcji:

  1. Wyzwalacz (TRIGGER): Definicja, co uruchamia proces.
  2. Ciało Procesu: Logiczna sekwencja operacji podzielona na fazy (np. Gromadzenie, Przetwarzanie, Decyzje).
  3. Zakończenie: Jawny sygnał końca przepływu.

2.1. Żelazna Zasada: Podwójny Komentarz

Każde polecenie operacyjne musi być poprzedzone dwoma komentarzami. To nie jest opcjonalne.

  • // Co robimy: (Opis techniczny czynności)
  • // Po co: (Uzasadnienie biznesowe)

Wyjątki: TRIGGER i SYSTEMS nie wymagają podwójnego komentarza (mają wbudowany opis).

// Co robimy: Filtrujemy zamówienia ze statusem 'Anulowane'.
// Po co: Aby nie naliczać prowizji od sprzedaży, która nie doszła do skutku.
FILTER ZAMOWIENIA WHERE Status != 'Anulowane' AS ZAMOWIENIA_SKUTECZNE

2.2. Deklaracja Systemów (SYSTEMS)

Opcjonalna sekcja definiująca zewnętrzne systemy i oprogramowanie używane w procesie. Umieszczana bezpośrednio po TRIGGER, przed pierwszym SET/LOAD.

Składnia:

// SYSTEMS:
// - <Nazwa Systemu>: <Typ> - <Opis, co zawiera/robi>
// - <Nazwa Systemu>: <Typ> - <Opis>

Przykład:

// TRIGGER: SCHEDULE EVERY DAY AT 09:00 CET

// SYSTEMS:
// - ERP Firma: REST API - System fakturowania i zarządzania płatnościami klientów
// - BazaHR: PostgreSQL - Baza danych pracowników i struktury organizacyjnej
// - Salesforce: REST API - CRM do zarządzania leadami i kontaktami
// - Mail Service: SMTP - Serwer pocztowy do wysyłki automatycznych powiadomień
// - Slack: Webhook - Komunikator zespołowy do alertów operacyjnych

// Co robimy: Ustalamy dzisiejszą datę.
// Po co: Do obliczeń terminów.
SET DZISIAJ = CURRENT_DATE()

Cel: Dokumentacja zależności procesu od zewnętrznych systemów - ułatwia onboarding nowych osób i identyfikację punktów awarii.

Format wpisu:

  • Nazwa Systemu: Nazwa używana w aliasach LOAD/SEND
  • Typ: REST API, GraphQL, SQL (PostgreSQL/MySQL), SMTP, Webhook, File System, itp.
  • Opis: Zwięzły opis roli systemu w procesie (1 zdanie)

3. Typy Danych i Literały

3.1. Typy Podstawowe

Typ Przykład Opis
STRING 'Tekst' Tekst w pojedynczych apostrofach
NUMBER 123, 45.67 Liczby całkowite i dziesiętne
BOOLEAN TRUE, FALSE Wartości logiczne
NULL NULL Brak wartości
DATE 2024-01-15 Data w formacie ISO 8601
DATETIME 2024-01-15T09:00:00Z Data i czas w formacie ISO 8601

3.2. Typy Złożone

Typ Przykład Opis
DATASET Rezultat LOAD/FILTER/CALCULATE Tabela danych z nazwanymi kolumnami
LIST [1, 2, 3] Lista wartości

3.3. Jednostki Fizyczne

Jednostki są częścią literału i muszą być oddzielone spacją:

SET PROG_VIP = 5000 EUR
SET TIMEOUT = 30 SECONDS
SET DELAY = 7 DAYS

Dostępne jednostki: EUR, USD, PLN, SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS, PERCENT


4. Konwencje Nazewnicze (OBOWIĄZKOWE)

4.1. Zmienne i Datasety

Format: UPPER_SNAKE_CASE

SET DZISIAJ = CURRENT_DATE()
LOAD API FROM '...' AS WSZYSTKIE_ZALEGLOSCI
FILTER DATA WHERE x > 10 AS WYNIK_FILTROWANIA

4.2. Kolumny w Datasetach

Format: Bez cudzysłowów, PascalCase z podkreślnikami dla złożonych nazw

FILTER SPRZEDAZ WHERE Status = 'Aktywny' AS RESULT
CALCULATE on DATA:
    Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
AS RESULT_Z_MARZA

Wyjątek: Jeśli nazwa kolumny zawiera spacje lub znaki specjalne, użyj pojedynczych apostrofów:

FILTER DATA WHERE 'Full Name' = 'Jan Kowalski' AS RESULT

4.3. Stringi Literalne

Format: Zawsze w pojedynczych apostrofach '...'

SET NAZWA = 'Raport Miesięczny'
SEND ALERT 'Uwaga: wykryto anomalię!' TO 'Slack / #alerts'

5. Operatory

5.1. Operatory Porównania

Operator Znaczenie Przykład
= Równe Status = 'Aktywny'
!= Różne Status != 'Anulowane'
> Większe Kwota > 1000
>= Większe lub równe Kwota >= 1000
< Mniejsze Kwota < 1000
<= Mniejsze lub równe Termin_Platnosci <= DZISIAJ

5.2. Operatory Logiczne

Operator Znaczenie Przykład Precedencja
NOT Negacja NOT (x > 10) 1 (najwyższa)
AND Koniunkcja x > 10 AND y < 20 2
OR Alternatywa Status = 'A' OR Status = 'B' 3 (najniższa)

Nawiasy: Używaj () do wymuszenia kolejności wykonywania.

FILTER DATA WHERE (Status = 'Aktywny' OR Status = 'Testowy') AND Kwota > 1000 AS RESULT

5.3. Operatory Specjalne

Operator Znaczenie Przykład
IS NULL Sprawdza czy wartość jest NULL Email IS NULL
IS NOT NULL Sprawdza czy wartość nie jest NULL Email IS NOT NULL
IS EMPTY Sprawdza czy dataset jest pusty DATASET IS EMPTY
IS NOT EMPTY Sprawdza czy dataset zawiera dane DATASET IS NOT EMPTY

5.4. Operatory Arytmetyczne

Operator Znaczenie Precedencja
+ Dodawanie 2
- Odejmowanie 2
* Mnożenie 1 (najwyższa)
/ Dzielenie 1
% Modulo (reszta z dzielenia) 1

6. Funkcje Wbudowane

6.1. Funkcje Daty i Czasu

Funkcja Zwraca Przykład
CURRENT_DATE() Dzisiejsza data 2024-01-15
CURRENT_DATETIME() Obecna data i czas 2024-01-15T09:30:00Z
ADD_DAYS(date, n) Data + n dni ADD_DAYS(DZISIAJ, 7)
SUBTRACT_DAYS(date, n) Data - n dni SUBTRACT_DAYS(DZISIAJ, 30)
FORMAT_DATE(date, format) Formatowanie daty FORMAT_DATE(DZISIAJ, 'DD-MM-YYYY')
DAYS_BETWEEN(date1, date2) Różnica w dniach DAYS_BETWEEN(Termin, DZISIAJ)

6.2. Funkcje Tekstowe

Funkcja Zwraca Przykład
UPPER(string) Tekst wielkimi literami UPPER('test')'TEST'
LOWER(string) Tekst małymi literami LOWER('TEST')'test'
TRIM(string) Tekst bez spacji na krańcach TRIM(' test ')'test'
LENGTH(string) Długość tekstu LENGTH('test')4
CONCAT(str1, str2, ...) Łączenie tekstów CONCAT('A', 'B')'AB'
SUBSTRING(string, start, len) Wycinek tekstu SUBSTRING('test', 1, 2)'te'

6.3. Funkcje Numeryczne

Funkcja Zwraca Przykład
ROUND(number, decimals) Zaokrąglenie ROUND(3.14159, 2)3.14
CEIL(number) Zaokrąglenie w górę CEIL(3.1)4
FLOOR(number) Zaokrąglenie w dół FLOOR(3.9)3
ABS(number) Wartość bezwzględna ABS(-5)5
MIN(a, b, ...) Minimum MIN(1, 5, 3)1
MAX(a, b, ...) Maksimum MAX(1, 5, 3)5

6.4. Funkcje Agregujące (tylko w CALCULATE)

Funkcja Zwraca Przykład
SUM(column) Suma wartości SUM(Kwota)
AVG(column) Średnia AVG(Ocena)
COUNT(column) Liczba rekordów COUNT(ID)
MIN(column) Minimum MIN(Cena)
MAX(column) Maksimum MAX(Cena)

7. Składnia Konstrukcji

7.1. Wyzwalacze (TRIGGER)

Składnia: // TRIGGER: <typ> <parametry>

Typy wyzwalaczy:

// TRIGGER: SCHEDULE EVERY DAY AT 08:00 CET
// TRIGGER: SCHEDULE EVERY MONDAY AT 09:00 CET
// TRIGGER: SCHEDULE EVERY 1st DAY OF MONTH AT 00:00 UTC
// TRIGGER: ON WEBHOOK RECEIVE (endpoint: '/api/nowy-klient')
// TRIGGER: MANUAL BY USER (Rola: 'Analityk Danych')
// TRIGGER: ON FILE UPLOAD (path: '/uploads/*.csv')

Wyzwalacz jest zawsze jednolinijkowy i znajduje się na początku procesu.


7.2. Zmienne Globalne (SET i OVERRIDE)

SET - Deklaracja Zmiennej

Składnia: SET <ZMIENNA> = <wartość>

Deklaruje nową zmienną globalną. Zmienna nie może być nadpisana przez kolejny SET.

// Co robimy: Zapisujemy dzisiejszą datę w zmiennej.
// Po co: Aby użyć jej później do filtrowania przeterminowanych zadań.
SET DZISIAJ = CURRENT_DATE()

// Co robimy: Definiujemy próg kwotowy dla zamówień VIP.
// Po co: Aby łatwo zmienić tę wartość w przyszłości bez przeszukiwania całego kodu.
SET PROG_VIP = 5000 EUR

Błąd - próba nadpisania przez SET:

SET DZISIAJ = CURRENT_DATE()
SET DZISIAJ = ADD_DAYS(DZISIAJ, 1)  // ❌ BŁĄD: Nie można nadpisać zmiennej przez SET

OVERRIDE - Nadpisanie Istniejącej Zmiennej

Składnia: OVERRIDE <ZMIENNA> = <wartość>

Nadpisuje wartość istniejącej zmiennej. Zmienna musi być wcześniej zadeklarowana przez SET.

// Co robimy: Zapisujemy dzisiejszą datę.
// Po co: Punkt startowy dla obliczeń.
SET DZISIAJ = CURRENT_DATE()

// Co robimy: Przesuwamy datę o 1 dzień do przodu.
// Po co: Aby obliczyć termin "jutro" dla procesu planowania.
OVERRIDE DZISIAJ = ADD_DAYS(DZISIAJ, 1)

Błąd - OVERRIDE bez wcześniejszego SET:

OVERRIDE NOWA_ZMIENNA = 100  // ❌ BŁĄD: Zmienna nie została wcześniej zadeklarowana

Uwaga: OVERRIDE należy używać ostrożnie - może prowadzić do trudniejszego śledzenia przepływu danych. W większości przypadków lepiej utworzyć nową zmienną:

SET DZISIAJ = CURRENT_DATE()
SET JUTRO = ADD_DAYS(DZISIAJ, 1)  // ✅ Preferowane - jasne co jest co

7.3. Gromadzenie Danych (LOAD)

Składnia: LOAD <źródło> FROM <lokalizacja> [parametry] AS <DATASET>

Typy źródeł:

API

// Co robimy: Pobieramy listę faktur niezapłaconych z API ERP.
// Po co: Aby zidentyfikować klientów zalegających z płatnościami.
LOAD API FROM 'https://erp.firma.pl/api/invoices?status=unpaid' AS FAKTURY
ON ERROR: STOP AND ALERT 'Admin ERP'

SQL

// Co robimy: Pobieramy listę aktywnych pracowników z bazy HR.
// Po co: Aby wiedzieć, komu naliczyć wynagrodzenie.
LOAD SQL FROM 'BazaHR' QUERY 'SELECT * FROM employees WHERE status = active' AS PRACOWNICY
ON ERROR: RETRY 3 TIMES THEN STOP AND ALERT 'Zespół Kadr'

Plik CSV/Excel

// Co robimy: Wczytujemy dane sprzedażowe z pliku Excel.
// Po co: Aby przeanalizować wyniki sprzedaży w ostatnim kwartale.
LOAD FILE FROM '/data/sprzedaz_Q4.xlsx' SHEET 'Sprzedaż' AS DANE_SPRZEDAZY
ON ERROR: STOP AND ALERT 'Data Analyst'

Uwagi:

  • 'BazaHR' to alias zdefiniowany w konfiguracji środowiska (poza zakresem języka)
  • Credentials są zarządzane przez środowisko wykonawcze

7.4. Filtrowanie (FILTER)

Składnia: FILTER <DATASET> WHERE <warunek> AS <NOWY_DATASET>

// Co robimy: Filtrujemy faktury przeterminowane o ponad 7 dni.
// Po co: Dajemy klientom tydzień "okresu karencji" zanim zaczniemy windykację.
FILTER WSZYSTKIE_ZALEGLOSCI WHERE Termin_Platnosci <= SUBTRACT_DAYS(DZISIAJ, 7) AS ZALEGLOSCI_DO_WINDYKACJI

Złożone warunki:

// Co robimy: Filtrujemy zamówienia aktywne lub testowe z kwotą powyżej 1000 EUR.
// Po co: Aby wykluczyć anulowane zamówienia i małe transakcje testowe.
FILTER ZAMOWIENIA WHERE (Status = 'Aktywny' OR Status = 'Testowy') AND Kwota > 1000 EUR AS ZAMOWIENIA_WAZNE

7.5. Sortowanie (SORT)

Składnia: SORT <DATASET> BY <kolumna> <ASC|DESC> [, <kolumna> <ASC|DESC> ...] AS <NOWY_DATASET>

Kierunki sortowania:

  • ASC - rosnąco (od najmniejszej do największej)
  • DESC - malejąco (od największej do najmniejszej)

Sortowanie po jednej kolumnie:

// Co robimy: Sortujemy produkty według ceny rosnąco.
// Po co: Aby pokazać najtańsze produkty na początku listy.
SORT PRODUKTY BY Cena ASC AS PRODUKTY_POSORTOWANE

Sortowanie po wielu kolumnach:

// Co robimy: Sortujemy zamówienia według daty malejąco, a potem według kwoty rosnąco.
// Po co: Aby zobaczyć najnowsze zamówienia, a wśród nich najpierw te najmniejsze.
SORT ZAMOWIENIA BY Data_Zamowienia DESC, Kwota ASC AS ZAMOWIENIA_POSORTOWANE

Uwagi:

  • Domyślny kierunek to ASC jeśli nie podano
  • Przy sortowaniu po wielu kolumnach, pierwsza kolumna ma najwyższy priorytet
  • Wartości NULL są traktowane jako najmniejsze (pojawiają się na początku przy ASC)

7.6. Grupowanie i Agregacja (GROUP)

Składnia:

GROUP <DATASET> BY <kolumna1> [, <kolumna2> ...]:
    <Agregat1> = <funkcja_agregująca>(kolumna)
    <Agregat2> = <funkcja_agregująca>(kolumna)
AS <NOWY_DATASET>

Wcięcia: Obowiązkowe 4 spacje dla każdego wiersza wewnątrz bloku.

Funkcje agregujące: SUM(), AVG(), COUNT(), MIN(), MAX()

Grupowanie po jednej kolumnie:

// Co robimy: Grupujemy sprzedaż według produktu i sumujemy sprzedane jednostki.
// Po co: Aby zobaczyć, ile sztuk każdego produktu sprzedaliśmy w sumie.
GROUP SPRZEDAZ BY Produkt:
    Suma_Jednostek = SUM(Ilosc)
    Suma_Przychodu = SUM(Cena * Ilosc)
AS SPRZEDAZ_PER_PRODUKT

Grupowanie po wielu kolumnach:

// Co robimy: Grupujemy zamówienia według kraju i miasta klienta.
// Po co: Aby zobaczyć średnią wartość zamówienia w każdym mieście.
GROUP ZAMOWIENIA BY Kraj, Miasto:
    Liczba_Zamowien = COUNT(ID_Zamowienia)
    Srednia_Wartosc = AVG(Kwota)
    Max_Zamowienie = MAX(Kwota)
AS STATYSTYKI_LOKALIZACJI

Semantyka:

  • Resultat zawiera jedną linię na unikalną kombinację wartości w kolumnach GROUP BY
  • Kolumny użyte w GROUP BY są automatycznie dostępne w wyniku
  • Wszystkie inne kolumny muszą być zagregowane

Przykład z rzeczywistymi danymi:

Wejście (SPRZEDAZ):
| Produkt | Kraj | Kwota |
|---------|------|-------|
| Laptop  | PL   | 5000  |
| Laptop  | PL   | 4500  |
| Laptop  | DE   | 6000  |
| Mysz    | PL   | 100   |

GROUP SPRZEDAZ BY Produkt, Kraj:
    Suma_Sprzedazy = SUM(Kwota)
    Liczba = COUNT(Kwota)
AS WYNIK

Wyjście (WYNIK):
| Produkt | Kraj | Suma_Sprzedazy | Liczba |
|---------|------|----------------|--------|
| Laptop  | PL   | 9500           | 2      |
| Laptop  | DE   | 6000           | 1      |
| Mysz    | PL   | 100            | 1      |

7.7. Obliczenia (CALCULATE)

Składnia:

CALCULATE on <DATASET>:
    <Nowa_Kolumna_1> = <wyrażenie>
    <Nowa_Kolumna_2> = <wyrażenie>
AS <NOWY_DATASET>

Wcięcia: Obowiązkowe 4 spacje dla każdego wiersza wewnątrz bloku.

// Co robimy: Wyliczamy marżę dla każdego produktu.
// Po co: Aby zidentyfikować najmniej rentowne towary.
CALCULATE on SPRZEDAZ:
    Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
    Marza_Procent = (Marza_PLN / Cena_Sprzedazy) * 100
AS SPRZEDAZ_Z_MARZA

Funkcje agregujące:

// Co robimy: Wyliczamy całkowitą sprzedaż i średnią marżę.
// Po co: Aby uzyskać podsumowanie wyników sprzedażowych.
CALCULATE on SPRZEDAZ:
    Suma_Sprzedazy = SUM(Kwota)
    Srednia_Marza = AVG(Marza_Procent)
AS PODSUMOWANIE_SPRZEDAZY

7.8. Łączenie Danych (JOIN)

Składnia: JOIN <typ> <DATASET1> WITH <DATASET2> ON <klucz> AS <NOWY_DATASET>

Typy JOIN:

  • INNER - tylko dopasowane rekordy z obu stron
  • LEFT - wszystkie z lewej + dopasowane z prawej
  • RIGHT - wszystkie z prawej + dopasowane z lewej
  • FULL - wszystkie rekordy z obu stron

Domyślny typ: Jeśli nie podano, używany jest INNER.

// Co robimy: Łączymy tabelę sprzedaży z bazą klientów po ID_Klienta.
// Po co: Aby wzbogacić dane transakcyjne o informacje demograficzne (miasto, wiek).
JOIN INNER SPRZEDAZ WITH KLIENCI ON ID_Klienta AS DANE_PELNE
// Co robimy: Łączymy zamówienia z płatnościami, zachowując wszystkie zamówienia.
// Po co: Aby zobaczyć, które zamówienia nie mają jeszcze płatności.
JOIN LEFT ZAMOWIENIA WITH PLATNOSCI ON ID_Zamowienia AS ZAMOWIENIA_Z_PLATNOSCI

7.9. Sterowanie Warunkowe (IF/ELSE)

Składnia:

IF <warunek> THEN
    // instrukcje (z podwójnymi komentarzami)
ELSE IF <warunek> THEN
    // instrukcje
ELSE
    // instrukcje
END IF

Wcięcia: Obowiązkowe 4 spacje dla treści wewnątrz bloków.

// Co robimy: Sprawdzamy, czy po filtracji zostały jakiekolwiek dane do wysyłki.
// Po co: Aby nie wysyłać pustych raportów do zarządu.
IF DANE_PELNE IS EMPTY THEN
    // Co robimy: Kończymy proces w tym miejscu.
    // Po co: Brak danych oznacza brak potrzeby generowania raportu.
    STOP PROCESS (Log: 'Brak danych z dzisiaj, pomijam wysyłkę')
ELSE IF COUNT(DANE_PELNE) < 10 THEN
    // Co robimy: Wysyłamy uproszczony raport.
    // Po co: Dla małej liczby danych pełny raport jest przesadą.
    SEND DANE_PELNE TO API 'https://raport.pl/simple'
ELSE
    // Co robimy: Generujemy i wysyłamy pełny raport.
    // Po co: Duża liczba danych wymaga szczegółowej analizy.
    SEND DANE_PELNE TO API 'https://raport.pl/full'
END IF

Warunki na zmiennych:

IF PROG_VIP > 10000 EUR THEN
    // instrukcje
END IF

7.10. Sztuczna Inteligencja (ASK_AI)

Semantyka: AI wykonuje zadanie dla każdego rekordu osobno (iteracja po wierszach).

Składnia Pełna (Precyzyjna)

ASK_AI on <DATASET>:
    ZADANIE: '<opis zadania z [Nazwa_Kolumny] jako placeholder>'
    ZWROC: <Nazwa_Nowej_Kolumny>
AS <NOWY_DATASET>

Wcięcia: Obowiązkowe 4 spacje dla ZADANIE i ZWROC.

// Co robimy: Prosimy AI o wygenerowanie spersonalizowanej treści maila.
// Po co: Aby komunikacja była dopasowana do klienta, a nie brzmiała jak robot.
ASK_AI on ZALEGLOSCI_DO_WINDYKACJI:
    ZADANIE: 'Napisz krótkie przypomnienie o fakturze [Nr_Faktury] na kwotę [Kwota]. Użyj uprzejmego tonu.'
    ZWROC: Tresc_Maila
AS PRZYGOTOWANE_WYSYLKI

Składnia Uproszczona (Naturalna)

ASK_AI: <opis zadania w naturalnym języku> AS <NOWY_DATASET>

Używaj gdy szczegóły (które kolumny, jak nazywać output) są oczywiste z kontekstu.

// Co robimy: AI generuje uprzejme przypomnienia o płatnościach.
// Po co: Aby komunikacja była dopasowana do klienta.
ASK_AI: napisz uprzejme przypomnienia o fakturach AS PRZYGOTOWANE_WYSYLKI
// Co robimy: AI ocenia sentyment komentarzy klientów.
// Po co: Aby wyłapać niezadowolonych klientów.
ASK_AI: oceń sentyment komentarzy na skali 1-5 AS ANKIETY_OCENIONE

Obsługa błędów (dla obu składni):

ASK_AI on DANE:
    ZADANIE: 'Wyciągnij NIP z tekstu [Body].'
    ZWROC: NIP
AS DANE_Z_NIP
ON ERROR: RETRY 2 TIMES THEN SKIP
ASK_AI: wyciągnij NIP z treści maili AS DANE_Z_NIP
ON ERROR: RETRY 2 TIMES THEN SKIP

Kiedy użyć której składni:

  • Pełna: Gdy precyzyjnie określasz źródłowe kolumny [Nazwa_Kolumny] i nazwę nowej kolumny
  • Uproszczona: Gdy szczegóły są jasne z kontekstu lub dla szybkiego prototypowania

7.11. Pętla Zwrotna z Człowiekiem (AWAIT APPROVAL)

Składnia:

AWAIT APPROVAL FROM '<rola>' ON <DATASET>
    POKAZ: '<kolumna1>', '<kolumna2>', ...
    PYTANIE: '<treść pytania>'
AS <NOWY_DATASET>

Semantyka:

  • Proces zatrzymuje się i czeka na decyzję użytkownika
  • <NOWY_DATASET> zawiera tylko zatwierdzone rekordy
  • Odrzucone rekordy są trwale usuwane z przepływu
  • Użytkownik może edytować wartości w kolumnach przed zatwierdzeniem

Wcięcia: Obowiązkowe 4 spacje dla POKAZ i PYTANIE.

// Co robimy: Wstrzymujemy proces do momentu akceptacji faktury przez menedżera.
// Po co: Zapobieganie automatycznemu opłaceniu błędnych lub zbyt wysokich faktur.
AWAIT APPROVAL FROM 'Menedżer Działu' ON FAKTURY_DO_OPLACENIA
    POKAZ: 'Kontrahent', 'Kwota_Brutto', 'Opis_Uslugi'
    PYTANIE: 'Czy akceptujesz tę fakturę do płatności?'
AS FAKTURY_ZATWIERDZONE

Timeout (opcjonalnie):

AWAIT APPROVAL FROM 'Kierownik Finansów' ON PRZYGOTOWANE_WYSYLKI
    POKAZ: 'Klient', 'Kwota', 'Tresc_Maila'
    PYTANIE: 'Czy wysłać to przypomnienie VIP?'
    TIMEOUT: 2 HOURS THEN SKIP
AS ZATWIERDZONE_VIP

7.12. Akcje Wyjściowe (SEND)

Składnia:

API

SEND <DATASET> TO API '<url>' [parametry]
// Co robimy: Wysyłamy przetworzone leady do systemu Salesforce przez API.
// Po co: Aby dział sprzedaży mógł natychmiast rozpocząć kontakt z nowymi klientami.
SEND GOTOWE_LEADY TO API 'https://salesforce.com/api/leads/bulk' (Method: POST, Headers: {'Authorization': 'Bearer TOKEN_VAR'})
ON ERROR: RETRY 5 TIMES THEN ALERT 'Admin CRM'

Uwaga: TOKEN_VAR to referencja do zmiennej środowiskowej (poza zakresem języka).

Email

SEND EMAIL TO '<recipient>' SUBJECT '<temat>' BODY '<treść>'
// Co robimy: Wysyłamy email z linkiem do raportu.
// Po co: Aby zarząd miał dostęp do wyników z rana.
SEND EMAIL TO 'prezes@firma.pl' SUBJECT 'Raport Dzienny' BODY 'Dostępny pod: https://raport.pl/daily'

Alert

SEND ALERT '<wiadomość>' TO '<kanał>'
// Co robimy: Wysyłamy powiadomienie na komunikator Slack.
// Po co: Aby poinformować zespół operacyjny o wykryciu anomalii w danych.
SEND ALERT 'Uwaga: Wykryto 5 transakcji powyżej 1mln PLN!' TO 'Slack / #finanse-alerty'

Eksport do Pliku

SEND <DATASET> TO FILE '<ścieżka>' FORMAT <CSV|XLSX|JSON>
// Co robimy: Eksportujemy dane do pliku CSV.
// Po co: Aby użytkownik mógł pobrać wyniki do Excela.
SEND RAPORT_KONCOWY TO FILE '/exports/raport_sprzedaz_Q4.csv' FORMAT CSV

7.13. Obsługa Błędów (ON ERROR)

Składnia: ON ERROR: <strategia>

Strategie:

Strategia Znaczenie Kontynuacja procesu
STOP Natychmiastowe zakończenie całego procesu Nie
STOP AND ALERT '<rola>' Zakończenie + powiadomienie Nie
SKIP Pomiń ten krok, kontynuuj dalej Tak
RETRY <n> TIMES THEN SKIP Ponów n razy, potem pomiń Tak
RETRY <n> TIMES THEN STOP Ponów n razy, potem zakończ Warunkowe
RETRY <n> TIMES THEN ALERT '<rola>' Ponów n razy, potem powiadom Tak

Zakres działania: ON ERROR odnosi się do bezpośrednio poprzedzającego polecenia.

// Co robimy: Pobieramy dane z API, które może być niestabilne.
// Po co: Aby uzyskać aktualne kursy walut do przeliczeń.
LOAD API FROM 'https://api.nbp.pl/exchangerates/tables/A/' AS KURSY_WALUT
ON ERROR: RETRY 3 TIMES THEN SKIP

Semantyka SKIP: Proces kontynuuje, ale zmienna KURSY_WALUT będzie NULL. Użytkownik musi obsłużyć ten przypadek:

IF KURSY_WALUT IS NULL THEN
    // Co robimy: Używamy wczorajszych kursów.
    // Po co: Aby proces mógł kontynuować mimo awarii NBP.
    SET KURSY_WALUT = WCZORAJSZE_KURSY
END IF

Semantyka STOP: Cały proces kończy się, żadne kolejne instrukcje nie są wykonywane.


7.14. Zakończenie Procesu

Składnia:

STOP PROCESS [(Log: '<wiadomość>')]
// Co robimy: Kończymy proces przedwcześnie.
// Po co: Brak danych oznacza brak pracy do wykonania.
STOP PROCESS (Log: 'Brak nowych faktur, proces zakończony')

Uwaga: Koniec pliku oznacza naturalny koniec procesu - nie trzeba pisać STOP PROCESS na końcu.


8. Zakres i Mutowanie Zmiennych

8.1. Zmienne Globalne (SET i OVERRIDE)

  • SET: Deklaruje nową zmienną - niemutowalną przez SET (nie można ponownie użyć SET na tej samej nazwie)
  • OVERRIDE: Nadpisuje istniejącą zmienną - mutowalną (można wielokrotnie nadpisywać)
  • Zakres: Globalny (widoczne w całym procesie)
SET COUNTER = 0
OVERRIDE COUNTER = 1  // ✅ OK
OVERRIDE COUNTER = 2  // ✅ OK - można wielokrotnie nadpisywać
SET COUNTER = 3       // ❌ BŁĄD - nie można użyć SET na istniejącej zmiennej

8.2. Datasety (AS)

  • Niemutowalne - każda transformacja tworzy nowy dataset
  • Zakres: Lokalny (widoczne od momentu utworzenia do końca procesu)
  • Nadpisywanie: Dozwolone, ale stary dataset przestaje istnieć
FILTER DATA WHERE x > 10 AS RESULT
FILTER RESULT WHERE y < 5 AS RESULT  // ✅ OK: RESULT zostaje zastąpiony
FILTER DATA WHERE z > 0 AS RESULT    // ✅ OK: RESULT znowu zastąpiony

Dobra praktyka: Używaj unikalnych nazw dla jasności:

FILTER DATA WHERE x > 10 AS DATA_STEP1
FILTER DATA_STEP1 WHERE y < 5 AS DATA_STEP2

9. Model Wykonania

9.1. Sekwencyjność

Wszystkie operacje wykonują się sekwencyjnie (jedna po drugiej) w kolejności zapisania.

Brak równoległości w samym języku - jeśli środowisko wykonawcze obsługuje równoległość (np. przetwarzanie wsadowe), jest to transparentne dla użytkownika.

9.2. Atomowość

Każde polecenie jest atomowe - albo wykonuje się w całości, albo w ogóle (i wywołuje błąd).

Brak częściowych rezultatów: Jeśli LOAD pobierze 500 z 1000 rekordów i padnie, zwrócony zostanie błąd (nie dataset z 500 rekordami).

9.3. Brak Rollbacku

Język nie obsługuje transakcji ani rollbacku. Jeśli proces wykonał 10 kroków i padł na 11., pierwsze 10 zostaje (np. wysłane emaile, zapisane pliki).

Odpowiedzialność użytkownika: Projektuj procesy idempotentne lub używaj zewnętrznych mechanizmów transakcyjnych.


10. Przykłady Pełnych Procesów

10.1. Automatyzacja Przypomnień o Płatnościach

// TRIGGER: SCHEDULE EVERY DAY AT 09:00 CET

// --- Faza 1: Inicjalizacja i Dane ---
// Co robimy: Ustalamy dzisiejszą datę do obliczeń opóźnień.
// Po co: Aby precyzyjnie wyliczyć liczbę dni po terminie.
SET DZISIAJ = CURRENT_DATE()

// Co robimy: Pobieramy z ERP faktury niezapłacone, z terminem płatności < DZISIAJ.
// Po co: Aby zidentyfikować klientów zalegających z płatnościami.
LOAD API FROM 'https://erp.firma.pl/api/invoices?status=unpaid' AS WSZYSTKIE_ZALEGLOSCI
ON ERROR: STOP AND ALERT 'Admin ERP'

// Co robimy: Filtrujemy tylko te przeterminowane o ponad 7 dni.
// Po co: Dajemy klientom tydzień "okresu karencji" zanim zaczniemy windykację.
FILTER WSZYSTKIE_ZALEGLOSCI WHERE Termin_Platnosci <= SUBTRACT_DAYS(DZISIAJ, 7) AS ZALEGLOSCI_DO_WINDYKACJI

// --- Faza 2: AI i Decyzje ---
// Co robimy: Prosimy AI o wygenerowanie spersonalizowanej, grzecznej treści maila dla każdego klienta.
// Po co: Aby komunikacja była dopasowana do klienta, a nie brzmiała jak robot.
ASK_AI on ZALEGLOSCI_DO_WINDYKACJI:
    ZADANIE: 'Napisz krótkie przypomnienie o fakturze [Nr_Faktury] na kwotę [Kwota]. Użyj uprzejmego tonu.'
    ZWROC: Tresc_Maila
AS PRZYGOTOWANE_WYSYLKI

// Co robimy: Dzielimy proces w zależności od kwoty zaległości.
// Po co: Duże kwoty są ryzykowne relacyjnie, chcemy by człowiek rzucił okiem przed wysyłką.
IF PRZYGOTOWANE_WYSYLKI.Kwota > 10000 EUR THEN

    // --- Ścieżka VIP (wymaga akceptacji) ---
    // Co robimy: Wstrzymujemy proces dla tych rekordów i wysyłamy prośbę do Kierownika Finansów.
    // Po co: Uniknięcie pomyłki przy kluczowych klientach.
    AWAIT APPROVAL FROM 'Kierownik Finansów' ON PRZYGOTOWANE_WYSYLKI
        POKAZ: 'Klient', 'Kwota', 'Tresc_Maila'
        PYTANIE: 'Czy wysłać to przypomnienie VIP?'
    AS ZATWIERDZONE_VIP

    // Co robimy: Wysyłamy tylko zatwierdzone maile VIP.
    // Po co: Aby dotrzeć do kluczowych klientów z potwierdzoną komunikacją.
    SEND ZATWIERDZONE_VIP TO API 'https://mail.firma.pl/send'

ELSE
    // --- Ścieżka Standard (automatyczna) ---
    // Co robimy: Wysyłamy maile automatycznie dla mniejszych kwot.
    // Po co: Oszczędność czasu menedżera przy rutynowych przypomnieniach.
    SEND PRZYGOTOWANE_WYSYLKI TO API 'https://mail.firma.pl/send'

END IF

10.2. Raportowanie Sprzedaży z Analizą Marży

// TRIGGER: SCHEDULE EVERY 1st DAY OF MONTH AT 00:00 UTC

// Co robimy: Ustawiamy zmienne czasowe dla poprzedniego miesiąca.
// Po co: Aby raport dotyczył kompletnego miesiąca.
SET DATA_OD = SUBTRACT_DAYS(CURRENT_DATE(), 30)
SET DATA_DO = CURRENT_DATE()

// Co robimy: Pobieramy dane sprzedażowe z bazy produkcyjnej.
// Po co: Aby przeanalizować wyniki sprzedaży za miniony miesiąc.
LOAD SQL FROM 'BazaProdukcyjna' QUERY 'SELECT * FROM sales WHERE sale_date BETWEEN @DATA_OD AND @DATA_DO' AS SPRZEDAZ
ON ERROR: RETRY 3 TIMES THEN STOP AND ALERT 'DBA Team'

// Co robimy: Wyliczamy marżę dla każdej transakcji.
// Po co: Aby zidentyfikować najbardziej i najmniej rentowne produkty.
CALCULATE on SPRZEDAZ:
    Marza_PLN = Cena_Sprzedazy - Koszt_Zakupu
    Marza_Procent = (Marza_PLN / Cena_Sprzedazy) * 100
AS SPRZEDAZ_Z_MARZA

// Co robimy: Filtrujemy transakcje z ujemną marżą.
// Po co: Aby zidentyfikować produkty sprzedawane ze stratą.
FILTER SPRZEDAZ_Z_MARZA WHERE Marza_PLN < 0 AS PRODUKTY_STRATNE

// Co robimy: Sprawdzamy, czy są produkty ze stratą.
// Po co: Aby zdecydować, czy trzeba wysłać alert do zarządu.
IF PRODUKTY_STRATNE IS NOT EMPTY THEN
    // Co robimy: Wysyłamy alert o produktach ze stratą.
    // Po co: Aby zarząd mógł szybko zareagować na problem.
    SEND ALERT 'Uwaga: Wykryto produkty sprzedawane ze stratą!' TO 'Slack / #management-alerts'
    
    // Co robimy: Eksportujemy szczegóły do pliku.
    // Po co: Aby zarząd miał pełne dane do analizy.
    SEND PRODUKTY_STRATNE TO FILE '/reports/produkty_stratne_miesiac.xlsx' FORMAT XLSX
END IF

// Co robimy: Generujemy podsumowanie całościowe.
// Po co: Aby uzyskać kluczowe metryki sprzedażowe miesiąca.
CALCULATE on SPRZEDAZ_Z_MARZA:
    Suma_Sprzedazy = SUM(Cena_Sprzedazy)
    Suma_Marzy = SUM(Marza_PLN)
    Srednia_Marza_Procent = AVG(Marza_Procent)
AS RAPORT_MIESIAC

// Co robimy: Wysyłamy raport miesięczny emailem.
// Po co: Aby dyrektor sprzedaży otrzymał automatyczny raport.
SEND EMAIL TO 'dyrektor.sprzedazy@firma.pl' SUBJECT 'Raport Miesięczny Sprzedaży' BODY 'Suma sprzedaży: [Suma_Sprzedazy], Marża: [Suma_Marzy]'

11. Checklist dla Autorów Procesów

Przed wdrożeniem procesu, sprawdź:

  • Każde polecenie operacyjne ma // Co robimy: i // Po co:
  • Wszystkie zmienne i datasety używają UPPER_SNAKE_CASE
  • Kolumny są w PascalCase lub w apostrofach (jeśli zawierają spacje)
  • Stringi literalne są w pojedynczych apostrofach '...'
  • Wszystkie operacje ryzykowne (LOAD, SEND, ASK_AI) mają ON ERROR
  • Warunki IF używają AND/OR/NOT (nie &&/||/!)
  • Bloki CALCULATE, GROUP i ASK_AI mają wcięcia 4 spacje
  • Każdy JOIN ma określony typ (INNER/LEFT/RIGHT/FULL)
  • Każdy SORT ma określony kierunek (ASC/DESC) dla każdej kolumny
  • AWAIT APPROVAL ma TIMEOUT jeśli proces nie może czekać w nieskończoność
  • Używasz SET do deklaracji, OVERRIDE do nadpisania (nie używaj SET dwukrotnie na tej samej zmiennej)
  • Proces ma jasny TRIGGER na początku
  • Jeśli proces używa zewnętrznych systemów, rozważ dodanie sekcji SYSTEMS
  • Jeśli używasz SKIP w ON ERROR, obsługujesz przypadek NULL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment