From b259442c2094055764f3e7019054da2bd8a15b4c Mon Sep 17 00:00:00 2001 From: bartool Date: Wed, 15 Oct 2025 11:36:01 +0200 Subject: [PATCH] init commit --- app1.py | 24 ++++ app2.py | 33 +++++ app3.py | 41 ++++++ gsheet_api.py | 59 ++++++++ guitar1.html | 128 +++++++++++++++++ guitar2.html | 378 +++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 122 ++++++++++++++++ mayo.py | 111 +++++++++++++++ requiments.txt | 28 ++++ 9 files changed, 924 insertions(+) create mode 100644 app1.py create mode 100644 app2.py create mode 100644 app3.py create mode 100644 gsheet_api.py create mode 100644 guitar1.html create mode 100644 guitar2.html create mode 100644 main.py create mode 100644 mayo.py create mode 100644 requiments.txt diff --git a/app1.py b/app1.py new file mode 100644 index 0000000..04089fe --- /dev/null +++ b/app1.py @@ -0,0 +1,24 @@ +import gspread +from google.oauth2.service_account import Credentials + +# Zasięgi uprawnień (Sheets + Drive) +SCOPES = [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/drive" +] + +# Wczytanie poświadczeń +creds = Credentials.from_service_account_file("credentials.json", scopes=SCOPES) + +# Połączenie z Google Sheets +client = gspread.authorize(creds) + +# Otworzenie arkusza po nazwie +sheet = client.open("Składalnia lista gitar przychodzących ").sheet1 # sheet1 = pierwsza zakładka + +# Pobranie wszystkich danych +rows = sheet.get_all_values() + +# Wyświetlenie pierwszych 5 wierszy +for row in rows[:5]: + print(row) diff --git a/app2.py b/app2.py new file mode 100644 index 0000000..df1824a --- /dev/null +++ b/app2.py @@ -0,0 +1,33 @@ +import requests +from bs4 import BeautifulSoup + +# konfiguracja logowania +LOGIN_URL = "http://192.168.0.152/mayo2/login.php" +DANE_LOGOWANIA = { + "login": "twoj_login", + "pass": "twoje_haslo", + "baza": "2", # lub "2" +} + +# Tworzymy sesję +s = requests.Session() + +# Logujemy się +resp = s.post(LOGIN_URL, data=DANE_LOGOWANIA) +if "Zaloguj się" in resp.text: + print("❌ Logowanie nieudane — sprawdź login/hasło") +else: + print("✅ Zalogowano poprawnie") + +# Pobieramy stronę po zalogowaniu +url = "http://192.168.0.152/mayo2/index.php?id_zestawu=32574&id_zamowienia=7466&modul=14&pozycja=" +page = s.get(url) +page.encoding = "utf-8" + +soup = BeautifulSoup(page.text, "html.parser") + +# Szukamy modelu +model_input = soup.find("input", {"name": "s_nr_kat"}) +model = model_input.get("value") if model_input else None + +print("Model:", model) diff --git a/app3.py b/app3.py new file mode 100644 index 0000000..1083f90 --- /dev/null +++ b/app3.py @@ -0,0 +1,41 @@ +from bs4 import BeautifulSoup + +# 1) jeśli masz body jako string: +# html = "...." + +# 2) albo wczytaj z pliku lokalnego: +with open("guitar2.html", "r", encoding="utf-8") as f: + html = f.read() + +soup = BeautifulSoup(html, "html.parser") + +# model — najpierw input[name="s_nr_kat"] +model = None +inp = soup.find("input", {"name": "s_nr_kat"}) +if inp and inp.has_attr("value"): + model = inp["value"].strip() + +# jeśli trzeba, fallback: znajdź Model i pobierz input w następnym +if not model: + b = next((b for b in soup.find_all("b") if b.get_text(strip=True).lower() == "model"), None) + if b: + td = b.find_parent("td") + if td: + next_td = td.find_next_sibling("td") + if next_td: + inp2 = next_td.find("input") + if inp2 and inp2.has_attr("value"): + model = inp2["value"].strip() + +# nr zamówienia — span.czarnobiale (może zawierać ) +order = None +span = soup.find("span", class_="czarnobiale") +if span: + order = span.get_text(strip=True) + +# dodatkowe oczyszczanie (usuwa ewentualne spacje) +if order: + order = order.replace("\n", " ").strip() + +print("Model:", model) +print("Nr zam.:", order) diff --git a/gsheet_api.py b/gsheet_api.py new file mode 100644 index 0000000..e8df731 --- /dev/null +++ b/gsheet_api.py @@ -0,0 +1,59 @@ +import gspread +from google.oauth2.service_account import Credentials + +SCOPES = [ + "https://www.googleapis.com/auth/spreadsheets", + "https://www.googleapis.com/auth/drive" +] + +def connect(): + creds = Credentials.from_service_account_file("credentials.json", scopes=SCOPES) + client = gspread.authorize(creds) + return client + + +def list_sheets(doc_name): + client = connect() + spreadsheet = client.open(doc_name) + return [ws.title for ws in spreadsheet.worksheets()] + + +def get_sheet_data(doc_name, sheet_name): + client = connect() + sheet = client.open(doc_name).worksheet(sheet_name) + return sheet.get_all_values() + + +def ensure_worksheet(doc_name, sheet_name): + """ + Zwraca worksheet o danej nazwie. + Tworzy nowy, jeśli nie istnieje. + """ + client = connect() + spreadsheet = client.open(doc_name) + try: + ws = spreadsheet.worksheet(sheet_name) + except gspread.exceptions.WorksheetNotFound: + print(f"➕ Tworzę nowy arkusz: {sheet_name}") + ws = spreadsheet.add_worksheet(title=sheet_name, rows=100, cols=10) + ws.append_row(["#", "Link", "Nr zamówienia", "Model"]) + return ws + + +def append_unique_row(doc_name, sheet_name, row_data): + """ + Dodaje wiersz tylko, jeśli dany nr zamówienia (kolumna 3) jeszcze nie istnieje. + """ + client = connect() + ws = ensure_worksheet(doc_name, sheet_name) + + existing = ws.col_values(3) # kolumna "Nr zamówienia" + order_number = str(row_data[2]).strip() + + if order_number in existing: + print(f"⏭️ Pomijam – nr {order_number} już istnieje w arkuszu {sheet_name}") + return False + + ws.append_row(row_data, value_input_option="USER_ENTERED") + print(f"📝 Dodano do {sheet_name}: {row_data}") + return True diff --git a/guitar1.html b/guitar1.html new file mode 100644 index 0000000..774ba43 --- /dev/null +++ b/guitar1.html @@ -0,0 +1,128 @@ + + +
+ Grupa +   + Model +   + Nr ser. +   + + DZZ + + + + Opis + + Odbiorca +   + P + + NTB + + CITES + + + + + + + +
+
 
