Metadata-Version: 2.3
Name: pureskillgg-assistant-coach
Version: 4.0.2
Summary: Shared Python modules to assist coaching services.
Author: PureSkill.gg
Author-email: PureSkill.gg <secure@pureskill.gg>
License: SEE LICENSE IN LICENSE.txt
Requires-Dist: boto3>=1.43.0,<2.0.0
Requires-Dist: pybars3>=0.9.7,<0.10.0
Requires-Dist: python-rapidjson>=1.23.0,<2.0.0
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/pureskillgg/assistant-coach
Project-URL: Repository, https://github.com/pureskillgg/assistant-coach
Description-Content-Type: text/x-rst

PureSkill.gg Assistant Coach
============================

|PyPI| |GitHub Actions|

.. |PyPI| image:: https://img.shields.io/pypi/v/pureskillgg-assistant-coach.svg
   :target: https://pypi.python.org/pypi/pureskillgg-assistant-coach
   :alt: PyPI
.. |GitHub Actions| image:: https://github.com/pureskillgg/assistant-coach/workflows/main/badge.svg
   :target: https://github.com/pureskillgg/assistant-coach/actions
   :alt: GitHub Actions

Shared Python library that loads PureSkill.gg CS2 coaching content and renders it into localized, data-filled assessments for downstream coaching services.

What it does
------------

``assistant-coach`` (published to PyPI as ``pureskillgg-assistant-coach``) is a small, importable Python library. It reads the versioned coaching *content* authored elsewhere -- departments, assignments, breakdowns, resources, facets, and data-science models -- and turns it into the player-facing assessment objects (titles, bodies, breakdowns with substituted match data, and matched learning resources) that a coaching service returns to a user.

The single public surface is ``pureskillgg_assistant_coach.department``, which exports:

- ``Department`` -- the core model. Construct one for a named department (a coaching domain, e.g. ``dexter`` in the fixtures) with a reader, a structured logger, an injectable ``create_ds_models`` factory, and an injectable ``create_id`` (defaults to ``uuid4``, overridable for deterministic tests).
- ``DepartmentReaderS3`` -- the production reader; loads content JSON from S3.
- ``DepartmentReaderFs`` -- the filesystem reader; loads the same JSON from a local root (tests and local dev).
- ``create_breakdown`` and ``create_resource`` -- factory helpers for the rendered ``Breakdown`` and ``Resource`` objects.

Loading. ``Department.load()`` pulls the department's assessments and the global meta through its reader; ``Department.load_ds_models()`` lazily builds data-science models from the reader's ``models`` JSON via the injected ``create_ds_models`` factory. Each reader fetches five per-department documents (``assignments``, ``resources``, ``breakdowns``, ``facets``, ``models``) plus a global ``meta`` document. On read, ``attach_content_to_assignment`` denormalizes every assignment by inlining the breakdown, resource, and facet definitions it references, dropping any resource whose ``createdAt`` is in the future (``resource_is_current``).

Querying and rendering. Given an assignment name and match ``data``:

- ``get_title`` and ``get_body`` return ``Translation`` objects.
- ``get_breakdowns(name, data)`` validates that every requested breakdown label exists on the assignment (raising ``RuntimeError`` otherwise), then builds ``Breakdown`` objects with per-breakdown match data injected.
- ``get_resources`` and ``get_single_resource`` build ``Resource`` objects.
- ``get_facets`` returns the facet schema; ``is_assignment`` and ``get_ds_model`` round out the surface.

``Translation`` is the templating engine. It walks a nested dict of per-language strings (keyed ``en``, ``jp``, ``sp``, ...), compiles each leaf string as a `Handlebars <https://handlebarsjs.com/>`_ template (via ``pybars3``), and renders it with the stringified match data so coaching copy can interpolate values like round numbers. It registers a custom ``list`` helper that joins items into ``"a"``, ``"a & b"``, or ``"a, b & c"`` prose.

``Resource.tags_match`` implements tag-based resource selection via ``tag_matcher``: tags are grouped (AND across groups, OR within a group), ``~``-prefixed tags are negations, and a resource matches only when every group is satisfied -- returning the deduped, sorted matched tags. Every rendered breakdown and resource gets a fresh ``id`` from ``create_id``.

