import os, sys, traceback try: import oauth2client except: os.system('pip install oauth2client') import oauth2client from oauth2client.file import Storage from oauth2client import tools from oauth2client.client import flow_from_clientsecrets try: from apiclient.discovery import build except: os.system('pip install google-api-python-client') from apiclient.discovery import build try: import gspread, time from gspread_formatting import cellFormat, textFormat, color, format_cell_range except: os.system('pip3 install gspread') os.system('pip3 install gspread_formatting') import gspread, time from gspread_formatting import cellFormat, textFormat, color, format_cell_range from support.base import get_logger, d logger = get_logger() class GoogleSheetBase: current_flow = None color_format = { 'green' : cellFormat( backgroundColor=color(0, 1, 0), #set it to yellow textFormat=textFormat(foregroundColor=color(0, 0, 0)), ), 'yellow' : cellFormat( backgroundColor=color(1, 1, 0), #set it to yellow textFormat=textFormat(foregroundColor=color(0, 0, 0)), ), 'white' : cellFormat( backgroundColor=color(1, 1, 1), #set it to yellow textFormat=textFormat(foregroundColor=color(0, 0, 0)), ) } def __init__(self, doc_id, credentials_filepath, tab_index, unique_header): self.credentials_filepath = credentials_filepath self.credentials = self.get_credentials() self.doc_id = doc_id doc_url = f'https://docs.google.com/spreadsheets/d/{doc_id}' gsp = gspread.authorize(self.credentials) doc = gsp.open_by_url(doc_url) self.tab_index = tab_index self.ws = doc.get_worksheet(tab_index) self.header_info = None self.header_info_reverse = None self.unique_header = unique_header def get_credentials(self, project_filepath=None): if os.path.exists(self.credentials_filepath) == False: logger.info(f"credentials_filepath : {self.credentials_filepath}") url = self.__make_token_cli(project_filepath) logger.debug(f"Auth URL : {url}") code = input("Input Code : ") self.__save_token(self.credentials_filepath, code) store = Storage(self.credentials_filepath) credentials = store.get() if not credentials or credentials.invalid: logger.warning('credentials error') #flow = client.flow_from_clientsecrets('credentials.json', SCOPES) #creds = tools.run_flow(flow, store) os.remove(self.credentials_filepath) return self.get_credentials(self.credentials_filepath) return credentials def __make_token_cli(self, project_filepath): try: if project_filepath == None: project_filepath = os.path.join(os.path.dirname(__file__), 'cs.json') self.current_flow = flow_from_clientsecrets( project_filepath, # downloaded file 'https://www.googleapis.com/auth/drive', # scope redirect_uri='urn:ietf:wg:oauth:2.0:oob') return self.current_flow.step1_get_authorize_url() except Exception as e: logger.error(f"Exception: {e}") logger.error(traceback.format_exc()) def __save_token(self, credentials_filepath, code): try: credentials = self.current_flow.step2_exchange(code) storage = Storage(credentials_filepath) storage.put(credentials) return True except Exception as e: logger.error(f"Exception: {e}") logger.error(traceback.format_exc()) return False def get_sheet_data(self): tmp = self.ws.get_all_values()#[:-1] self.set_sheet_header(tmp[0]) rows = tmp[1:] ret = [] for row in rows: item = {} for idx, col in enumerate(row): item[self.header_info_reverse[idx+1]] = col ret.append(item) return ret def set_sheet_header(self, row): self.header_info = {} self.header_info_reverse = {} for idx, col in enumerate(row): self.header_info[col] = idx + 1 self.header_info_reverse[idx+1] = col logger.debug(self.header_info) def find_row_index(self, total_data, data): find_row_index = -1 #find = False #data['IDX'] = len(total_data)+1 for idx, item in enumerate(total_data): if item[self.unique_header] == str(data[self.unique_header]): #find = True find_row_index = idx #data['IDX'] = find_row_index + 1 break if find_row_index == -1: data['IDX'] = len(total_data)+1 return find_row_index def sleep(self): time.sleep(0.5) def sleep_exception(self): time.sleep(10) def after_update_cell(self, sheet_row_index, sheet_col_index, key, value, old_value): pass def set_color(self, sheet_row, sheet_col1, sheet_col2, color): format_cell_range(self.ws, gspread.utils.rowcol_to_a1(sheet_row,sheet_col1)+':' + gspread.utils.rowcol_to_a1(sheet_row,sheet_col2), color) def set_color_row(self, sheet_row, color): format_cell_range(self.ws, gspread.utils.rowcol_to_a1(sheet_row,1)+':' + gspread.utils.rowcol_to_a1(sheet_row,len(self.header_info)), color) def set_color_cell(self, sheet_row, sheet_col, color): format_cell_range(self.ws, gspread.utils.rowcol_to_a1(sheet_row,sheet_col)+':' + gspread.utils.rowcol_to_a1(sheet_row,sheet_col), color) def write_data(self, total_data, data): find_row_index = self.find_row_index(total_data, data) write_count = 0 for key, value in data.items(): if key.startswith('_'): continue if value == None: continue if key not in self.header_info: continue while True: try: if find_row_index != -1 and str(total_data[find_row_index][key]) != str(value): logger.warning(f"업데이트 : {key} {total_data[find_row_index][key]} ==> {value}") self.ws.update_cell(find_row_index+2, self.header_info[key], value) self.after_update_cell(find_row_index+2, self.header_info[key], key, value, total_data[find_row_index][key]) write_count += 1 self.sleep() elif find_row_index == -1 and value != '': logger.warning(f"추가 : {key} {value}") self.ws.update_cell(len(total_data)+2, self.header_info[key], value) self.after_update_cell(len(total_data)+2, self.header_info[key], key, value, None) write_count += 1 self.sleep() break except gspread.exceptions.APIError: self.sleep_exception() except Exception as exception: logger.error(f"{key} - {value}") logger.error('Exception:%s', exception) logger.error(traceback.format_exc()) logger.error(self.header_info) self.sleep_exception() if find_row_index == -1: total_data.append(data) else: total_data[find_row_index] = data return write_count