+ +   + + +   + +   + +   + +   + + +   + +
+ + + + \ No newline at end of file diff --git a/guitar2.html b/guitar2.html new file mode 100644 index 0000000..472178a --- /dev/null +++ b/guitar2.html @@ -0,0 +1,378 @@ + + + +
+ +
+ + + + + + +
Mayones Guitars & Basses - System Obsługi Zamówień + Wyloguj: + + kielpint + +
+
+ + + + + +
+ + +
+ + + + + +
+
Dot. zam.: 0077/2024/1   z datą realizacji:   2024-02-29USA | Chicago Music Exchange | USA-
+ + + + + + + + + + +
Grupa  Model   Nr ser.   + DZZ
Opis Odbiorca  P NTB CITES
+
+
 
+ +   + + +   + +   + +   + +   + + +   + +
+
+
CITES     
Status: Data wystąpienia: Data otrzymania:
Komentarz:

SZYJKA

  Radius

  - GITARA SETIUS/REGIUS/CUSTOM / 16     + NotatkaUsuń

  DREWNO - Szyjka

  - Klon amerykański-Mahoń-Wenge-Amazakoe (11 części)     + NotatkaUsuń

  DREWNO - Podstrunnica

  - EBN / EBONY (Heban)     + NotatkaUsuń

  DREWNO - Główka

  - CP-4A / Curly Poplar 4A     + NotatkaUsuń

  WYMIARY

  - GITARA REGIUS 7/PRO 7 / Szyjka szerokość I : 49 mm     + NotatkaUsuń
  - GITARA REGIUS 7/PRO 7 / Szyjka szerokość XII : 62 mm     + NotatkaUsuń
  - GITARA REGIUS 7/PRO 7 / Szyjka szerokość XXIV : 69 mm     + NotatkaUsuń
  - GITARA SETIUS/REGIUS/CUSTOM / Szyjka grubość I : 20 mm     + NotatkaUsuń
  - GITARA SETIUS/REGIUS/CUSTOM / Szyjka grubość XII : 21 mm     + NotatkaUsuń

  MARKERY - BOK

  - SB-20 / Kropeczki (boczne) / 2mm / Fluorescencyjne / Niebieskie / Nie do jasnych krawędzi     + NotatkaUsuń

  MARKERY - FRONT

  - Bez markerów     + NotatkaUsuń

  PROGI

  - FERD WAGNER / FW 9685 / 667851 / Stainless Steel / 2,80×3,34 (1,45) / EXTRA JUMBO [JD brak] / STANDARD     + NotatkaUsuń
