blob: 88de5dcf6c630d24acf6c50cb7993777cc71f732 [file] [log] [blame]
# -*- coding: utf-8 -*-"
# Copyright 2021 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Use information in rebase database to create rebase spreadsheet
Required python modules:
google-api-python-client google-auth-httplib2 google-auth-oauthlib
The Google Sheets API needs to be enabled to run this script.
Also, you'll need to generate access credentials and store those
in credentials.json.
Disable pyline noise
pylint: disable=no-absolute-import
"""
import os
import pickle
from googleapiclient import discovery # pylint: disable=import-error
from google_auth_oauthlib.flow import InstalledAppFlow # pylint: disable=import-error
from google.auth.transport.requests import Request # pylint: disable=import-error, disable=no-name-in-module
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
# Spreadsheet functions
def getsheet():
"""Get and return reference to spreadsheet"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
try: # py2 or token saved with py3
creds = pickle.load(token)
except UnicodeDecodeError: # py3, token saved with py2
creds = pickle.load(token, encoding='latin-1') # pylint: disable=unexpected-keyword-arg
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = discovery.build('sheets', 'v4', credentials=creds)
# service = discovery.build('sheets', 'v4', developerKey=API_KEY)
return service.spreadsheets() # pylint: disable=no-member
def init_spreadsheet(filename, title):
"""Initialize spreadsheet"""
sheet = getsheet()
try:
with open(filename, 'r') as f:
ssid = f.read().strip('\n')
request = sheet.get(
spreadsheetId=ssid, ranges=[], includeGridData=False)
response = request.execute()
sheets = response.get('sheets')
delete_sheets((sheet, ssid), sheets)
except IOError:
ssid = create_spreadsheet(sheet, title)
with open(filename, 'w') as f:
f.write(ssid)
return (sheet, ssid)
# Generic topic functions
def get_other_topic_id(c):
"""Calculate and return other_topic_id"""
other_topic_id = 0
c.execute('select topic, name from topics order by name')
for topic, name in c.fetchall():
if name == 'other':
return topic
if topic >= other_topic_id:
other_topic_id = topic + 1
return other_topic_id
def get_topic_name(c, topic):
"""Get topic name from topic id"""
c.execute("select name from topics where topic is '%s'" % topic)
topic = c.fetchone()
if topic:
return topic[0]
return None
# Spreadsheet manipulation functions
def doit(sheet, requests):
"""Execute a request"""
body = {'requests': requests}
request = sheet[0].batchUpdate(spreadsheetId=sheet[1], body=body)
response = request.execute()
return response
def hide_sheet(sheet, sheetId, hide):
"""Move 'Data' sheet to end of spreadsheet."""
request = []
request.append({
'updateSheetProperties': {
'properties': {
'sheetId': sheetId,
'hidden': hide,
},
'fields': 'hidden'
}
})
doit(sheet, request)
def create_spreadsheet(sheet, title):
"""Create a spreadsheet and return reference to it"""
spreadsheet = {'properties': {'title': title}}
request = sheet.create(body=spreadsheet, fields='spreadsheetId')
response = request.execute()
return response.get('spreadsheetId')
def delete_sheets(sheet, sheets):
"""Delete all sheets except sheet 0. In sheet 0, delete all values."""
# Unhide 'Data' sheet. If it is hidden we can't remove the other sheets.
hide_sheet(sheet, 0, False)
request = []
for s in sheets:
sheetId = s['properties']['sheetId']
if sheetId != 0:
request.append({'deleteSheet': {'sheetId': sheetId}})
else:
rows = s['properties']['gridProperties']['rowCount']
request.append({
'deleteRange': {
'range': {
'sheetId': sheetId,
'startRowIndex': 0,
'endRowIndex': rows,
},
'shiftDimension': 'ROWS',
}
})
# We are letting this fail if there was nothing to clean. This will
# hopefully result in re-creating the spreadsheet.
doit(sheet, request)
def resize_sheet(requests, sheetId, start, end):
"""Resize a sheet in provided range"""
requests.append({
'autoResizeDimensions': {
'dimensions': {
'sheetId': sheetId,
'dimension': 'COLUMNS',
'startIndex': start,
'endIndex': end
}
}
})
def add_sheet_header(requests, sheetId, fields):
"""Add provided header line to specified sheet.
Make it bold.
Args:
requests: Reference to list of requests to send to API.
sheetId: Sheet Id
fields: string with comma-separated list of fields
"""
# Generate header row
requests.append({
'pasteData': {
'data': fields,
'type': 'PASTE_NORMAL',
'delimiter': ',',
'coordinate': {
'sheetId': sheetId,
'rowIndex': 0
}
}
})
# Convert header row to bold and centered
requests.append({
'repeatCell': {
'range': {
'sheetId': sheetId,
'startRowIndex': 0,
'endRowIndex': 1
},
'cell': {
'userEnteredFormat': {
'horizontalAlignment': 'CENTER',
'textFormat': {
'bold': True
}
}
},
'fields': 'userEnteredFormat(textFormat,horizontalAlignment)'
}
})
def move_sheet(sheet, sheetId, to):
"""Move 'Data' sheet to end of spreadsheet."""
request = []
request.append({
'updateSheetProperties': {
'properties': {
'sheetId': sheetId,
'index': to,
},
'fields': 'index'
}
})
doit(sheet, request)
def source_range(sheetId, rows, column):
"""Return source range"""
return {
'sourceRange': {
'sources': [{
'sheetId': sheetId,
'startRowIndex': 0,
'endRowIndex': rows,
'startColumnIndex': column,
'endColumnIndex': column + 1
}]
}
}
def scope(name, sheetId, rows, column):
"""Return single source range"""
return {name: source_range(sheetId, rows, column)}
def sscope(name, sheetId, rows, start, end):
"""Return multiple source ranges"""
s = [scope(name, sheetId, rows, start)]
while start < end:
start += 1
s += [scope(name, sheetId, rows, start)]
return s