Files
GoogleSheetBot/gsheet_api.py
2026-05-07 15:13:48 +02:00

114 lines
5.0 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
import time
import gspread
from google.oauth2.service_account import Credentials
from gspread.exceptions import APIError, WorksheetNotFound
SCOPES = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive"
]
class GSheetAPI:
def __init__(self, credentials_file="credentials.json"):
"""Inicjalizuje klienta API przy tworzeniu obiektu."""
def connect():
creds = Credentials.from_service_account_file(credentials_file, scopes=SCOPES)
return gspread.authorize(creds)
self.client = self._execute_with_retry(connect)
logging.info("✅ Połączono z Google Sheets API.")
def _execute_with_retry(self, func, *args, **kwargs):
"""Wykonuje funkcję z logiką ponawiania dla błędu API 503."""
retries = 5
delay = 60
last_exception = None
for i in range(retries):
try:
return func(*args, **kwargs)
except APIError as e:
last_exception = e
# Sprawdzamy, czy błąd ma status code 503 (Service Unavailable)
if hasattr(e, 'response') and e.response.status_code == 503:
logging.warning(
f"Błąd API 503: Usługa niedostępna. Ponawiam za {delay}s... (Próba {i + 1}/{retries})"
)
time.sleep(delay)
else:
# Inne błędy API rzucamy od razu dalej
raise
except Exception as e:
# Przechwytujemy inne potencjalne błędy sieciowe i rzucamy je dalej
logging.error(f"Wystąpił nieoczekiwany błąd podczas komunikacji z API: {e}")
raise
logging.error(f"Operacja '{func.__name__}' nie powiodła się po {retries} próbach.")
if last_exception:
raise last_exception
# Fallback, gdyby pętla się zakończyła bez wyjątku
raise Exception(f"Operacja '{func.__name__}' nie powiodła się po {retries} próbach.")
def list_sheets(self, doc_name):
"""Zwraca listę arkuszy w danym dokumencie."""
spreadsheet = self._execute_with_retry(self.client.open, doc_name)
worksheets = self._execute_with_retry(spreadsheet.worksheets)
return [ws.title for ws in worksheets]
def get_sheet_data(self, doc_name, sheet_name):
"""Pobiera wszystkie dane z danego arkusza."""
spreadsheet = self._execute_with_retry(self.client.open, doc_name)
sheet = self._execute_with_retry(spreadsheet.worksheet, sheet_name)
return self._execute_with_retry(sheet.get_all_values)
def ensure_worksheet(self, doc_name, sheet_name):
"""
Zwraca worksheet o danej nazwie.
Tworzy nowy, jeśli nie istnieje.
"""
spreadsheet = self._execute_with_retry(self.client.open, doc_name)
try:
ws = self._execute_with_retry(spreadsheet.worksheet, sheet_name)
except WorksheetNotFound:
logging.info(f" Tworzę nowy arkusz: {sheet_name}")
ws = self._execute_with_retry(spreadsheet.add_worksheet, title=sheet_name, rows=100, cols=10)
header = ["#", "Link", "Nr zamówienia", "Model", "Wykończenie", "Kolor Top", "Kolor Body", "Kolor Neck", "Kolor Head", "Finish K/C", "Finish S"]
self._execute_with_retry(ws.append_row, header, value_input_option="USER_ENTERED")
return ws
def batch_append_unique_rows(self, doc_name, sheet_name, rows_data):
"""
Dodaje wiele wierszy za jednym razem, pomijając te,
których nr zamówienia (kolumna 3) już istnieje.
"""
if not rows_data:
logging.info(" Brak danych do dodania.")
return
ws = self.ensure_worksheet(doc_name, sheet_name)
logging.info("🔍 Sprawdzam istniejące numery zamówień w arkuszu docelowym...")
existing_orders_values = self._execute_with_retry(ws.col_values, 3)
existing_orders = {str(x).strip() for x in existing_orders_values}
logging.debug(f"Znaleziono {len(existing_orders)} istniejących numerów.\n existing_orders: {existing_orders}")
unique_rows_to_add = []
for row in rows_data:
order_number = str(row[2]).strip()
if order_number not in existing_orders:
unique_rows_to_add.append(row)
existing_orders.add(order_number)
if unique_rows_to_add:
logging.info(f"📝 Dodaję {len(unique_rows_to_add)} nowych unikalnych wierszy do arkusza {sheet_name}...")
self._execute_with_retry(ws.append_rows, unique_rows_to_add, value_input_option="USER_ENTERED")
logging.info("✅ Zakończono dodawanie.")
else:
logging.info(" Nie znaleziono żadnych nowych wierszy do dodania.")
skipped_count = len(rows_data) - len(unique_rows_to_add)
if skipped_count > 0:
logging.info(f"⏭️ Pominięto {skipped_count} wierszy, które już istniały w arkuszu.")