KONSTRUKCJA

  Konstrukcja

  - neck-thru-body (szyjka przez korpus)     + NotatkaUsuń

  Skala

  - GITARA STANDARD / 645mm (25,4")     + NotatkaUsuń
OGÓLNE

  LAMÓWKA (binding)

  - Korpus / Szyjka / Główka     + NotatkaUsuń
  - 3 częściowa / 2 x ABS Czarny + Akryl Czarny Perloid     + NotatkaUsuń
KORPUS

  DREWNO - Top

  - CP-4A / Curly Poplar 4A     + NotatkaUsuń

  DREWNO - Korpus

  - ALD-F / FLAMED ALDER (body) / Olcha Płomienista     + NotatkaUsuń

  KONFIGURACJA - Przetworniki

  - H-H     + NotatkaUsuń

  KONFIGURACJA - Potencjometry

  - CG-05T / 1 x Volume - W MIEJSCU TONE     + NotatkaUsuń
  - Przełącznik Ślizgowy     + NotatkaUsuń
KOLOR

  KOLOR - Top

  - T-DRED-G / Trans Dirty-Red Gloss (transparent brudny czerwony połysk)     + NotatkaUsuń

  KOLOR - Korpus

  - T-ANTIQUE-BLK-M / Antique Black Matt     + NotatkaUsuń

  KOLOR - Szyjka

  - T-ANTIQUE-BLK-M / Antique Black Matt     + NotatkaUsuń

  KOLOR - Główka

  - T-DRED-G / Trans Dirty-Red Gloss (transparent brudny czerwony połysk)     + NotatkaUsuń

  KOLOR - Wykończenie [K/C]

  - G+M / Gloss+Matt     + NotatkaUsuń
ELEKTRONIKA

  PRZETWORNIKI - Gitara

  - VELVETRONE / H15+H13-7-SET / STD / Ironside + Solium | 7 string | Humbucker Set     + NotatkaUsuń
  - / SPECIAL / - PINS: GOLD SCREWS     + NotatkaUsuń

  ELEKTRONIKA

  - MAYONES / Treble Bleed Mod     + NotatkaUsuń
  - BOURNS / PDA241-HTR01-504A2 | STD / Potencjometr / Volume / 500kA     + NotatkaUsuń

  PRZEŁĄCZNIKI I GNIAZDA

  - SWITCHCRAFT / C12B / Gniazdo Jack     + NotatkaUsuń
  - ELECTROSWITCH / 5-way | STD / Przełącznik / Electroswitch 5-poz Multipole (ślizgowy) - Konieczna REZERWACJA [wyklucza split na volume push-pull]     + NotatkaUsuń
AKCESORIA

  MOSTEK - Gitara

  - HIPSHOT / 41075B / 7 String / Fixed .175 / BLK     + NotatkaUsuń
  - / HYBRID GLD I / Mieszany/Mixed | BLK+GLD     + NotatkaUsuń

  STROIKI - Gitara

  - HIPSHOT / 6G000-D07-B / GripLock 3+4 / Black / Regius     + NotatkaUsuń
  - / HYBRID GLD I / Mieszany/Mixed | BLK+GLD     + NotatkaUsuń

  Siodełko

  - BT-6700-00 / Grafitowe / Graph-Tech BT-6700-00 (do 7-ki)     + NotatkaUsuń

  Gałki

  - O-Ring     + NotatkaUsuń

  AKCESORIA - Kolor

  - HYBRID GLD I / Mieszany/Mixed | BLK+GLD | (czarny mostek, złote siodełka, złote klucze, czarne motylki, czarne nakrętki, czarne śruby)     + NotatkaUsuń
DODATKI

  WYPOSAŻENIE DODATKOWE

  - SCHALLER / ZACZEPY / Zaczep Schaller Security Lock BLK (2 szt)     + NotatkaUsuń
  - / CANTO-HYBRID-DD-GUITAR / Mayones Hybrid Guitar Double Deck     + NotatkaUsuń
+ +
  Notatka produkcyjna2x LD113
  Dodatkowe
  informacje
  Dodaj element +
+
+ + + + + + + + +
+   Dołącz zdjęcie +
+ + + +
+ + + + + +
+
+ +   + +   + +   + +   + +
+
+ +
+ + + +

+
+
+
+ +


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+ + + + + + + + + + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..09df005 --- /dev/null +++ b/main.py @@ -0,0 +1,122 @@ +from gsheet_api import list_sheets, get_sheet_data, append_unique_row +from mayo import MayoSession +from dotenv import load_dotenv +import os +import re + +load_dotenv() + +# --- konfiguracja ---pip list +DOC_NAME = os.getenv("DOC_NAME") +MAYO_URL = os.getenv("MAYO_URL") +LOGIN = os.getenv("MAYO_LOGIN") +PASSWORD = os.getenv("MAYO_PASSWORD") +RESULT_DOC = "gitary 2025" + + + +def normalize(text): + if not text: + return "" + # usuwa spacje, niełamliwe spacje, nowe linie itp. + return re.sub(r"\s+", "", text) + +def get_finish_type(row_data): + """ + Analizuje dane z listy wiersza, aby określić rodzaj wykończenia. + + Args: + row_data: Lista zawierająca dane zamówienia, gdzie: + - row_data[4] to 'color_top' + - row_data[5] to 'color_body' + + Returns: + Ciąg znaków: "GLOSS", "SATIN", "MIX", lub "MAT" na podstawie + ostatnich liter w kodach kolorów. Zwraca None, jeśli żaden + warunek nie jest spełniony. + """ + try: + color_top = row_data["color_top"].strip() + color_body = row_data["color_body"].strip() + + # Sprawdzenie, czy dane są dostępne + if not color_top or not color_body: + return None + + # Pobranie ostatniej litery z kodów kolorów + top_last_char = color_top[-1] + body_last_char = color_body[-1] + + # Warunki logiczne + if top_last_char == 'G' and body_last_char == 'G': + return "GLOSS" + elif top_last_char == 'S' and body_last_char == 'S': + return "SATIN" + elif top_last_char in ('G', 'S') and body_last_char == 'M': + return "MIX" + elif top_last_char in ('M', 'R') and body_last_char in ('M', 'R'): + return "MAT" + except: + return None # Zwraca None, jeśli żaden z powyższych warunków nie zostanie spełniony + + + +def main(): + print("📄 Pobieram listę arkuszy...") + sheets = list_sheets(DOC_NAME) + for i, name in enumerate(sheets): + print(f"{i+1}. {name}") + + # wybór arkusza (na razie manualny) + sheet_name = input("\nWybierz arkusz do przetworzenia: ") + + print(f"📋 Pobieram dane z arkusza: {sheet_name}") + rows = get_sheet_data(DOC_NAME, sheet_name) + + mayo = MayoSession(MAYO_URL, LOGIN, PASSWORD) + mayo.login() + + counter = 1 + # Zakładamy: kolumna B = link, kolumna C = nr zam. + for row in rows[1:]: # pomijamy nagłówek + link = row[1] + nr_zam = row[2] + + if not link: + continue + + print(f"\n🔗 Sprawdzam: {link}") + info = mayo.get_order_info(link) + order_number = info["order_number"] + model = info["model"] + + print(f"Nr z arkusza: {nr_zam}") + print(f"Nr ze strony: {info['order_number']}") + print(f"Model: {info['model']}") + + if normalize(info["order_number"]) == normalize(nr_zam): + print("✅ Numer się zgadza") + else: + print("⚠️ Numer NIE pasuje!") + + row_data = [ + counter, + link, + nr_zam, + model, + get_finish_type(info), + info["color_top"], + info["color_body"], + info["color_neck"], + info["color_head"], + info["finish"], + ] + + print(f"top: {info['color_top']}, body: {info['color_body']}, neck: {info['color_neck']}, head: {info['color_head']}, finish: {info["finish"]}") + print(f"WYKONCZENIE: {get_finish_type(info)}") + append_unique_row(RESULT_DOC, sheet_name, row_data) + + counter += 1 + +if __name__ == "__main__": + main() diff --git a/mayo.py b/mayo.py new file mode 100644 index 0000000..a4d689d --- /dev/null +++ b/mayo.py @@ -0,0 +1,111 @@ +import requests +from bs4 import BeautifulSoup +import re + +class MayoSession: + def __init__(self, base_url, login, password, db="1"): + """ + base_url: np. 'http://192.168.0.152/mayo2' + login, password: dane logowania + db: numer bazy (np. "1" = Mayones 2) + """ + self.session = requests.Session() + self.base_url = base_url + self.login_url = f"{self.base_url}/login.php" + self.credentials = { + "login": login, + "pass": password, + "baza": db + } + + def login(self): + """Loguje się do systemu lokalnego.""" + r = self.session.post(self.login_url, data=self.credentials) + if "Zaloguj się" in r.text or "login" in r.url: + raise Exception("Nie udało się zalogować do Mayo.") + print("✅ Zalogowano poprawnie do systemu Mayo.") + + # def get_order_info(self, url): + # """ + # Pobiera dane z podanej strony zamówienia: + # - numer zamówienia + # - model gitary + # """ + # r = self.session.get(url) + # r.encoding = "utf-8" + # soup = BeautifulSoup(r.text, "html.parser") + + # # nr zamówienia + # order_span = soup.find("span", class_="czarnobiale") + # order_number = order_span.get_text(strip=True) if order_span else None + + # # model gitary + # input_tag = soup.find("input", {"name": "s_nr_kat"}) + # model = input_tag.get("value").strip() if input_tag else None + + # return {"order_number": order_number, "model": model} + + def get_order_info(self, url): + """ + Pobiera dane z podanej strony zamówienia: + - numer zamówienia + - model + - kolory (Top, Korpus, Szyjka, Główka) + - wykończenie + """ + r = self.session.get(url) + r.encoding = "utf-8" + soup = BeautifulSoup(r.text, "html.parser") + + # --- nr zamówienia --- + order_span = soup.find("span", class_="czarnobiale") + order_number = order_span.get_text(strip=True) if order_span else None + + # --- model gitary --- + input_tag = soup.find("input", {"name": "s_nr_kat"}) + model = input_tag.get("value").strip() if input_tag else None + + # --- kolory i wykończenie --- + color_sections = {} + # Znajdź wszystkie

zawierające z tekstem "KOLOR" + for p in soup.find_all("p"): + a_tag = p.find("a") + if not a_tag: + continue + title = a_tag.get_text(strip=True) + if title.startswith("KOLOR"): + # np. "KOLOR - Top" + label = title.replace("KOLOR - ", "").strip() + + # znajdź z wartością koloru (niedaleko tego

) + td = p.find_parent("td") + if td: + # przejdź do następnego , tam jest z kolorem + next_td = td.find_next_sibling("td") + if next_td: + span = next_td.find("span") + if span: + text = span.get_text(" ", strip=True) + # usuń ewentualne znaki nadmiarowe + text = re.sub(r"\s+", " ", text) + color_match = re.search(r"^\s*-\s*([A-Z0-9-]+)", text) + if color_match: + text = color_match.group(1) + color_sections[label] = text + + # --- normalizacja nazewnictwa --- + color_top = color_sections.get("Top") + color_body = color_sections.get("Korpus") + color_neck = color_sections.get("Szyjka") + color_head = color_sections.get("Główka") + finish = color_sections.get("Wykończenie [K/C]") + + return { + "order_number": order_number, + "model": model, + "color_top": color_top, + "color_body": color_body, + "color_neck": color_neck, + "color_head": color_head, + "finish": finish, + } diff --git a/requiments.txt b/requiments.txt new file mode 100644 index 0000000..a8a9c04 --- /dev/null +++ b/requiments.txt @@ -0,0 +1,28 @@ +beautifulsoup4==4.14.2 +cachetools==6.2.1 +certifi==2025.10.5 +charset-normalizer==3.4.4 +dotenv==0.9.9 +google-api-core==2.26.0 +google-api-python-client==2.184.0 +google-auth==2.41.1 +google-auth-httplib2==0.2.0 +google-auth-oauthlib==1.2.2 +googleapis-common-protos==1.70.0 +gspread==6.2.1 +httplib2==0.31.0 +idna==3.11 +oauthlib==3.3.1 +proto-plus==1.26.1 +protobuf==6.32.1 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pyparsing==3.2.5 +python-dotenv==1.1.1 +requests==2.32.5 +requests-oauthlib==2.0.0 +rsa==4.9.1 +soupsieve==2.8 +typing_extensions==4.15.0 +uritemplate==4.2.0 +urllib3==2.5.0