From 47342c0541dd68aa831ea81b12925cf57ace0e18 Mon Sep 17 00:00:00 2001 From: Mike Hosmar Date: Wed, 4 Jun 2025 14:02:21 -0400 Subject: [PATCH 1/5] Add script --- .gitignore | 3 + scripts/update_error_codes/requirements.txt | 14 ++ .../update_error_codes/update_error_codes.py | 138 ++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 scripts/update_error_codes/requirements.txt create mode 100755 scripts/update_error_codes/update_error_codes.py diff --git a/.gitignore b/.gitignore index b2d6de306..a6baeae19 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +**/service_account.json +*/.venv/ diff --git a/scripts/update_error_codes/requirements.txt b/scripts/update_error_codes/requirements.txt new file mode 100644 index 000000000..29c5339b4 --- /dev/null +++ b/scripts/update_error_codes/requirements.txt @@ -0,0 +1,14 @@ +cachetools==5.5.2 +certifi==2025.4.26 +charset-normalizer==3.4.2 +google-auth==2.40.2 +google-auth-oauthlib==1.2.2 +gspread==6.2.1 +idna==3.10 +oauthlib==3.2.2 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +requests==2.32.3 +requests-oauthlib==2.0.0 +rsa==4.9.1 +urllib3==2.4.0 diff --git a/scripts/update_error_codes/update_error_codes.py b/scripts/update_error_codes/update_error_codes.py new file mode 100755 index 000000000..be4cb8919 --- /dev/null +++ b/scripts/update_error_codes/update_error_codes.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +# This script pulls error codes from a Google Sheet and updates the local mdx files in components/error_codes. +# The sheet can be found at: +# https://docs.google.com/spreadsheets/d/1GcQOTUiUgADPrYiLwWsgM9B8dvKHKgTAl_bsJcpXKm0/edit#gid=0 +# It requires the gspread library to interact with Google Sheets. +# It is setup to use a service account for authentication. +# Make sure to set the GOOGLE_SHEET_CREDENTIALS environment variable to the path of your service account JSON file. +# It may be easier to to trigger the script from the github actions workflow. + +import os + +import gspread + + +def create_file(file_path): + """Add a warning at the top of the mdx file indicating it is auto-generated.""" + with open(file_path, 'w', encoding='utf-8') as f: + f.write( + '{/* This file has been auto-generated by scripts/update_error_codes.py */}\n\n' + ) + +def add_section_description(file_path): + """Add heading description to the top-level mdx file.""" + with open(file_path, 'a', encoding='utf-8') as f: + section_description = """ + + +# Error Codes + +This document contains error codes and troubleshooting notes for each error code. + +""" + f.write(section_description) + + +def add_import_top_level_file(file_path, file_name, category_description): + """Update the top-level mdx file with import statements for each category.""" + with open(file_path, 'a', encoding='utf-8') as f: + module_name = category_description.title().replace(' ', '') + f.write(f'import {module_name} from "./{file_name}";\n') + + +def add_impl_top_level_file(file_path, category_description): + """Update the top-level mdx file to include the implementation of each category.""" + with open(file_path, 'a', encoding='utf-8') as f: + module_name = category_description.title().replace(' ', '') + f.write(f'<{module_name} />\n\n') + + +def preamble_error_code_file(file_path, category_number, category_description): + """Write the preamble for the mdx file.""" + with open(file_path, 'a', encoding='utf-8') as f: + f.write( + f'---\n## E{category_number}00 {category_description} {{#{category_description.lower().replace(" ", "-")}}}\n\n' + ) + + +def update_error_code_file(file_path, error_number, error_title, error_message): + """Update the mdx file with the error code and message.""" + with open(file_path, 'a', encoding='utf-8') as f: + f.write(f'### E{int(error_number):02d} - {error_title} {{#e{int(error_number):02d}}} \n{error_message}\n\n') + + +if __name__ == '__main__': + gc = gspread.service_account( + filename=os.environ.get('GOOGLE_SHEET_CREDENTIALS', './service_account.json') + ) + sh = gc.open_by_key('1GcQOTUiUgADPrYiLwWsgM9B8dvKHKgTAl_bsJcpXKm0') + print('Opened Google Sheet:', sh.title) + categories = sh.get_worksheet(0).get_all_records() + + script_dir = os.path.dirname(os.path.abspath(__file__)) + top_level_file_path = os.path.join(script_dir, '../../components/error_codes/error_codes.mdx') + + used_categories = [] + for category in categories: + print('Processing category:', category['Error Category Description']) + file_name = ( + # str(category['Error Category Number']) + '_' + str(category['Error Category Description']).lower().replace(' ', '_') + ) + file_path = os.path.join(script_dir, f'../../components/error_codes/{file_name}.mdx') + records = sh.get_worksheet(int(category['Error Category Number']) + 1).get_all_records() + + # tuple to hold error codes and messages + errors = [] + for record in records: + error_number = record['Error Number'] + error_title = record['Error Title'] + error_message = record['Troubleshooting Notes'] + if not error_title: + continue + errors.append(( + str(category['Error Category Number']) + str(error_number), + error_title, + error_message, + )) + + if len(errors) == 0: + print( + f'No errors found for category {category["Error Category Description"]}. Skipping.' + ) + continue + + used_categories.append(category) + + create_file(file_path) + preamble_error_code_file( + file_path, category['Error Category Number'], category['Error Category Description'] + ) + for error in errors: + error_number, error_title, error_message = error + update_error_code_file(file_path, error_number, error_title, error_message) + print(f'Updated {file_path}') + + create_file(top_level_file_path) + for category in used_categories: + add_import_top_level_file( + top_level_file_path, + f'_{category["Error Category Description"].lower().replace(" ", "_")}.mdx', + category['Error Category Description'], + ) + add_section_description(top_level_file_path) + for category in used_categories: + add_impl_top_level_file(top_level_file_path, category['Error Category Description']) From 4150e3ddf8bf2dc214a1c687d7a2fcc7782aa191 Mon Sep 17 00:00:00 2001 From: Mike Hosmar Date: Wed, 4 Jun 2025 14:02:29 -0400 Subject: [PATCH 2/5] add action --- .github/workflows/update_error_codes.yml | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/update_error_codes.yml diff --git a/.github/workflows/update_error_codes.yml b/.github/workflows/update_error_codes.yml new file mode 100644 index 000000000..789ee0d52 --- /dev/null +++ b/.github/workflows/update_error_codes.yml @@ -0,0 +1,52 @@ +name: Update Error Codes + +on: + workflow_dispatch: {} + schedule: + - cron: '0 2 * * *' # Runs daily at 2:00 AM UTC + +jobs: + update-error-codes: + runs-on: ubuntu-latest + container: + image: python:3.11 + env: + GOOGLE_SHEET_CREDENTIALS: scripts/update_error_codes/service_account.json + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up Python dependencies + run: | + pip install --upgrade pip + pip install -r scripts/update_error_codes/requirements.txt + + - name: Write service account credentials + run: | + mkdir -p scripts/update_error_codes + echo "$GOOGLE_SHEET_CREDENTIALS_JSON" > scripts/update_error_codes/service_account.json + shell: bash + env: + GOOGLE_SHEET_CREDENTIALS_JSON: ${{ secrets.GOOGLE_SHEET_CREDENTIALS_JSON }} + + - name: Run update_error_codes.py + run: | + python scripts/update_error_codes/update_error_codes.py + + - name: Configure git + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + commit-message: 'chore: update error codes from Google Sheet' + title: 'chore: update error codes from Google Sheet' + body: 'Automated update of error codes from Google Sheet.' + branch: update/error-codes-auto + delete-branch: true + add-paths: | + components/error_codes/*.mdx From b344e6d13b0a955d24292fc1fdaa729f9c056b63 Mon Sep 17 00:00:00 2001 From: Mike Hosmar Date: Mon, 9 Jun 2025 15:41:46 -0400 Subject: [PATCH 3/5] small changes lost when branch was made --- scripts/update_error_codes/update_error_codes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_error_codes/update_error_codes.py b/scripts/update_error_codes/update_error_codes.py index be4cb8919..52c378e70 100755 --- a/scripts/update_error_codes/update_error_codes.py +++ b/scripts/update_error_codes/update_error_codes.py @@ -40,7 +40,7 @@ def add_section_description(file_path): # Error Codes -This document contains error codes and troubleshooting notes for each error code. +This section includes a full list of the possible error codes along with troubleshooting guidance for each one. """ f.write(section_description) @@ -64,7 +64,7 @@ def preamble_error_code_file(file_path, category_number, category_description): """Write the preamble for the mdx file.""" with open(file_path, 'a', encoding='utf-8') as f: f.write( - f'---\n## E{category_number}00 {category_description} {{#{category_description.lower().replace(" ", "-")}}}\n\n' + f'---\n## E{category_number}00 - {category_description} {{#{category_description.lower().replace(" ", "-")}}}\n\n' ) From 8131b7ef1d97a344f8353cefd4813c92a104c84b Mon Sep 17 00:00:00 2001 From: Mike Hosmar Date: Thu, 12 Jun 2025 12:44:45 -0400 Subject: [PATCH 4/5] Match ID in Robot QR --- scripts/update_error_codes/update_error_codes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_error_codes/update_error_codes.py b/scripts/update_error_codes/update_error_codes.py index 52c378e70..66df1a074 100755 --- a/scripts/update_error_codes/update_error_codes.py +++ b/scripts/update_error_codes/update_error_codes.py @@ -71,7 +71,7 @@ def preamble_error_code_file(file_path, category_number, category_description): def update_error_code_file(file_path, error_number, error_title, error_message): """Update the mdx file with the error code and message.""" with open(file_path, 'a', encoding='utf-8') as f: - f.write(f'### E{int(error_number):02d} - {error_title} {{#e{int(error_number):02d}}} \n{error_message}\n\n') + f.write(f'### E{int(error_number):02d} - {error_title} {{#E{int(error_number):02d}}} \n{error_message}\n\n') if __name__ == '__main__': From d3d95371607e8748d7bbd6838969fac8be7fc38b Mon Sep 17 00:00:00 2001 From: Mike Hosmar Date: Fri, 4 Jul 2025 10:52:07 -0400 Subject: [PATCH 5/5] Add script to generate labels for clearpath_diagnostics --- .../generate_firmware_errors_cpp.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 scripts/update_error_codes/generate_firmware_errors_cpp.py diff --git a/scripts/update_error_codes/generate_firmware_errors_cpp.py b/scripts/update_error_codes/generate_firmware_errors_cpp.py new file mode 100644 index 000000000..750535f3c --- /dev/null +++ b/scripts/update_error_codes/generate_firmware_errors_cpp.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +# This script pulls error codes from an Excel file and generates a C++ data structure. +# The structure is: inline static const std::map> FIRMWARE_ERRORS = { +# {error_num, {"Error Title", "Troubleshooting Notes"}}, +# }; + +import os +import gspread + +def generate_cpp_map(errors, output_path): + """Generate a C++ map from the error data.""" + with open(output_path, 'w', encoding='utf-8') as f: + f.write('#include \n#include \n#include \n\n') + f.write('inline static const std::map> FIRMWARE_ERRORS = {\n') + for error in errors: + error_num = error[0] + error_title = error[1].replace('"', '\"') if error[1] else '' + error_notes = error[2].replace('"', '\"') if error[2] else '' + f.write(f' {{{error_num}, {{"{error_title}", "{error_notes}"}}}},\n') + f.write('};\n') + +if __name__ == '__main__': + # Use Google Sheets API as before + gc = gspread.service_account( + filename=os.environ.get('GOOGLE_SHEET_CREDENTIALS', './service_account.json') + ) + sh = gc.open_by_key('1GcQOTUiUgADPrYiLwWsgM9B8dvKHKgTAl_bsJcpXKm0') + categories = sh.get_worksheet(0).get_all_records() + + errors = [] + for category in categories: + records = sh.get_worksheet(int(category['Error Category Number']) + 1).get_all_records() + for record in records: + error_number = record['Error Number'] + error_title = record['Error Title'] + error_message = record['Troubleshooting Notes'] + if not error_title: + continue + errors.append(( + int(str(category['Error Category Number']) + str(error_number)), + error_title, + error_message, + )) + generate_cpp_map(errors, 'firmware_errors.hpp')