There is no CLI, server, or job runner here. It is purely an importable model and templating library, validated by ``pytest`` plus ``syrupy`` snapshot tests against ``fixtures/assessment``.

Pipeline role
-------------

This is a leaf-node helper library, not a deployable service: there are no Lambdas, no ``serverless.yml``, and no Terraform anywhere in the repo. It sits late in the pipeline, after a match has been parsed and analyzed.

- **Upstream of it:** the coaching *content* is authored and deployed elsewhere (the ``csgo-assessment`` JSON) to an S3 bucket under the prefix ``csgo/<version>/...``. The match-level inputs (the ``data`` passed into ``get_breakdowns`` / ``Translation``) come from the data-science / coach layer.
- **Consumers:** a coaching service imports ``Department`` and a reader, reads the versioned content, and asks this library to render the assessment objects. ``csgo-coach`` and ``csgo-progression`` use this abstraction to load tag-filtered coaching content and enrich evaluations and reports.

Major components
----------------

All live under ``pureskillgg_assistant_coach/department/``:

- ``Department`` (``model.py``) -- core class. ``load()`` / ``load_ds_models()`` pull content and DS models via the reader; ``get_title`` / ``get_body`` / ``get_breakdowns`` / ``get_resources`` / ``get_single_resource`` / ``get_facets`` / ``is_assignment`` / ``get_ds_model`` build rendered, localized coaching objects for an assignment plus match data. Raises ``RuntimeError`` on an unknown breakdown label.
- ``DepartmentReaderS3`` (``reader_s3.py``) -- production reader. Reads ``assignments`` / ``resources`` / ``breakdowns`` / ``facets`` / ``models`` / ``meta`` JSON from S3 via ``boto3``, decodes with ``rapidjson``, caches the ``resources`` document in memory, and denormalizes assignments through ``attach_content_to_assignment``.
- ``DepartmentReaderFs`` (``reader_fs.py``) -- filesystem equivalent of the S3 reader, reading the same documents from a local root.
- ``Translation`` (``translation.py``) -- per-language Handlebars renderer; recursively walks nested translation dicts, compiles each leaf string, and renders it with stringified match data; registers the custom ``list`` prose-join helper.
- ``create_breakdown`` / ``Breakdown`` (``breakdown.py``) -- factory and class wrapping a breakdown definition: extracts resource labels and sentiment, renders its translated features with injected data, and emits an object with a generated ``id``.
- ``create_resource`` / ``Resource`` + ``tag_matcher`` (``resource.py``) -- factory and class for a coaching resource (text / youtube / link / etc.); renders translated fields and exposes ``tags_match`` for tag-group resource selection.
- ``attach_content_to_assignment`` (``attach_content_to_assignment.py``) -- denormalizes an assignment by inlining referenced breakdown / resource / facet definitions; ``resource_is_current`` filters out resources whose ``createdAt`` is in the future.

AWS resources
-------------

The library touches exactly one AWS resource, and only to read it:

- **S3 content bucket** (caller-supplied) -- ``DepartmentReaderS3`` constructs ``boto3.client("s3")`` and calls ``get_object`` against the ``bucket`` passed into its constructor. There is **no literal bucket name in this repo**; the consuming service injects it. Keys live under ``csgo/<version>/...`` -- specifically ``csgo/<version>/meta.json`` and ``csgo/<version>/department/<name>/{assignments,resources,breakdowns,facets,models}.json``.

There are no Lambdas, DynamoDB tables, SQS / DLQ queues, SNS topics, Step Functions, EventBridge rules, AppSync APIs, or API Gateways owned by this repo, and therefore no CloudWatch log groups of its own.

Logs and observability
----------------------

This is a pure library, so it has no runtime infrastructure and no log groups, DLQs, or Sentry integration of its own.

