from google.oauth2.service_account import Credentials from googleapiclient.discovery import build SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'] def get_spreatsheed_service(credentials): return build('sheets', 'v4', credentials=credentials, cache_discovery=False) def get_google_creds(): creds = None creds = Credentials.from_service_account_file('credentials.json', scopes=SCOPES) return creds def process_spreadsheet_data(rows): processed_data = [] for row in rows: processed_row = convert_spreadsheet_data(row) processed_data.append(processed_row) if processed_row else None return processed_data from datetime import datetime from datetime import datetime def convert_spreadsheet_data(data, default_year=None): if not data.get('day') or not data['day'].isdigit(): return None result = {} result['day'] = int(data['day']) if default_year is None: default_year = datetime.now().year date_str = data.get('date', '').strip() parsed_date = None if date_str: try: parsed_date = datetime.strptime(f'{date_str} {default_year}', '%b %d %Y') except ValueError: try: parsed_date = datetime.strptime(f'{date_str} {default_year}', '%B %d %Y') except: parsed_date = None if parsed_date and parsed_date.month == 1 and ('Dec' in data.get('date', '')): parsed_date = parsed_date.replace(year=default_year + 1) result['date'] = parsed_date def parse_percent(value): if not value: return 0.0 v = value.strip().replace(',', '.').replace('%', '') try: return float(v) / 100.0 except: return 0.0 result['plan'] = parse_percent(data.get('plan', '0')) result['actual'] = parse_percent(data.get('actual', '0')) result['gap'] = result['actual'] - result['plan'] return result def fetch_all_sections(service, spreadsheet_id, sheet_name): result = service.spreadsheets().values().get(spreadsheetId=spreadsheet_id, range=f'{sheet_name}!A5:M5000').execute() values = result.get('values', []) if not values: raise ValueError('No data found in sheet') data = [] current_section = None current_subsystem = None for row in values: row += [''] * (13 - len(row)) colA, colB, colC, colD, colE, colF, colG, colH, colI, colJ, colK, colL, colM = row if colC and (not colD) and (not colE): current_section = colC.strip() current_subsystem = None continue if colD and (not colE): current_subsystem = colD.strip() continue if colE: task = colE.strip() pic = colF.strip() start_date = indo_formatted_date(colG.strip()) finish_date = indo_formatted_date(colH.strip()) duration = colI.strip() plan = colK.strip() actual = colL.strip() gap = colM.strip() data.append({'system': current_section, 'subsystem': current_subsystem, 'task': task, 'PIC': pic, 'start_date': start_date, 'end_date': finish_date, 'duration': int(duration), 'plan': plan, 'actual': actual, 'gap': gap}) return data def indo_formatted_date(date_str, base_year=2025): eng_to_indo_month = {'Jan': 'Januari', 'Feb': 'Februari', 'Mar': 'Maret', 'Apr': 'April', 'May': 'Mei', 'Jun': 'Juni', 'Jul': 'Juli', 'Aug': 'Agustus', 'Sep': 'September', 'Oct': 'Oktober', 'Nov': 'November', 'Dec': 'Desember'} indo_days = {0: 'Senin', 1: 'Selasa', 2: 'Rabu', 3: 'Kamis', 4: 'Jumat', 5: 'Sabtu', 6: 'Minggu'} if '-' in date_str: d, m = date_str.split('-') date_str = f'{m} {d}' try: dt = datetime.strptime(f'{date_str} {base_year}', '%b %d %Y') except: return None if dt.month == 1: dt = dt.replace(year=base_year + 1) day_name = indo_days[dt.weekday()] month_name = eng_to_indo_month[dt.strftime('%b')] return f'{day_name}, {month_name} {dt.day}, {dt.year}'