diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9386c09..88723ed 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -20,6 +20,9 @@ COPY compiler_admin compiler_admin COPY pyproject.toml pyproject.toml RUN pip install -e .[dev,test] +COPY docs/requirements.txt docs/requirements.txt +RUN pip install -r docs/requirements.txt + USER root RUN chown -R $USER:$USER /home/$USER USER $USER diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5104125..3750cc3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,6 +2,8 @@ "name": "compilerla/admin", "dockerComposeFile": ["../compose.yaml"], "service": "dev", + "runServices": ["dev", "docs"], + "forwardPorts": ["docs:8000"], "workspaceFolder": "/home/compiler/compiler-admin", "postAttachCommand": ["/bin/bash", ".devcontainer/postAttach.sh"], "customizations": { diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml new file mode 100644 index 0000000..dea09fe --- /dev/null +++ b/.github/workflows/mkdocs.yml @@ -0,0 +1,106 @@ +name: Publish docs +on: + workflow_dispatch: + pull_request: + branches: + - main + paths: + - "docs/**" + - "mkdocs.yml" + - ".github/workflows/mkdocs.yml" + push: + branches: + - main + paths: + - ".github/workflows/mkdocs.yml" + - "docs/**" + - ".markdownlint.yaml" + - "mkdocs.yml" + +jobs: + docs-preview: + name: Publish docs preview + runs-on: ubuntu-latest + # only pull requests should generate a preview + if: github.event.pull_request + permissions: + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: "refs/pull/${{ github.event.number }}/merge" + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version-file: .github/workflows/.python-version + cache: pip + cache-dependency-path: | + "**/pyproject.toml" + "docs/requirements.txt" + + - name: Build MkDocs website + run: | + pip install .[dev] -r docs/requirements.txt + mkdocs build + + - name: Install Netlify CLI + run: npm install --location=global netlify-cli@17.x.x + + - name: Deploy Preview to Netlify + run: | + netlify deploy \ + --alias="${GITHUB_REPOSITORY#*/}-${{ github.event.number }}" \ + --auth=${{ secrets.NETLIFY_AUTH_TOKEN }} \ + --dir="site" \ + --site=${{ vars.NETLIFY_PREVIEW_APP_SITE_ID }} + + - name: Find existing comment + uses: peter-evans/find-comment@v3 + id: find-comment + with: + issue-number: ${{ github.event.number }} + comment-author: "github-actions[bot]" + body-includes: "Preview url: https://" + + - name: Add Netlify link PR comment + uses: actions/github-script@v7 + if: steps.find-comment.outputs.comment-id == '' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const hostnameSuffix = "compiler-previews.netlify.app" + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Preview url: https://${context.repo.repo}-${{ github.event.number }}--${hostnameSuffix}`, + }) + + docs: + name: Publish docs + runs-on: ubuntu-latest + # don't publish for pull requests + if: github.event.pull_request == null + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version-file: .github/workflows/.python-version + cache: pip + cache-dependency-path: | + "**/pyproject.toml" + "docs/requirements.txt" + + - name: Install MkDocs requirements + run: | + pip install .[dev] -r docs/requirements.txt + + - name: Deploy MkDocs website + run: | + mkdocs gh-deploy --force diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..f4c8d40 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,19 @@ +# includes/excludes all rules by default +default: true + +# 4-space list indentation works best with MkDocs +MD007: + indent: 4 + +# Remove line length limit - no one wants to hard wrap all their paragraphs +MD013: false + +# Allow inline HTML +MD033: false + +# Allow duplicate headers +MD024: false + +# Allow mixing code block and fenced code regions +# Useful for pages with admonitions and fenced code blocks +MD046: false diff --git a/README.md b/README.md index 33d27f2..99d49ed 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,15 @@ Automating Compiler's administrative tasks. -Built on top of [GAMADV-XTD3](https://github.com/taers232c/GAMADV-XTD3) and [GYB](https://github.com/GAM-team/got-your-back). +Built on top of [GAM7](https://github.com/GAM-team/GAM) and [GYB](https://github.com/GAM-team/got-your-back). **Note:** This tool can only be used by those with administrator access to Compiler's Google Workspace. -## Usage +## Documentation + +For full installation, usage, and command reference, please see the [docs site](https://docs.compiler.la/compiler-admin). + +## Basic Usage ```bash $ compiler-admin -h @@ -24,229 +28,3 @@ options: -h, --help show this help message and exit -v, --version show program's version number and exit ``` - -## Getting started - -```bash -mkdir -p ~/.config/compiler-admin - -git clone https://github.com/compilerla/compiler-admin.git - -cd compiler-admin -``` - -Now open in VS Code, and when prompted, reopen in the devcontainer. - -## Initial setup - -Initial setup of a GAMADV-XTD3 project and GYB project is required to provide necessary API access to the Google Workspace. - -```bash -$ compiler-admin init -h -usage: compiler-admin init [-h] [--gam] [--gyb] username - -positional arguments: - username A Compiler user account name, sans domain. - -options: - -h, --help show this help message and exit - --gam If provided, initialize a new GAM project. - --gyb If provided, initialize a new GYB project. -``` - -The `init` commands follows the steps in the [GAMADV-XTD3 Wiki](https://github.com/taers232c/GAMADV-XTD3/wiki/#requirements). - -Additionally, GYB is used for Gmail backup/restore. See the [GYB Wiki](https://github.com/GAM-team/got-your-back/wiki) for more information. - -## Working with time entires - -The `time` command provides an interface for working with time entries from Compiler's various systems: - -```bash -$ compiler-admin time --help -Usage: compiler-admin time [OPTIONS] COMMAND [ARGS]... - - Work with Compiler time entries. - -Options: - --help Show this message and exit. - -Commands: - convert Convert a time report from one format into another. - download Download a Toggl time report in CSV format. - lock Lock Toggl time entries. - verify Verify time entry CSV files. -``` - -### Locking time entries - -Use this command to lock Toggl time entries up to some date (defaulting to the last day of the prior month). - -```bash -$ compiler-admin time lock --help -Usage: compiler-admin time lock [OPTIONS] - - Lock Toggl time entries. - -Options: - --date TEXT The date to lock time entries, formatted as YYYY-MM-DD. - Defaults to the last day of the previous month. - --help Show this message and exit. -``` - -### Downloading a Toggl report - -Use this command to download a time report from Toggl in CSV format (defaulting to the prior month): - -```bash -$ compiler-admin time download --help -Usage: compiler-admin time download [OPTIONS] - - Download a Toggl time report in CSV format. - -Options: - --start YYYY-MM-DD The start date of the reporting period. Defaults - to the beginning of the prior month. - --end YYYY-MM-DD The end date of the reporting period. Defaults to - the end of the prior month. - --output TEXT The path to the file where downloaded data should - be written. Defaults to a path calculated from the - date range. - --all Download all time entries. The default is to - download only billable time entries. - -c, --client CLIENT_ID An ID for a Toggl Client to filter for in reports. - Can be supplied more than once. - -p, --project PROJECT_ID An ID for a Toggl Project to filter for in - reports. Can be supplied more than once. - -t, --task TASK_ID An ID for a Toggl Project Task to filter for in - reports. Can be supplied more than once. - -u, --user USER_ID An ID for a Toggl User to filter for in reports. - Can be supplied more than once. - --help Show this message and exit. -``` - -### Converting an hours report - -With a CSV exported from either Harvest or Toggl, use this command to convert to the opposite format: - -```bash -$ compiler-admin time convert --help -Usage: compiler-admin time convert [OPTIONS] - - Convert a time report from one format into another. - -Options: - --input TEXT The path to the source data for conversion. - Defaults to $TOGGL_DATA or stdin. - --output TEXT The path to the file where converted data - should be written. Defaults to $HARVEST_DATA - or stdout. - --from [harvest|toggl] The format of the source data. [default: - toggl] - --to [harvest|justworks|toggl] The format of the converted data. [default: - harvest] - --client TEXT The name of the client to use in converted - data. - --help Show this message and exit. -``` - -### Verifying an hours conversion - -```bash -$ compiler-admin time verify --help -Usage: compiler-admin time verify [OPTIONS] [FILES]... - - Verify time entry CSV files. - -Options: - --help Show this message and exit. -``` - -## Working with users - -The following commands are available to work with users in the Compiler domain: - -```bash -$ compiler-admin user -h -usage: compiler-admin user [-h] {alumni,create,convert,delete,offboard,reset,restore,signout} ... - -positional arguments: - {alumni,create,convert,delete,offboard,reset,restore,signout} - The user command to run. - alumni Convert a user account to a Compiler alumni. - create Create a new user in the Compiler domain. - convert Convert a user account to a new type. - delete Delete a user account. - offboard Offboard a user account. - reset Reset a user's password to a randomly generated string. - restore Restore an email backup from a prior offboarding. - signout Signs a user out from all active sessions. - -options: - -h, --help show this help message and exit -``` - -### Creating a user - -```bash -$ compiler-admin user create -h -usage: compiler-admin user create [-h] [--notify NOTIFY] username - -positional arguments: - username A Compiler user account name, sans domain. - -options: - -h, --help show this help message and exit - --notify NOTIFY An email address to send the newly created account info. -``` - -Additional options are passed through to GAM, see more about [GAM user create](https://github.com/taers232c/GAMADV-XTD3/wiki/Users#create-a-user) - -### Convert a user - -```bash -$ compiler-admin user convert -h -usage: compiler-admin user convert [-h] [--force] [--notify NOTIFY] username {alumni,contractor,partner,staff} - -positional arguments: - username A Compiler user account name, sans domain. - {alumni,contractor,partner,staff} - Target account type for this conversion. - -options: - -h, --help show this help message and exit - --force Don't ask for confirmation before conversion. - --notify NOTIFY An email address to send the alumni's new password. -``` - -### Offboarding a user - -```bash -$ compiler-admin user offboard -h -usage: compiler-admin user offboard [-h] [--alias ALIAS] [--force] username - -positional arguments: - username A Compiler user account name, sans domain. - -options: - -h, --help show this help message and exit - --alias ALIAS Account to assign username as an alias. - --force Don't ask for confirmation before offboarding. -``` - -This script creates a local backup of `USER`'s inbox, see [Restore](#restore-an-email-backup) - -### Restore an email backup - -Retore a backup from a prior [Offboarding](#offboarding-a-user) into the `archive@compiler.la` account. - -```bash -$ compiler-admin user restore -h -usage: compiler-admin user restore [-h] username - -positional arguments: - username The user's account name, sans domain. - -options: - -h, --help show this help message and exit -``` diff --git a/compiler_admin/commands/init.py b/compiler_admin/commands/init.py index 5266d42..eb30d75 100644 --- a/compiler_admin/commands/init.py +++ b/compiler_admin/commands/init.py @@ -1,13 +1,12 @@ import os +import subprocess from pathlib import Path from shutil import rmtree -import subprocess import click from compiler_admin.services.google import USER_ARCHIVE, CallGAMCommand - GAM_CONFIG_DIR = os.environ.get("GAMCFGDIR", "./.config/gam") GAM_CONFIG_PATH = Path(GAM_CONFIG_DIR) GYB_CONFIG_PATH = GAM_CONFIG_PATH.parent / "gyb" @@ -31,8 +30,8 @@ def init(username: str, init_gam: bool = False, init_gyb: bool = False): See: - - https://github.com/taers232c/GAMADV-XTD3/wiki/How-to-Install-Advanced-GAM - - https://github.com/GAM-team/got-your-back/wiki + - [https://github.com/GAM-team/GAM/wiki/How-to-Install-GAM7](https://github.com/GAM-team/GAM/wiki/How-to-Install-GAM7) + - [https://github.com/GAM-team/got-your-back/wiki](https://github.com/GAM-team/got-your-back/wiki) """ if init_gam: _clean_config_dir(GAM_CONFIG_PATH) diff --git a/compiler_admin/commands/time/convert.py b/compiler_admin/commands/time/convert.py index 52e24b3..bcc183a 100644 --- a/compiler_admin/commands/time/convert.py +++ b/compiler_admin/commands/time/convert.py @@ -7,7 +7,6 @@ from compiler_admin.services.harvest import CONVERTERS as HARVEST_CONVERTERS from compiler_admin.services.toggl import CONVERTERS as TOGGL_CONVERTERS - CONVERTERS = {"harvest": HARVEST_CONVERTERS, "toggl": TOGGL_CONVERTERS} @@ -59,9 +58,7 @@ def convert( to_fmt="harvest", client="", ): - """ - Convert a time report from one format into another. - """ + """Convert a time report from one format into another.""" converter = _get_source_converter(from_fmt, to_fmt) click.echo(f"Converting data from format: {from_fmt} to format: {to_fmt}") diff --git a/compiler_admin/commands/time/download.py b/compiler_admin/commands/time/download.py index bee1c0f..d6e6a23 100644 --- a/compiler_admin/commands/time/download.py +++ b/compiler_admin/commands/time/download.py @@ -6,7 +6,6 @@ from compiler_admin.services.toggl import TOGGL_COLUMNS, download_time_entries - TZINFO = timezone("America/Los_Angeles") @@ -98,9 +97,7 @@ def download( task_ids: List[int] = [], user_ids: List[int] = [], ): - """ - Download a Toggl time report in CSV format. - """ + """Download a Toggl time report in CSV format.""" if not output: output = f"Toggl_time_entries_{start.strftime('%Y-%m-%d')}_{end.strftime('%Y-%m-%d')}.csv" diff --git a/compiler_admin/commands/time/verify.py b/compiler_admin/commands/time/verify.py index ba8ada7..127ebce 100644 --- a/compiler_admin/commands/time/verify.py +++ b/compiler_admin/commands/time/verify.py @@ -1,6 +1,7 @@ -import click import sys +import click + from compiler_admin.services import harvest, toggl from compiler_admin.services.time import TimeSummary from compiler_admin.services.toggl import normalize_summary @@ -40,10 +41,28 @@ def _diff_summaries(summary1: TimeSummary, summary2: TimeSummary): diffs.append(f"Total rows: {summary1.total_rows} vs {summary2.total_rows}") if summary1.total_hours != summary2.total_hours: diffs.append(f"Total hours: {summary1.total_hours} vs {summary2.total_hours}") + # Detailed diff for hours_per_project if summary1.hours_per_project != summary2.hours_per_project: - diffs.append("Hours per project differ.") + all_projects = sorted(set(summary1.hours_per_project.keys()) | set(summary2.hours_per_project.keys())) + for project in all_projects: + hours1 = summary1.hours_per_project.get(project, 0.0) + hours2 = summary2.hours_per_project.get(project, 0.0) + if hours1 != hours2: + diffs.append(f" Project '{project}' hours: {hours1} vs {hours2}") + + # Detailed diff for hours_per_user_project if summary1.hours_per_user_project != summary2.hours_per_user_project: - diffs.append("Hours per user/project differ.") + all_users = sorted(set(summary1.hours_per_user_project.keys()) | set(summary2.hours_per_user_project.keys())) + for user in all_users: + projects1 = summary1.hours_per_user_project.get(user, {}) + projects2 = summary2.hours_per_user_project.get(user, {}) + all_user_projects = sorted(set(projects1.keys()) | set(projects2.keys())) + for project in all_user_projects: + hours1 = projects1.get(project, 0.0) + hours2 = projects2.get(project, 0.0) + if hours1 != hours2: + diffs.append(f" User '{user}', Project '{project}' hours: {hours1} vs {hours2}") + return diffs diff --git a/compiler_admin/commands/user/create.py b/compiler_admin/commands/user/create.py index 06e0dfa..ea0bfe0 100644 --- a/compiler_admin/commands/user/create.py +++ b/compiler_admin/commands/user/create.py @@ -22,7 +22,7 @@ def create(username: str, notify: str = "", gam_args: list = []): Extra args are passed along to GAM as options. - https://github.com/taers232c/GAMADV-XTD3/wiki/Users#create-a-user + https://github.com/GAM-team/GAM/wiki/Users#create-a-user """ account = user_account_name(username) diff --git a/compiler_admin/main.py b/compiler_admin/main.py index 86a4340..ecea530 100644 --- a/compiler_admin/main.py +++ b/compiler_admin/main.py @@ -1,7 +1,6 @@ import click from compiler_admin import __version__ - from compiler_admin.commands.info import info from compiler_admin.commands.init import init from compiler_admin.commands.time import time diff --git a/compose.yaml b/compose.yaml index cffc641..335713c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -3,7 +3,7 @@ services: build: context: . dockerfile: .devcontainer/Dockerfile - image: compiler_admin:dev + image: compiler/admin:dev env_file: - .env entrypoint: sleep infinity @@ -11,3 +11,15 @@ services: - .:/home/compiler/compiler-admin - ~/.config/compiler-admin:/home/compiler/.config/compiler-admin - ./.downloads:/home/compiler/Downloads + + docs: + build: + context: . + dockerfile: .devcontainer/Dockerfile + image: compiler/admin:dev + entrypoint: mkdocs + command: serve --dev-addr "0.0.0.0:8000" + ports: + - "8000" + volumes: + - ./:/home/compiler/compiler-admin diff --git a/docs/explanation/architecture.md b/docs/explanation/architecture.md new file mode 100644 index 0000000..5dba8bc --- /dev/null +++ b/docs/explanation/architecture.md @@ -0,0 +1,38 @@ +# Architecture Overview + +This document provides a high-level overview of the `compiler-admin` codebase structure, intended for developers who may want to contribute to the project. + +The project is organized into three main layers: + +``` +compiler_admin/ +├── api/ +├── commands/ +└── services/ +``` + +## `commands/` + +This directory contains the definition of the command-line interface. The project uses the [Click](https://click.palletsprojects.com/) library to build the CLI. + +- Each subcommand (e.g., `info`, `init`, `time`, `user`) has its own module. +- For nested commands like `time` and `user`, the `__init__.py` file in the respective directory acts as a command group that aggregates the subcommands from the other modules in that directory. +- These modules are responsible for parsing CLI arguments and options. They should contain minimal business logic, instead calling functions in the `services/` layer to perform the actual work. + +## `services/` + +This directory contains the core business logic of the application. + +- Each module in this directory is responsible for a specific domain of functionality (e.g., `google.py` for Google Workspace interactions, `toggl.py` for Toggl-related logic, `harvest.py` for Harvest-related logic). +- These services are where the interactions with external tools (like `gam` and `gyb`) and APIs happen. +- Functions in this layer are called by the `commands/` layer and are designed to be reusable. + +## `api/` + +This directory contains low-level clients for interacting with third-party HTTP APIs. + +- For example, `api/toggl.py` contains a `Toggl` class that is a direct client for the Toggl Track REST API. +- This layer is responsible for handling the details of HTTP requests, authentication, and response handling. +- The `services/` layer uses these API clients to implement its logic. For instance, `services/toggl.py` might use the `api.toggl.Toggl` client to download a report, and then perform data processing on the result. + +This separation of concerns makes the application easier to understand, maintain, and test. diff --git a/docs/explanation/concepts.md b/docs/explanation/concepts.md new file mode 100644 index 0000000..86598c7 --- /dev/null +++ b/docs/explanation/concepts.md @@ -0,0 +1,37 @@ +# Core Concepts + +This section explains some of the foundational concepts and external tools that `compiler-admin` is built upon. + +## External Tools: GAM and GYB + +The `compiler-admin` tool is fundamentally a wrapper that simplifies and automates common administrative workflows by building on two powerful, open-source command-line tools for Google Workspace administration: + +- **[GAM (Google Apps Manager)](https://github.com/GAM-team/GAM):** GAM7 provides comprehensive control over nearly every aspect of a Google Workspace account, from managing users, groups, and OUs to controlling service settings and Drive permissions. `compiler-admin` uses GAM7 for all user and group management tasks like creating users, changing their OU, and resetting passwords. + +- **[GYB (Got Your Back)](https://github.com/GAM-team/got-your-back):** GYB is a specialized tool for backing up and restoring Gmail mailboxes. `compiler-admin` uses GYB during the user offboarding process to create a complete, local backup of a user's inbox before the account is deactivated or deleted. + +By using these tools, `compiler-admin` avoids reinventing the wheel and can focus on providing a streamlined interface for Compiler's specific administrative workflows. + +## User Account Types + +Compiler organizes its Google Workspace users into several types, which correspond to a combination of Organizational Units (OUs) and Google Groups. This structure determines a user's access and permissions. + +- **Staff (`OU: staff`, `Group: staff@, team@`)**: Full-time employees. +- **Partners (`OU: staff/partners`, `Group: partners@, staff@, team@`)**: Company partners, who are a subset of staff but with additional permissions. +- **Contractors (`OU: contractors`, `Group: team@`)**: External contractors with more limited access than staff. +- **Alumni (`OU: alumni`)**: Deactivated accounts of former employees. They have no group memberships and cannot log in. + +The `user convert`, `user deactivate`, and `user reactivate` commands are the primary tools for moving users between these types. + +## Time Reporting Workflow + +The company uses multiple systems for time tracking and invoicing, which necessitates converting time reports between different formats. + +1. **Toggl Track**: This is the primary system where all team members track their time. +2. **Harvest / Justworks**: These are secondary systems used for invoicing, payroll, or project management. + +The `time` commands in `compiler-admin` facilitate this workflow: +- `time download`: Exports detailed time entries from Toggl. +- `time convert`: Converts the Toggl CSV format into a format compatible with Harvest or Justworks. +- `time verify`: Provides a way to check that the data remains consistent after conversion. +- `time lock`: Locks the entries in Toggl after the data has been exported and processed, ensuring the integrity of the invoiced records. diff --git a/docs/guides/time/convert.md b/docs/guides/time/convert.md new file mode 100644 index 0000000..3e166fe --- /dev/null +++ b/docs/guides/time/convert.md @@ -0,0 +1,70 @@ +# How to Convert Time Reports + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin time convert` reference section](../../reference/cli/time.md#compiler-admin-time-convert). + +This guide explains how to use the `compiler-admin time convert` command to convert a time report from one format (like Toggl) to another (like Harvest or Justworks). + +## Basic Usage + +The `convert` command reads from an input source, which defaults to `toggl` format, and writes to an output source, which defaults to `harvest` format. + +The simplest usage reads from standard input and writes to standard output: + +```bash +cat toggl-report.csv | compiler-admin time convert > harvest-report.csv +``` + +## Specifying Input and Output Files + +For clarity, it's often better to use the `--input` and `--output` flags. + +```bash +compiler-admin time convert --input toggl-report.csv --output harvest-report.csv +``` + +## Specifying Conversion Formats + +You can explicitly define the source and destination formats using the `--from` and `--to` flags. + +### Convert from Toggl to Harvest + +```bash +compiler-admin time convert \ + --from toggl \ + --to harvest \ + --input toggl-export.csv \ + --output harvest-import.csv +``` + +### Convert from Harvest to Toggl + +```bash +compiler-admin time convert \ + --from harvest \ + --to toggl \ + --input harvest-export.csv \ + --output toggl-import.csv +``` + +### Convert from Toggl to Justworks + +```bash +compiler-admin time convert \ + --from toggl \ + --to justworks \ + --input toggl-export.csv \ + --output justworks-import.csv +``` + +## Setting a Client Name + +When converting, you may need to specify a client name for the destination format. Use the `--client` option for this. + +```bash +compiler-admin time convert \ + --input toggl.csv \ + --output harvest.csv \ + --client "Specific Client Name" +``` diff --git a/docs/guides/time/download.md b/docs/guides/time/download.md new file mode 100644 index 0000000..be28560 --- /dev/null +++ b/docs/guides/time/download.md @@ -0,0 +1,53 @@ +# How to Download a Toggl Time Report + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin time download` reference section](../../reference/cli/time.md#compiler-admin-time-download). + +This guide explains how to use the `compiler-admin time download` command to download a detailed time report from Toggl in CSV format. + +## Basic Usage + +To download a report for the previous calendar month (the default behavior): + +```bash +compiler-admin time download +``` + +This will create a CSV file in the current directory named `Toggl_time_entries_[start-date]_[end-date].csv`. + +## Specifying a Date Range + +You can specify a custom date range using the `--start` and `--end` options. The date format is `YYYY-MM-DD`. + +```bash +compiler-admin time download --start 2025-01-01 --end 2025-01-31 +``` + +## Specifying an Output File + +To save the report to a specific file path, use the `--output` option. + +```bash +compiler-admin time download --output /path/to/my-report.csv +``` + +## Filtering the Report + +The command provides several options for filtering the time entries included in the report: + +- `--all`: Include all time entries, not just billable ones. +- `-c, --client CLIENT_ID`: Filter by a specific Toggl client ID. +- `-p, --project PROJECT_ID`: Filter by a specific Toggl project ID. +- `-t, --task TASK_ID`: Filter by a specific Toggl task ID. +- `-u, --user USER_ID`: Filter by a specific Toggl user ID. + +You can use these options multiple times to include multiple IDs. + +### Example + +To download all billable time entries for Project ID `12345` and `67890` for the prior month: + +```bash +compiler-admin time download -p 12345 -p 67890 +``` diff --git a/docs/guides/time/lock.md b/docs/guides/time/lock.md new file mode 100644 index 0000000..be4c940 --- /dev/null +++ b/docs/guides/time/lock.md @@ -0,0 +1,31 @@ +# How to Lock Time Entries in Toggl + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin time lock` reference section](../../reference/cli/time.md#compiler-admin-time-lock). + +This guide explains how to use the `compiler-admin time lock` command to lock time entries in Toggl, preventing them from being edited. + +This is typically the first step before downloading and preparing time reports. + +You can see the current lock date at . + +## Basic Usage + +To lock time entries up to the last day of the previous calendar month (the default behavior), run the command without any options: + +```bash +compiler-admin time lock +``` + +The command will print the date it is locking entries up to and ask for confirmation before proceeding. + +## Specifying a Lock Date + +You can lock entries up to any specific date using the `--date` option. The date format is `YYYY-MM-DD`. + +For example, to lock all entries on or before January 31, 2025: + +```bash +compiler-admin time lock --date 2025-01-31 +``` diff --git a/docs/guides/time/run-github-workflow.md b/docs/guides/time/run-github-workflow.md new file mode 100644 index 0000000..c7bce3b --- /dev/null +++ b/docs/guides/time/run-github-workflow.md @@ -0,0 +1,48 @@ +# How to Run the Monthly Time Reporting GitHub Actions Workflow + +This guide explains how to manually trigger and monitor the `Monthly Time Reporting` GitHub Actions workflow, which automates the process of downloading, converting, verifying, and posting monthly time reports. + +## Overview + +The workflow performs the following automated steps: + +- **Lock Toggl time entries**: Locks time entries in Toggl up to the specified `end-date` (or end of prior month). +- **Download Toggl time entries**: Downloads a detailed CSV report from Toggl. +- **Convert Toggl entries to Harvest format**: Converts the downloaded report into a Harvest-compatible CSV. +- **Verify time entries**: Compares the downloaded and converted files to ensure data integrity, and generates a summary. +- **Post to Slack**: Sends the summary and the converted Harvest CSV to a designated Slack channel. + +This workflow ensures that monthly time reporting is consistent and automated. + +## Prerequisites + +To run this workflow, you must have: + +- Write access to the GitHub repository. +- The necessary GitHub Secrets configured in the repository settings. These secrets include API tokens and configuration for Toggl, Harvest, Google Workspace (GAM), and Slack. Without these, the workflow will fail. + +## Locating the Workflow + +The workflow definition file is located at: +[`./.github/workflows/time-reporting.yml`](https://github.com/compilerla/compiler-admin/blob/main/.github/workflows/time-reporting.yml) + +## Manually Triggering the Workflow + +To manually trigger the workflow: + +1. Navigate to the [**Actions**](https://github.com/compilerla/compiler-admin/actions) tab in the GitHub repository. +2. In the left sidebar, click on the **Monthly Time Reporting** workflow. +3. On the workflow page, click the **Run workflow** dropdown button. +4. Leave the `Branch: main` selection as-is. +5. You will see optional input fields for `start-date` and `end-date`. + - If left blank, the workflow will default to the previous calendar month. + - Enter dates in `YYYY-MM-DD` format if you need to process a specific period. +6. Click the green **Run workflow** button to start the execution. + +## Monitoring a Workflow Run + +After triggering, you will be redirected to the workflow run page (you may need to refresh). + +1. Click on the active workflow run to view its progress. +2. You can expand each job step (e.g., "Lock Toggl time entries", "Download Toggl time entries", "Verify time entries") to see its detailed output. +3. The final step, "Post to Slack", will send a summary of the time report to the configured Slack channel and upload the converted Harvest CSV file. diff --git a/docs/guides/time/verify.md b/docs/guides/time/verify.md new file mode 100644 index 0000000..6976888 --- /dev/null +++ b/docs/guides/time/verify.md @@ -0,0 +1,68 @@ +# How to Verify Time Reports + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin time verify` reference section](../../reference/cli/time.md#compiler-admin-time-verify). + +This guide explains how to use the `compiler-admin time verify` command to check the contents of time report CSV files. + +You can use this command in two ways: + +1. To get a summary of a single time report. +2. To compare two different time reports (e.g., a Toggl export and a converted Harvest file) to ensure they match. + +## Summarizing a Single Report + +To see a summary of a single CSV file, provide its path to the command: + +```bash +compiler-admin time verify toggl-report.csv +``` + +The output will show you a summary of the data in that file, including: + +- Date range +- Total number of entries +- Total hours +- Hours broken down by project +- Hours broken down by user and project + +```text +Summary for: toggl-report.csv + Date range: 2025-09-01 - 2025-09-30 + + Total entries: 150 + Total hours: 160.0 + Project A: 80.0 + Project B: 50.0 + Project C: 30.0 + + user1@example.com: + Project A: 40.0 + Project B: 20.0 + user2@example.com: + Project A: 40.0 + Project B: 30.0 + Project C: 30.0 +``` + +## Comparing Two Reports + +To verify that a conversion was successful, you can provide two file paths. The command will compare their summaries. + +The tool automatically detects the file format (Toggl or Harvest) and normalizes the data so that a meaningful comparison can be made. + +```bash +compiler-admin time verify toggl-report.csv harvest-report.csv +``` + +If the reports match, the command will output "Summaries match." and exit successfully. + +If there are differences in total hours, projects, or other details, the command will print a list of the discrepancies and exit with an error. + +```text +Summaries do not match: +- Total hours: 160.0 vs 155.0 + Project 'Project B' hours: 50.0 vs 45.0 + User 'user1@example.com', Project 'Project B' hours: 20.0 vs 15.0 +``` diff --git a/docs/guides/user/backupcodes.md b/docs/guides/user/backupcodes.md new file mode 100644 index 0000000..357ed52 --- /dev/null +++ b/docs/guides/user/backupcodes.md @@ -0,0 +1,25 @@ +# How to Get a User's 2FA Backup Codes + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user backupcodes` reference section](../../reference/cli/user.md#compiler-admin-user-backupcodes). + +This guide explains how to use the `compiler-admin user backupcodes` command to get a list of active 2-Step Verification backup codes for a user. + +This can be useful if a user has lost their primary 2FA device and any recovery options. + +## Basic Usage + +To get backup codes for a user, provide their `username`. + +```bash +compiler-admin user backupcodes some.user +``` + +The command will perform the following actions: + +1. Check if the user has existing backup codes. +2. If they do, it will print them. +3. If they do not, it will generate a new set of backup codes and print them. + +The output will be a list of the user's one-time-use backup codes. diff --git a/docs/guides/user/convert.md b/docs/guides/user/convert.md new file mode 100644 index 0000000..4165ff8 --- /dev/null +++ b/docs/guides/user/convert.md @@ -0,0 +1,44 @@ +# How to Convert a User's Account Type + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user convert` reference section](../../reference/cli/user.md#compiler-admin-user-convert). + +This guide explains how to use the `compiler-admin user convert` command to change a user's account type. + +This process moves the user between organizational units (OUs) and updates their group memberships (e.g., adding or removing them from the "Staff" or "Partners" groups). + +## Basic Usage + +To convert a user, you must provide their `username` and the target `account_type`. + +```bash +compiler-admin user convert some.user staff +``` + +This command would convert `some.user@compiler.la` to a "Staff" account. + +## Available Account Types + +The following target account types are available: + +- `staff`: For full-time staff members. +- `partner`: For partners of the company. +- `contractor`: For external contractors. +- `alumni`: For former employees. This is typically handled by the `offboard` command automatically. + +## Forcing the Conversion + +The command will ask for confirmation before proceeding. To bypass this, you can use the `--force` flag. + +```bash +compiler-admin user convert some.user contractor --force +``` + +## Notifying on Conversion to Alumni + +When converting a user to an `alumni` account, a new password is set. You can use the `--notify` option to send this new password to an email address. + +```bash +compiler-admin user convert some.user alumni --notify their.personal@email.com +``` diff --git a/docs/guides/user/create.md b/docs/guides/user/create.md new file mode 100644 index 0000000..82b191a --- /dev/null +++ b/docs/guides/user/create.md @@ -0,0 +1,37 @@ +# How to Create a New User + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user create` reference section](../../reference/cli/user.md#compiler-admin-user-create). + +This guide explains how to use the `compiler-admin user create` command to create a new user account in the Compiler Google Workspace. + +## Basic Usage + +To create a new user, you must provide a `username` (the part of the email address before `@compiler.la`). + +```bash +compiler-admin user create new.user +``` + +This creates the user `new.user@compiler.la`. The command generates a random temporary password and requires the user to change it on their first login. + +## Notifying a Manager + +You can send the new user's credentials to a manager or their personal email address using the `--notify` option. + +```bash +compiler-admin user create new.user --notify manager@compiler.la +``` + +## Passing Additional Options to GAM + +The `compiler-admin user create` command is a wrapper around the powerful `gam create user` command. You can pass additional arguments directly to GAM to specify more details about the user, such as their first and last name. + +For example, to create a user and set their name: + +```bash +compiler-admin user create new.user firstname "New" lastname "User" +``` + +For a full list of the available options you can pass through to GAM, please refer to the [GAM7 documentation on creating users](https://github.com/GAM-team/GAM/wiki/Users#create-a-user). diff --git a/docs/guides/user/deactivate.md b/docs/guides/user/deactivate.md new file mode 100644 index 0000000..8364eb9 --- /dev/null +++ b/docs/guides/user/deactivate.md @@ -0,0 +1,45 @@ +# How to Deactivate a User + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user deactivate` reference section](../../reference/cli/user.md#compiler-admin-user-deactivate). + +This guide explains how to use the `compiler-admin user deactivate` command. + +This command is a major step in the offboarding process. It secures an account by revoking access, clearing personal information, and moving the user to the "Alumni" organizational unit (OU). This command is automatically called as part of the more comprehensive `user offboard` command. For most offboarding scenarios, you should use `user offboard` instead. + +## Actions Performed + +The `deactivate` command performs the following actions: + +- Removes the user from all of their groups. +- Moves the user to the "Alumni" OU. +- Resets their password to a random string. +- Signs the user out of all active sessions. +- Clears profile information (address, location, phone number, secondary email). +- Resets their recovery email and phone number. +- Turns off 2-Step Verification on their account. + +## Basic Usage + +To deactivate a user, provide their `username`. + +```bash +compiler-admin user deactivate some.user +``` + +The command will ask for confirmation before proceeding. To bypass this, use the `--force` flag. + +## Setting Recovery Information + +You can set a new recovery email or phone number for the deactivated account using the `--recovery-email` and `--recovery-phone` options. + +```bash +compiler-admin user deactivate some.user --recovery-email personal@email.com +``` + +To clear the recovery information, pass an empty string (which is the default): + +```bash +compiler-admin user deactivate some.user --recovery-email "" --recovery-phone "" +``` diff --git a/docs/guides/user/delete.md b/docs/guides/user/delete.md new file mode 100644 index 0000000..d1da94e --- /dev/null +++ b/docs/guides/user/delete.md @@ -0,0 +1,31 @@ +# How to Delete a User + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user delete` reference section](../../reference/cli/user.md#compiler-admin-user-delete). + +This guide explains how to use the `compiler-admin user delete` command to permanently delete a user account from the Compiler Google Workspace. + +**Warning:** This action is irreversible. All of the user's data that has not been transferred or backed up will be permanently lost. For a safer, more comprehensive process, use the `user offboard` command. The `offboard` command can also delete the user as its final step. + +## Basic Usage + +To delete a user, provide their `username`. + +```bash +compiler-admin user delete some.user +``` + +The command will ask for confirmation before proceeding. + +## Forcing the Deletion + +To bypass the confirmation prompt, use the `--force` flag. + +```bash +compiler-admin user delete some.user --force +``` + +## Important Safeguard + +This command will fail if the user's email address has been assigned as an alias to another account. This is a safety measure to prevent accidental deletion of an active email alias. If you intend to delete the user, you must first remove the alias from the other account. diff --git a/docs/guides/user/offboard.md b/docs/guides/user/offboard.md new file mode 100644 index 0000000..9d43189 --- /dev/null +++ b/docs/guides/user/offboard.md @@ -0,0 +1,46 @@ +# How to Offboard a User + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user offboard` reference section](../../reference/cli/user.md#compiler-admin-user-offboard). + +This guide explains how to use the `compiler-admin user offboard` command to completely and securely offboard a user from the Compiler Google Workspace. + +This is a comprehensive command that orchestrates several other actions. For most offboarding scenarios, this is the command you should use. + +## Actions Performed + +The `offboard` command performs the following sequence of actions: + +1. **Deactivates the user account**: It runs all the steps from the `user deactivate` command (moves to Alumni OU, resets password, signs out, clears profile info, etc.). +2. **Backs up email**: It creates a full backup of the user's Gmail inbox and stores it locally in a `GYB-GMail-Backup-user@compiler.la` directory. +3. **Transfers data**: It initiates a transfer of the user's Google Drive files and Google Calendar events to the `archive@compiler.la` account. +4. **Deprovisions access**: It deprovisions POP and IMAP access for the account. +5. **Sets an alias (optional)**: It can assign the user's email address as an alias to another account. +6. **Deletes the account (optional)**: It can permanently delete the user's account after all other steps are complete. + +## Basic Usage + +To offboard a user, provide their `username`. + +```bash +compiler-admin user offboard departing.user +``` + +The command will ask for confirmation before proceeding. To bypass this, use the `--force` flag. + +## Assigning an Alias + +To forward the user's future emails to a manager or a shared inbox, use the `--alias` option. + +```bash +compiler-admin user offboard departing.user --alias manager.user +``` + +## Deleting the Account + +If the account should be permanently deleted after the data is backed up and transferred, add the `--delete` flag. **Use this option with caution.** + +```bash +compiler-admin user offboard departing.user --delete +``` diff --git a/docs/guides/user/reactivate.md b/docs/guides/user/reactivate.md new file mode 100644 index 0000000..a4b1f8a --- /dev/null +++ b/docs/guides/user/reactivate.md @@ -0,0 +1,52 @@ +# How to Reactivate a User + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user reactivate` reference section](../../reference/cli/user.md#compiler-admin-user-reactivate). + +This guide explains how to use the `compiler-admin user reactivate` command to restore a previously deactivated user account. + +## Actions Performed + +The `reactivate` command performs the following actions: + +- Checks that the user is currently in the "Alumni" OU (i.e., deactivated). +- Adds the user back to the default "Team" group. +- Moves the user to either the "Staff" or "Contractors" OU. +- If moved to "Staff", adds them to the "Staff" group. +- Resets their password and requires them to change it on next login. +- Can update their recovery email and phone number. +- Generates a new set of 2-Step Verification backup codes and prints them to the console. + +## Basic Usage + +To reactivate a user, provide their `username`. By default, they are reactivated as a contractor. + +```bash +compiler-admin user reactivate some.user +``` + +The command will ask for confirmation before proceeding. To bypass this, use the `--force` flag. + +## Reactivating as Staff + +To reactivate a user as a full staff member, use the `--staff` flag. + +```bash +compiler-admin user reactivate some.user --staff +``` + +## Setting Recovery and Notification Info + +You can set the user's recovery information and notify them or a manager of the reactivation. + +- `--recovery-email`: Sets the user's recovery email address. +- `--recovery-phone`: Sets the user's recovery phone number. +- `--notify`: Sends the new password credentials to the specified email address. + +```bash +compiler-admin user reactivate some.user \ + --staff \ + --recovery-email some.user.personal@email.com \ + --notify manager@compiler.la +``` diff --git a/docs/guides/user/reset.md b/docs/guides/user/reset.md new file mode 100644 index 0000000..cec29dd --- /dev/null +++ b/docs/guides/user/reset.md @@ -0,0 +1,40 @@ +# How to Reset a User's Password + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user reset` reference section](../../reference/cli/user.md#compiler-admin-user-reset). + +This guide explains how to use the `compiler-admin user reset` command to reset a user's password and sign them out of all sessions. + +## Actions Performed + +The `reset` command performs two main actions: + +1. Generates a new, random password for the user and sets the requirement that they must change it on their next login. +2. Immediately signs the user out of all active Google sessions on all devices. + +## Basic Usage + +To reset a user's password, provide their `username`. + +```bash +compiler-admin user reset some.user +``` + +The command will ask for confirmation before proceeding. + +## Forcing the Reset + +To bypass the confirmation prompt, use the `--force` flag. + +```bash +compiler-admin user reset some.user --force +``` + +## Notifying the User or a Manager + +You can send the new temporary password to the user or their manager using the `--notify` option. + +```bash +compiler-admin user reset some.user --notify manager@compiler.la +``` diff --git a/docs/guides/user/restore.md b/docs/guides/user/restore.md new file mode 100644 index 0000000..782320b --- /dev/null +++ b/docs/guides/user/restore.md @@ -0,0 +1,30 @@ +# How to Restore an Email Backup + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user restore` reference section](../../reference/cli/user.md#compiler-admin-user-restore). + +This guide explains how to use the `compiler-admin user restore` command to restore a user's Gmail backup into the central `archive@compiler.la` account. + +This command is used to access the emails of a user who was previously offboarded using the `user offboard` command, which creates a local backup directory. + +## Prerequisites + +Before running the restore command, you must have the user's local email backup directory. This directory is created by the `user offboard` command and is named in the format `GYB-GMail-Backup-username@compiler.la`. + +This command must be run from the same parent directory where the backup folder is located. + +## Basic Usage + +To restore a backup for a given `username`, run the following command: + +```bash +compiler-admin user restore departing.user +``` + +This command will: + +1. Locate the backup directory `GYB-GMail-Backup-departing.user@compiler.la`. +2. Connect to the `archive@compiler.la` mailbox. +3. Upload all emails from the backup directory into the archive mailbox. +4. Apply a new Gmail label to all restored emails with the user's original email address (`departing.user@compiler.la`) so they can be easily found and filtered within the archive account. diff --git a/docs/guides/user/signout.md b/docs/guides/user/signout.md new file mode 100644 index 0000000..60453e4 --- /dev/null +++ b/docs/guides/user/signout.md @@ -0,0 +1,29 @@ +# How to Sign a User Out of All Sessions + +!!! seealso "Full Command Reference" + + For a complete reference of all options, see the [`compiler-admin user signout` reference section](../../reference/cli/user.md#compiler-admin-user-signout). + +This guide explains how to use the `compiler-admin user signout` command to immediately invalidate a user's login sessions on all devices. + +This is useful in case a device is lost or stolen, or if an account is suspected of being compromised. This command is also called automatically as part of the `user reset` and `user deactivate` commands. + +## Basic Usage + +To sign a user out, provide their `username`. + +```bash +compiler-admin user signout some.user +``` + +The command will ask for confirmation before proceeding. + +## Forcing the Signout + +To bypass the confirmation prompt, use the `--force` flag. + +```bash +compiler-admin user signout some.user --force +``` + +This will immediately revoke all of the user's active Google sessions. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..010690d --- /dev/null +++ b/docs/index.md @@ -0,0 +1,26 @@ +# Compiler Admin + +Automating Compiler's administrative tasks. + +Built on top of [GAM7](https://github.com/GAM-team/GAM) and [GYB](https://github.com/GAM-team/got-your-back). + +**Note:** This tool can only be used by those with administrator access to Compiler's Google Workspace. + +## Basic Usage + +```bash +$ compiler-admin -h +usage: compiler-admin [-h] [-v] {info,init,time,user} ... + +positional arguments: + {info,init,time,user} + The command to run + info Print configuration and debugging information. + init Initialize a new admin project. This command should be run once before any others. + time Work with Compiler time entries. + user Work with users in the Compiler org. + +options: + -h, --help show this help message and exit + -v, --version show program's version number and exit +``` diff --git a/docs/reference/cli/info.md b/docs/reference/cli/info.md new file mode 100644 index 0000000..446e8e5 --- /dev/null +++ b/docs/reference/cli/info.md @@ -0,0 +1,8 @@ +# `compiler-admin info` + + +::: mkdocs-click + :module: compiler_admin.main + :command: info + :depth: 1 + :prog_name: compiler-admin info diff --git a/docs/reference/cli/init.md b/docs/reference/cli/init.md new file mode 100644 index 0000000..73354c4 --- /dev/null +++ b/docs/reference/cli/init.md @@ -0,0 +1,8 @@ +# `compiler-admin init` + + +::: mkdocs-click + :module: compiler_admin.main + :command: init + :depth: 1 + :prog_name: compiler-admin init diff --git a/docs/reference/cli/time.md b/docs/reference/cli/time.md new file mode 100644 index 0000000..8a1cb87 --- /dev/null +++ b/docs/reference/cli/time.md @@ -0,0 +1,9 @@ +# `compiler-admin time` + + +::: mkdocs-click + :module: compiler_admin.main + :command: time + :depth: 1 + :prog_name: compiler-admin time + :list_subcommands: True diff --git a/docs/reference/cli/user.md b/docs/reference/cli/user.md new file mode 100644 index 0000000..a83aa2e --- /dev/null +++ b/docs/reference/cli/user.md @@ -0,0 +1,9 @@ +# `compiler-admin user` + + +::: mkdocs-click + :module: compiler_admin.main + :command: user + :depth: 1 + :prog_name: compiler-admin user + :list_subcommands: True diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md new file mode 100644 index 0000000..7e0f382 --- /dev/null +++ b/docs/reference/configuration.md @@ -0,0 +1,32 @@ +# Configuration + +The `compiler-admin` tool can be configured using the following environment variables. + +## Toggl Configuration + +| Variable | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------ | +| `TOGGL_API_TOKEN` | **Required.** Your API token for the Toggl Track API. | +| `TOGGL_WORKSPACE_ID` | **Required.** The ID of your Toggl workspace. | +| `TOGGL_CLIENT_NAME` | The name of the client to use when converting reports from Harvest to Toggl format. | +| `TOGGL_PROJECT_INFO` | Path to a JSON file used to cache Toggl project information. This helps map Toggl projects to other systems. | +| `TOGGL_USER_INFO` | Path to a JSON file used to cache Toggl user information. | + +## Harvest Configuration + +| Variable | Description | +| --------------------- | ----------------------------------------------------------------------------------- | +| `HARVEST_CLIENT_NAME` | The name of the client to use when converting reports from Toggl to Harvest format. | + +## Google Workspace (GAM/GYB) Configuration + +| Variable | Description | +| ----------- | ------------------------------------------------------------------------------------- | +| `GAMCFGDIR` | The directory where GAM7 stores its configuration files. Defaults to `./.config/gam`. | + +## Data Conversion + +| Variable | Description | +| -------------- | ------------------------------------------------------------------------------------------------------ | +| `TOGGL_DATA` | The default input path for the `time convert` command when converting from Toggl. Defaults to stdin. | +| `HARVEST_DATA` | The default output path for the `time convert` command when converting to Harvest. Defaults to stdout. | diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..9810306 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,15 @@ +# Temporary local pin to work around bug in click>=8.2.2 that breaks watching. +# See: https://github.com/squidfunk/mkdocs-material/issues/8478 +# https://github.com/mkdocs/mkdocs/issues/4032 +# https://github.com/pallets/click/issues/3084 +# Remove when a fixed version of Click has been released +click==8.2.1 +mdx_truly_sane_lists +mkdocs==1.6.1 +mkdocs-awesome-pages-plugin +mkdocs-click +mkdocs-macros-plugin +mkdocs-material==9.6.21 +mkdocs-redirects +mkdocstrings +mkdocstrings-python diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md new file mode 100644 index 0000000..799b4eb --- /dev/null +++ b/docs/tutorials/getting-started.md @@ -0,0 +1,40 @@ +# Getting Started + +This tutorial will guide you through the initial setup of the `compiler-admin` tool, including cloning the repository and initializing the necessary configurations for Google Workspace access. + +## 1. Clone the Repository + +First, clone the `compiler-admin` repository to your local machine and create the configuration directory it uses. + +```bash +mkdir -p ~/.config/compiler-admin +git clone https://github.com/compilerla/compiler-admin.git +cd compiler-admin +``` + +## 2. Open in Development Container + +This project is configured to use a VS Code Development Container. Open the cloned repository in VS Code. You should be prompted to "Reopen in Container". Click that button to build and open the development environment. + +This ensures you have all the required dependencies, like GAM7 and GYB, installed and ready to go. + +## 3. Initialize the Project + +Before you can use `compiler-admin` to manage Google Workspace, you need to authorize it to use the necessary Google APIs. This is done using the `init` command. + +This command will set up a Google Cloud Platform (GCP) project with the required APIs enabled and create the necessary OAuth credentials for both GAM (for user/group management) and GYB (for Gmail backups). + +To run the initialization, you need to provide your Compiler Google account username (the part before `@compiler.la`). + +```bash +compiler-admin init --gam --gyb your_username +``` + +Follow the prompts from GAM and GYB. You will be asked to go through a web-based OAuth flow to grant permissions. + +For more detailed information on the underlying tools, you can refer to their documentation: + +- [GAM7 Wiki](https://github.com/GAM-team/GAM/wiki/#requirements) +- [GYB Wiki](https://github.com/GAM-team/got-your-back/wiki) + +Once this is complete, your `compiler-admin` tool is ready to use. diff --git a/docs/tutorials/monthly-time-reporting.md b/docs/tutorials/monthly-time-reporting.md new file mode 100644 index 0000000..12d06c7 --- /dev/null +++ b/docs/tutorials/monthly-time-reporting.md @@ -0,0 +1,59 @@ +# Monthly Time Reporting + +This tutorial walks you through the complete workflow for processing monthly time reports, a common administrative task at Compiler. The process involves downloading time entries from Toggl, converting them to the format needed for other systems, verifying the data, and finally locking the entries in Toggl to prevent further changes. + +## 1. Lock Time Entries in Toggl + +Once you have verified the data and are confident the reports are accurate, the final step is to lock the time entries in Toggl. This prevents any further modifications to the time period you have just processed. + +The `time lock` command locks entries up to a specific date. By default, it locks entries up to the last day of the previous month, which is exactly what we want in this scenario. + +```bash +compiler-admin time lock +``` + +## 2. Download the Toggl Time Report + +The first step is to download the detailed time entries from Toggl. The `time download` command handles this. By default, it downloads all billable time entries for the previous calendar month. + +```bash +compiler-admin time download +``` + +This will create a CSV file in your current directory with a name like `Toggl_time_entries_YYYY-MM-DD_YYYY-MM-DD.csv`. + +You can customize the date range and other filters. For a full list of options, run: + +```bash +compiler-admin time download --help +``` + +## 3. Convert the Report Format + +Next, you may need to convert the downloaded Toggl report into another format, such as the one used by Harvest. The `time convert` command is used for this. + +It can read from a file or standard input and write to a file or standard output. Let's use the file we just downloaded as input and create a new file for the Harvest-formatted data. + +```bash +# Let's assume the downloaded file is named +# toggl_report_2025-09.csv +compiler-admin time convert \ + --input toggl_report_2025-09.csv \ + --output harvest_report_2025-09.csv +``` + +This creates a new file, `harvest_report_2025-09.csv`, with the data correctly formatted for Harvest. + +## 4. Verify the Conversion + +Before proceeding, it's a good practice to verify that the data was converted correctly and that the totals match. The `time verify` command compares two time entry files. + +```bash +compiler-admin time verify \ + Toggl_time_entries_2025-09-01_2025-09-30.csv \ + harvest_report_2025-09.csv +``` + +If the summaries of the two files match (total hours, hours per project, etc.), the command will exit silently with a "Summaries match." message. If there are discrepancies, it will print them. + +After completing these steps, you have successfully downloaded, converted, verified, and finalized the time entries for the month. diff --git a/docs/tutorials/offboarding-a-user.md b/docs/tutorials/offboarding-a-user.md new file mode 100644 index 0000000..a85572a --- /dev/null +++ b/docs/tutorials/offboarding-a-user.md @@ -0,0 +1,53 @@ +# Offboarding a User + +This tutorial covers the process of securely offboarding a user from the Compiler Google Workspace. The `compiler-admin` tool automates several critical steps to ensure a smooth and secure transition. + +## 1. Offboard the User + +The `user offboard` command is a comprehensive script that handles the main steps of offboarding: + +- Deactivates the user's account and moves them to the "Alumni" organizational unit (OU). +- Resets their password and signs them out of all active sessions. +- Removes them from all Google Groups. +- Backs up their entire Gmail inbox to a local directory on the machine running the tool. +- Initiates the transfer of their Google Drive and Calendar data to the `archive@compiler.la` user. +- Deprovisions POP/IMAP access. + +To run the command, you simply need to provide the user's `username`. + +```bash +compiler-admin user offboard departing_username +``` + +### Assigning an Alias + +It's common practice to forward the departing user's email to a manager or a general-purpose inbox. You can do this by assigning their email address as an alias to another account using the `--alias` option. + +```bash +compiler-admin user offboard departing_username --alias manager_username +``` + +### Deleting the Account + +By default, the user's account is deactivated but not deleted. If you need to permanently delete the account after the offboarding process is complete, you can add the `--delete` flag. + +```bash +# Use with caution! +compiler-admin user offboard departing_username --delete +``` + +The command will ask for confirmation before proceeding unless you also add the `--force` flag. + +## 2. Restoring an Email Backup + +The offboarding process creates a local backup of the user's Gmail inbox in a directory named `GYB-GMail-Backup-user@compiler.la`. + +If you ever need to access this backup, you can restore it to the central `archive@compiler.la` account. The `user restore` command handles this. It will upload the emails from the backup directory and apply a label with the user's original email address, so they are easy to find. + +To restore a backup for `departing_username`: + +```bash +compiler-admin user restore departing_username +``` + +This completes the offboarding and data archival process. diff --git a/docs/tutorials/onboarding-a-user.md b/docs/tutorials/onboarding-a-user.md new file mode 100644 index 0000000..a88e9b7 --- /dev/null +++ b/docs/tutorials/onboarding-a-user.md @@ -0,0 +1,37 @@ +# Onboarding a New User + +This tutorial demonstrates how to onboard a new user into the Compiler Google Workspace. The process involves creating their account and then assigning them the correct account type, such as "Staff" or "Contractor". + +## 1. Create the User Account + +The first step is to create the basic user account with the `user create` command. You need to provide a `username` for the new account (the part before `@compiler.la`). + +The command will generate a random password and require the user to change it upon their first login. + +You can also specify an email address with the `--notify` option to send the new account credentials to a manager or the user's personal email. + +```bash +compiler-admin user create new_username --notify manager@compiler.la +``` + +This command creates the user, adds them to the default "team" group, and sends their temporary password to `manager@compiler.la`. + +## 2. Set the Account Type + +By default, new users may be placed in a default organizational unit (OU). The next step is to assign them to the correct one, which also manages their access and permissions. The `user convert` command is used for this. + +Let's say we want to make this new user a full-time staff member. + +```bash +compiler-admin user convert new_username staff +``` + +This command moves the user `new_username@compiler.la` into the "Staff" OU and adds them to the "staff" Google Group. + +The available account types are: + +- `staff` +- `partner` +- `contractor` + +After these two steps, the new user is fully onboarded and ready to go. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..81d1149 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,99 @@ +site_name: "compiler/compiler-admin: Documentation" +repo_url: https://github.com/compilerla/compiler-admin +edit_uri: edit/main/docs +site_url: https://docs.compiler.la/compiler-admin + +nav: + - Home: index.md + - Tutorials: + - Getting Started: tutorials/getting-started.md + - Monthly Time Reporting: tutorials/monthly-time-reporting.md + - Onboarding a User: tutorials/onboarding-a-user.md + - Offboarding a User: tutorials/offboarding-a-user.md + - How-to Guides: + - Time Management: + - Download a Toggl Report: guides/time/download.md + - Convert Time Reports: guides/time/convert.md + - Lock Time Entries: guides/time/lock.md + - Verify Time Reports: guides/time/verify.md + - Run GitHub Actions Workflow: guides/time/run-github-workflow.md + - User Management: + - Create a User: guides/user/create.md + - Convert User Account Type: guides/user/convert.md + - Deactivate a User: guides/user/deactivate.md + - Reactivate a User: guides/user/reactivate.md + - Offboard a User: guides/user/offboard.md + - Restore an Email Backup: guides/user/restore.md + - Reset a Password: guides/user/reset.md + - Get Backup Codes: guides/user/backupcodes.md + - Sign Out a User: guides/user/signout.md + - Delete a User: guides/user/delete.md + - Reference: + - Configuration: reference/configuration.md + - CLI Reference: + - init: reference/cli/init.md + - info: reference/cli/info.md + - time: reference/cli/time.md + - user: reference/cli/user.md + - Explanation: + - Core Concepts: explanation/concepts.md + - Architecture: explanation/architecture.md + +theme: + name: material + features: + - content.code.copy + - navigation.expand + - navigation.tabs + - toc.integrate + palette: + scheme: default + +extra: + analytics: + provider: google + property: G-VR1QBF67SK + +plugins: + - search + - awesome-pages + - mkdocstrings: + handlers: + python: + options: + show_root_heading: true + show_root_toc_entry: false + show_symbol_type_heading: true + show_symbol_type_toc: true + - redirects: + redirect_maps: + +extra_css: + - https://use.fontawesome.com/releases/v6.1.2/css/all.css + +markdown_extensions: + - admonition + - attr_list + - codehilite: + linenums: true + - mdx_truly_sane_lists + - meta + - mkdocs-click + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.inlinehilite + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tabbed + - smarty + - toc: + # insert a blank space before the character + permalink: " ¶" diff --git a/tests/commands/time/test_verify.py b/tests/commands/time/test_verify.py index 800c63f..60b04a5 100644 --- a/tests/commands/time/test_verify.py +++ b/tests/commands/time/test_verify.py @@ -28,6 +28,29 @@ def test_verify_two_files(cli_runner, harvest_file, toggl_file): assert "Summaries match." in result.output +def test_verify_two_files_mismatched_details(cli_runner, tmp_path): + """Test that verify shows detailed differences for mismatched files.""" + file1_content = """Date,Client,Project,Notes,Hours,First name,Last name +2025-01-01,ClientA,ProjectX,Note1,8.0,John,Doe +2025-01-01,ClientA,ProjectY,Note2,4.0,John,Doe +""" + file2_content = """Date,Client,Project,Notes,Hours,First name,Last name +2025-01-01,ClientA,ProjectX,Note1,8.0,John,Doe +2025-01-01,ClientA,ProjectY,Note2,5.0,John,Doe +""" + file1 = tmp_path / "file1.csv" + file2 = tmp_path / "file2.csv" + file1.write_text(file1_content) + file2.write_text(file2_content) + + result = cli_runner.invoke(verify, [str(file1), str(file2)]) + assert result.exit_code == 1 + assert "Summaries do not match:" in result.output + assert "- Total hours: 12.0 vs 13.0" in result.output + assert " Project 'ProjectY' hours: 4.0 vs 5.0" in result.output + assert " User 'John Doe', Project 'ProjectY' hours: 4.0 vs 5.0" in result.output + + def test_verify_invalid_file_count(cli_runner, tmp_path): """Test that verify fails with an invalid number of files.""" result = cli_runner.invoke(verify, [])