| #!/usr/bin/env python3 |
| # Copyright 2021 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # pylint: disable=C0209,R9170 |
| |
| """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 |
| """ |
| |
| from __future__ import print_function |
| |
| import sqlite3 |
| |
| from common import chromeos_path |
| from common import rebase_target_tag |
| from common import rebase_target_version |
| from common import rebasedb |
| from common import upstream_path |
| from common import upstreamdb |
| import genlib |
| from githelpers import revision_sha |
| |
| from config import rebase_baseline_branch |
| from config import rebase_spreadsheet_filename |
| from config import spreadsheet_share_with |
| |
| |
| other_topic_id = 0 # Sheet Id to be used for "other" topic |
| |
| red = {"red": 1, "green": 0.4, "blue": 0} |
| yellow = {"red": 1, "green": 1, "blue": 0} |
| orange = {"red": 1, "green": 0.6, "blue": 0} |
| green = {"red": 0, "green": 0.9, "blue": 0} |
| blue = {"red": 0.3, "green": 0.6, "blue": 1} |
| white = {"red": 1, "green": 1, "blue": 1} |
| |
| share_msg = f"Rebase of {rebase_baseline_branch} branch to {rebase_target_tag()} kernel upstream" |
| |
| |
| def get_other_topic_id(): |
| """Calculate other_topic_id""" |
| |
| global other_topic_id # pylint: disable=global-statement |
| |
| conn = sqlite3.connect(rebasedb) |
| c = conn.cursor() |
| |
| c.execute("select topic, name from topics order by name") |
| for topic, name in c.fetchall(): |
| if name == "other": |
| other_topic_id = topic |
| break |
| if topic >= other_topic_id: |
| other_topic_id = topic + 1 |
| |
| conn.close() |
| return other_topic_id |
| |
| |
| def add_topics_summary(requests): |
| """Add topics to summary sheet""" |
| |
| conn = sqlite3.connect(rebasedb) |
| c = conn.cursor() |
| c2 = conn.cursor() |
| version = rebase_target_version() |
| counted_rows = 0 |
| counted_effrows = 0 |
| |
| c.execute("select topic, name from topics order by name") |
| rowindex = 1 |
| for topic, name in c.fetchall(): |
| # Insert 'other' topic last, and don't count it here. |
| if name == "other": |
| continue |
| # Only add summary entry if there are commits touching this topic |
| c2.execute( |
| "select disposition, reason from commits where topic=%d" % topic |
| ) |
| rows = 0 |
| effrows = 0 |
| for d, r in c2.fetchall(): |
| # Skip entries associated with a topic if they are fully upstream |
| # and are not being replaced. |
| if d == "drop" and r == "upstream": |
| continue |
| rows += 1 |
| if d == "pick": |
| effrows += 1 |
| counted_rows += rows |
| counted_effrows += effrows |
| requests.append( |
| { |
| "pasteData": { |
| "data": '=HYPERLINK("#gid=%d","%s");%d;%d;;;;chromeos-%s-%s' |
| % ( |
| topic, |
| name, |
| rows, |
| effrows, |
| version, |
| name.replace("/", "-"), |
| ), |
| "type": "PASTE_NORMAL", |
| "delimiter": ";", |
| "coordinate": {"sheetId": 0, "rowIndex": rowindex}, |
| } |
| } |
| ) |
| rowindex += 1 |
| |
| allrows = 0 |
| alleff = 0 |
| c2.execute("select disposition, reason from commits where topic != 0") |
| for d, r in c2.fetchall(): |
| if d == "drop" and r == "upstream": |
| continue |
| allrows += 1 |
| if d != "drop": |
| alleff += 1 |
| |
| # Now create an 'other' topic. We'll use it for unnamed topics. |
| requests.append( |
| { |
| "pasteData": { |
| "data": '=HYPERLINK("#gid=%d","other");%d;%d;;;;chromeos-%s-other' |
| % ( |
| other_topic_id, |
| allrows - counted_rows, |
| alleff - counted_effrows, |
| version, |
| ), |
| "type": "PASTE_NORMAL", |
| "delimiter": ";", |
| "coordinate": {"sheetId": 0, "rowIndex": rowindex}, |
| } |
| } |
| ) |
| |
| conn.close() |
| |
| |
| def create_summary(requests): |
| """Create summary sheet""" |
| |
| requests.append( |
| { |
| "updateSheetProperties": { |
| # 'sheetId': 0, |
| "properties": { |
| "title": "Summary", |
| }, |
| "fields": "title", |
| } |
| } |
| ) |
| |
| header = ( |
| "Topic, Entries, Effective Entries, Owner, Reviewer, Status, " |
| "Topic branch, Comments" |
| ) |
| genlib.add_sheet_header(requests, 0, header) |
| |
| # Now add all topics |
| add_topics_summary(requests) |
| |
| |
| def add_description(requests): |
| """Add describing text to 'Summary' sheet""" |
| |
| requests.append( |
| { |
| "appendCells": { |
| "sheetId": 0, |
| "rows": [ |
| {}, |
| { |
| "values": [ |
| { |
| "userEnteredValue": { |
| "stringValue": "Topic branch markers:" |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": "blue"}, |
| "userEnteredFormat": {"backgroundColor": blue}, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": "branch dropped: All patches upstream, no longer applicable, moved to another topic, or no longer needed" # pylint: disable=line-too-long |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": "green"}, |
| "userEnteredFormat": {"backgroundColor": green}, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": "clean (no or minor conflicts)" |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": "yellow"}, |
| "userEnteredFormat": { |
| "backgroundColor": yellow |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": "mostly clean; problematic patches marked yellow" |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": "orange"}, |
| "userEnteredFormat": { |
| "backgroundColor": orange |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": """some problems; problematic patches marked |
| orange""" |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": "red"}, |
| "userEnteredFormat": {"backgroundColor": red}, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": "severe problems" |
| }, |
| }, |
| ] |
| }, |
| {}, |
| { |
| "values": [ |
| {}, |
| { |
| "userEnteredValue": { |
| "stringValue": "Source", |
| }, |
| "userEnteredFormat": { |
| "textFormat": {"bold": True}, |
| }, |
| }, |
| { |
| "userEnteredValue": {"stringValue": "Target"}, |
| "userEnteredFormat": { |
| "textFormat": {"bold": True}, |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": { |
| "stringValue": "Revision", |
| }, |
| "userEnteredFormat": { |
| "textFormat": {"bold": True}, |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": rebase_baseline_branch, |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": rebase_target_tag(), |
| }, |
| }, |
| ] |
| }, |
| { |
| "values": [ |
| { |
| "userEnteredValue": { |
| "stringValue": "SHA", |
| }, |
| "userEnteredFormat": { |
| "textFormat": {"bold": True}, |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": revision_sha( |
| chromeos_path, |
| rebase_baseline_branch, |
| ), |
| }, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": revision_sha( |
| upstream_path, rebase_target_tag() |
| ), |
| }, |
| }, |
| ] |
| }, |
| ], |
| "fields": "*", |
| } |
| } |
| ) |
| |
| |
| def addsheet(requests, index, topic, name): |
| """Add sheet with header""" |
| |
| print('Adding sheet id=%d index=%d title="%s"' % (topic, index, name)) |
| |
| requests.append( |
| { |
| "addSheet": { |
| "properties": { |
| "sheetId": topic, |
| "index": index, |
| "title": name, |
| } |
| } |
| } |
| ) |
| |
| # Generate header row |
| genlib.add_sheet_header( |
| requests, topic, "SHA, Description, Disposition, Contact, Comments" |
| ) |
| |
| |
| def add_topics_sheets(requests): |
| """Add topics sheets""" |
| |
| conn = sqlite3.connect(rebasedb) |
| c = conn.cursor() |
| |
| c.execute("select topic, name from topics order by name") |
| |
| index = 1 |
| for topic, name in c.fetchall(): |
| # Insert 'other' topic at very end |
| if name != "other": |
| addsheet(requests, index, topic, name) |
| index += 1 |
| |
| # Add 'other' topic sheet at the very end |
| addsheet(requests, index, other_topic_id, "other") |
| conn.close() |
| |
| |
| def add_sha( |
| requests, |
| sheetId, |
| sha, |
| contact, |
| email, |
| subject, |
| disposition, |
| reason, |
| dsha, |
| origin, |
| ): |
| """Add sha to topics sheet""" |
| |
| comment = "" |
| color = white |
| |
| contact_format = "stringValue" |
| if ( |
| "@google.com" in email |
| or "@chromium.org" in email |
| or "@collabora.com" in email |
| ): |
| contact = '=HYPERLINK("mailto:%s","%s")' % (email, contact) |
| contact_format = "formulaValue" |
| |
| if disposition == "pick" and reason == "revisit": |
| if dsha: |
| comment = "revisit (similarities with %s commit %s)" % ( |
| origin, |
| dsha, |
| ) |
| else: |
| comment = "revisit (imperfect match)" |
| color = orange |
| elif disposition == "replace" and dsha: |
| comment = "with %s commit %s" % (origin, dsha) |
| color = yellow |
| if reason == "revisit": |
| comment += " (revisit: imperfect match)" |
| color = orange |
| elif disposition == "drop": |
| color = yellow |
| if reason == "revisit": |
| color = red |
| if dsha: |
| comment = "revisit (imperfect match with %s commit %s)" % ( |
| origin, |
| dsha, |
| ) |
| else: |
| comment = "revisit (imperfect match)" |
| elif reason == "upstream/fixup": |
| comment = "fixup of upstream patch %s" % dsha |
| elif reason == "upstream/match": |
| comment = "%s commit %s" % (origin, dsha) |
| elif reason == "revisit/fixup": |
| comment = "fixup of %s commit %s" % (origin, dsha) |
| elif reason == "reverted": |
| comment = reason |
| if dsha: |
| comment += " (commit %s)" % dsha |
| elif reason == "fixup/reverted": |
| comment = "fixup of reverted commit %s" % dsha |
| else: |
| comment = reason |
| if dsha: |
| comment += " (%s commit %s)" % (origin, dsha) |
| |
| print("Adding sha %s (%s) to sheet ID %d" % (sha, subject, sheetId)) |
| |
| requests.append( |
| { |
| "appendCells": { |
| "sheetId": sheetId, |
| "rows": [ |
| { |
| "values": [ |
| { |
| "userEnteredValue": {"stringValue": sha}, |
| "userEnteredFormat": {"backgroundColor": color}, |
| }, |
| { |
| "userEnteredValue": {"stringValue": subject}, |
| "userEnteredFormat": {"backgroundColor": color}, |
| }, |
| { |
| "userEnteredValue": { |
| "stringValue": disposition |
| }, |
| "userEnteredFormat": {"backgroundColor": color}, |
| }, |
| { |
| "userEnteredValue": {contact_format: contact}, |
| "userEnteredFormat": {"backgroundColor": color}, |
| }, |
| { |
| "userEnteredValue": {"stringValue": comment}, |
| "userEnteredFormat": {"backgroundColor": color}, |
| }, |
| ] |
| } |
| ], |
| "fields": "*", |
| } |
| } |
| ) |
| |
| |
| def add_commits(requests): |
| """Add commits to sheets""" |
| |
| conn = sqlite3.connect(rebasedb) |
| uconn = sqlite3.connect(upstreamdb) |
| c = conn.cursor() |
| c2 = conn.cursor() |
| cu = uconn.cursor() |
| |
| sheets = set([]) |
| |
| c.execute( |
| "select sha, dsha, contact, email, subject, disposition, reason, topic \ |
| from commits where topic > 0" |
| ) |
| for ( |
| sha, |
| dsha, |
| contact, |
| email, |
| subject, |
| disposition, |
| reason, |
| topic, |
| ) in c.fetchall(): |
| # Skip entries associated with a topic if they are fully upstream |
| # and are not being replaced. |
| if disposition == "drop" and reason == "upstream": |
| continue |
| c2.execute("select topic, name from topics where topic=%d" % topic) |
| if c2.fetchone(): |
| sheetId = topic |
| else: |
| sheetId = other_topic_id |
| |
| cu.execute("select sha from commits where sha='%s'" % dsha) |
| if cu.fetchone(): |
| origin = "upstream" |
| else: |
| origin = "linux-next" |
| sheets.add(sheetId) |
| add_sha( |
| requests, |
| sheetId, |
| sha, |
| contact, |
| email, |
| subject, |
| disposition, |
| reason, |
| dsha, |
| origin, |
| ) |
| |
| for s in sheets: |
| genlib.resize_sheet(requests, s, 0, 5) |
| |
| |
| def main(): |
| """Main function""" |
| |
| sheet = genlib.init_spreadsheet( |
| rebase_spreadsheet_filename, |
| "Rebase %s -> %s" % (rebase_baseline_branch, rebase_target_tag()), |
| ) |
| get_other_topic_id() |
| |
| requests = [] |
| create_summary(requests) |
| add_topics_sheets(requests) |
| genlib.doit(sheet, requests) |
| requests = [] |
| add_commits(requests) |
| # Now auto-resize columns A, B, C, and G in Summary sheet |
| genlib.resize_sheet(requests, 0, 0, 3) |
| genlib.resize_sheet(requests, 0, 6, 7) |
| # Add description after resizing |
| add_description(requests) |
| genlib.doit(sheet, requests) |
| genlib.share_sheet(sheet[1], spreadsheet_share_with, share_msg) |
| |
| |
| if __name__ == "__main__": |
| main() |