- **Normal and error logs** live in whichever service imports the package. The library accepts an injected structured logger (used via ``log.bind(...)`` in ``reader_s3.py`` and ``model.py``) but never configures it, so log format, level, and destination are owned by the consumer (e.g. the CloudWatch log groups of ``csgo-coach`` / ``csgo-progression``).
- **Errors surface as raised Python exceptions** to the caller: ``botocore`` ``ClientError`` (e.g. ``NoSuchKey`` / ``AccessDenied``) from S3 ``get_object``; ``rapidjson.JSONDecodeError`` on malformed content JSON; and ``KeyError`` from ``attach_content_to_assignment`` when an assignment references a breakdown, resource, or facet key that is missing from the loaded documents. ``get_breakdowns`` raises ``RuntimeError`` for an unknown breakdown label. Retry, alerting, and DLQ behavior for any of these is the responsibility of the consuming service.
- **CI logs** for this repo are in `GitHub Actions <https://github.com/pureskillgg/assistant-coach/actions>`_.

Documentation
-------------

- `docs/content-model.md <docs/content-model.md>`_ -- the department content model: the five per-department documents and global meta, the ``csgo/<version>/...`` S3 layout and versioning, assignment denormalization, the multi-language ``Translation`` rendering engine, and the ``tag_matcher`` AND-of-OR resource-selection semantics.

Description
-----------

Models to generate assessments.

Installation
------------

This package is registered on the `Python Package Index (PyPI)`_
as pureskillgg-assistant-coach_.

Install it with

::

    $ uv add pureskillgg-assistant-coach

.. _pureskillgg-assistant-coach: https://pypi.python.org/pypi/pureskillgg-assistant-coach
.. _Python Package Index (PyPI): https://pypi.python.org/

Development and Testing
-----------------------

Quickstart
~~~~~~~~~~

::

    $ git clone https://github.com/pureskillgg/assistant-coach.git
    $ cd assistant-coach
    $ uv sync

Run each command below in a separate terminal window:

::

    $ make watch

Primary development tasks are defined in the `Makefile`.

Source Code
~~~~~~~~~~~

The `source code`_ is hosted on GitHub.
Clone the project with

::

    $ git clone https://github.com/pureskillgg/assistant-coach.git

.. _source code: https://github.com/pureskillgg/assistant-coach

Requirements
~~~~~~~~~~~~

You will need `Python 3`_ and uv_.

Install the development dependencies with

::

    $ uv sync

.. _uv: https://docs.astral.sh/uv/
.. _Python 3: https://www.python.org/

Tests
~~~~~

Lint code with

::

    $ make lint


Run tests with

::

    $ make test

Run tests on changes with

::

    $ make watch

Publishing
~~~~~~~~~~

Use the `uv version`_ command to release a new version.
Then run `make version` to commit and push a new git tag
which will trigger a GitHub action.

Publishing may be triggered using on the web
using a `workflow_dispatch on GitHub Actions`_.

.. _uv version: https://docs.astral.sh/uv/reference/cli/#uv-version
.. _workflow_dispatch on GitHub Actions: https://github.com/pureskillgg/assistant-coach/actions?query=workflow%3Aversion

GitHub Actions
--------------

*GitHub Actions should already be configured: this section is for reference only.*

The following repository secrets must be set on GitHub Actions.

- ``PYPI_API_TOKEN``: API token for publishing on PyPI.

These must be set manually.

Secrets for Optional GitHub Actions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The version and format GitHub actions
require a user with write access to the repository
including access to read and write packages.
Set these additional secrets to enable the action:

- ``GH_USER``: The GitHub user's username.
- ``GH_TOKEN``: A personal access token for the user.
- ``GIT_USER_NAME``: The name to set for Git commits.
- ``GIT_USER_EMAIL``: The email to set for Git commits.
- ``GPG_PRIVATE_KEY``: The `GPG private key`_.
- ``GPG_PASSPHRASE``: The GPG key passphrase.

.. _GPG private key: https://github.com/marketplace/actions/import-gpg#prerequisites

Contributing
------------

Please submit and comment on bug reports and feature requests.

To submit a patch:

1. Fork it (https://github.com/pureskillgg/assistant-coach/fork).
2. Create your feature branch (`git checkout -b my-new-feature`).
3. Make changes.
4. Commit your changes (`git commit -am 'Add some feature'`).
5. Push to the branch (`git push origin my-new-feature`).
6. Create a new Pull Request.

License
-------

This Python package is Copyright (c) 2019-2021 FPS Critic, Inc.

Warranty
--------

This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall the copyright holder or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and on
any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
