Skip to content

Commit

Permalink
Maintenance updates
Browse files Browse the repository at this point in the history
* Update Python version to 3.12
* Update Python dependencies
* Update pyproject.toml and add Ruff rule G004 to 'ignore' list
* Clean up section headers in Makefile
* Update 'Environment Variables' and description in README
  • Loading branch information
jonavellecuerdo committed Sep 19, 2024
1 parent bdf569e commit da3d1d9
Show file tree
Hide file tree
Showing 9 changed files with 623 additions and 535 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default_language_version:
python: python3.11 # set for project python version
python: python3.12 # set for project python version
repos:
- repo: local
hooks:
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11.2
3.12
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.11-slim as build
FROM python:3.12-slim as build
WORKDIR /app
COPY . .

Expand Down
52 changes: 33 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,64 @@ SHELL=/bin/bash
DATETIME:=$(shell date -u +%Y%m%dT%H%M%SZ)

help: ## Print this message
@awk 'BEGIN { FS = ":.*##"; print "Usage: make <target>\n\nTargets:" } \
/^[-_[:alpha:]]+:.?*##/ { printf " %-15s%s\n", $$1, $$2 }' $(MAKEFILE_LIST)
@awk 'BEGIN { FS = ":.*#"; print "Usage: make <target>\n\nTargets:" } \
/^[-_[:alpha:]]+:.?*#/ { printf " %-15s%s\n", $$1, $$2 }' $(MAKEFILE_LIST)

### Dependency commands ###
#######################
# Dependency commands
#######################

install: ## Install dependencies and CLI app
install: # Install Python dependencies
pipenv install --dev
pipenv run pre-commit install

update: install ## Update all Python dependencies
update: install # Update Python dependencies
pipenv clean
pipenv update --dev

### Test commands ###
######################
# Unit test commands
######################

test: ## Run tests and print a coverage report
test: # Run tests and print a coverage report
pipenv run coverage run --source=ccslips -m pytest -vv
pipenv run coverage report -m

coveralls: test
coveralls: test # Write coverage data to an LCOV report
pipenv run coverage lcov -o ./coverage/lcov.info

### Code quality and safety commands ###
####################################
# Code quality and safety commands
####################################

# linting commands
lint: black mypy ruff safety
lint: black mypy ruff safety # Run linters

black:
black: # Run 'black' linter and print a preview of suggested changes
pipenv run black --check --diff .

mypy:
mypy: # Run 'mypy' linter
pipenv run mypy .

ruff:
ruff: # Run 'ruff' linter and print a preview of errors
pipenv run ruff check .

safety:
safety: # Check for security vulnerabilities and verify Pipfile.lock is up-to-date
pipenv check
pipenv verify

# apply changes to resolve any linting errors
lint-apply: black-apply ruff-apply
lint-apply: # Apply changes with 'black' and resolve 'fixable errors' with 'ruff'
black-apply ruff-apply

black-apply:
black-apply: # Apply changes with 'black'
pipenv run black .

ruff-apply:
ruff-apply: # Resolve 'fixable errors' with 'ruff'
pipenv run ruff check --fix .

#####################################
# Docker build and publish commands
#####################################

### Terraform-generated Developer Deploy Commands for Dev environment ###
dist-dev: ## Build docker container (intended for developer-based manual build)
docker build --platform linux/amd64 \
Expand Down Expand Up @@ -85,5 +95,9 @@ publish-stage: ## Only use in an emergency
docker push $(ECR_URL_STAGE):latest
docker push $(ECR_URL_STAGE):`git describe --always`

################
# Run commands
################

run-dev: # Run in dev against Alma sandbox
aws ecs run-task --cluster alma-integrations-creditcardslips-ecs-dev --task-definition alma-integrations-creditcardslips-ecs-dev --launch-type="FARGATE" --network-configuration '{ "awsvpcConfiguration": {"subnets": ["subnet-0488e4996ddc8365b", "subnet-022e9ea19f5f93e65"],"securityGroups": ["sg-095372030a26c7753"],"assignPublicIp": "DISABLED"}}'
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ safety = "*"
types-requests = "*"

[requires]
python_version = "3.11"
python_version = "3.12"

[scripts]
ccslips = "python -c \"from ccslips.cli import main; main()\""
1,020 changes: 543 additions & 477 deletions Pipfile.lock

Large diffs are not rendered by default.

37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,43 @@

A CLI application to generate and email credit card slips for Alma invoices via the Alma API.

## Description
Credit card slips are generated for items purchased with a credit card (e.g. from vendors like Amazon), which means we prepay rather than receive an invoice after shipment, which is our workflow with most vendors. The credit card slip is in lieu of a vendor-generated invoice, and is used for processing by Acquisition staff.
Credit card slips are generated for items purchased with a credit card (e.g. from vendors like Amazon), which means we prepay rather than receive an invoice after shipment, which is our workflow with most vendors. The credit card slip is in lieu of a vendor-generated invoice and is used for processing by Acquisition staff.

The application runs daily and retrieves purchase order (PO) lines from the Alma REST API with the following criteria:
The app retrieves purchase order (PO) lines from the Alma REST API with the following criteria:
* `status=ACTIVE`
* `acquisition_method=PURCHASE_NOLETTER` (`Credit card` in the Alma UI)
* A note that begins with `CC-`

The application is scheduled to run each day as an Elastic Container Service (ECS) task. By default, it retrieves PO lines from 2 days before the date the application is run. Originally, it was set for 1 day before the application is run but a bug was discovered in August 2023 that required a change to 2 days before in order to get the expected output.
**Note:** By default, it retrieves PO lines from two (2) days before the date the application is run. Originally, it was set for one (1) day before the application is run, but a bug was discovered in August 2023 that required the change in order to get the expected output.

The application fills in a template with the PO line data and the resulting file is emailed as an attachment to the necessary stakeholders. Acquisitions staff print out the attachment, mark it up, and complete recording the payment in Alma.
Data is extracted from the PO lines and used to fill in a template, and the resulting file is emailed as an attachment to the necessary stakeholders. Acquisitions staff print out the attachment, mark it up, and complete recording the payment in Alma.

This Python CLI application is run on a schedule as an Elastic Container Service (ECS) task in AWS via EventBridge rules.

## Development

- To preview a list of available Makefile commands: `make help`
- To install with dev dependencies: `make install`
- To update dependencies: `make update`
- To run unit tests: `make test`
- To lint the repo: `make lint`
- To run the app: `pipenv run ccslips --help`

## Required ENV Variables
## Environment Variables

### Required

- `ALMA_API_URL`: Base URL for the Alma API.
- `ALMA_API_READ_KEY`: Read-only key for the appropriate Alma instance (sandbox or prod) Acquisitions API.
- `WORKSPACE`: Set to `dev` for local development, this will be set to `stage` and `prod` in those environments by Terraform.
```shell
ALMA_API_URL=### Base URL for the Alma API.
ALMA_API_READ_KEY=### Read-only key for the appropriate Alma instance (sandbox or prod) Acquisitions API.
SENTRY_DSN=### If set to a valid Sentry DSN enables Sentry exception monitoring. This is not needed for local development.
WORKSPACE=### Set to `dev` for local development, this will be set to `stage` and `prod` in those environments by Terraform.
```

## Optional ENV Variables
### Optional

- `ALMA_API_TIMEOUT`: Request timeout for Alma API calls. Defaults to 30 seconds if not set.
- `LOG_LEVEL`: Set to a valid Python logging level (e.g. `DEBUG`, case-insensitive) if desired. Can also be passed as an option directly to the ccslips command. Defaults to `INFO` if not set or passed to the command.
- `SENTRY_DSN`: If set to a valid Sentry DSN enables Sentry exception monitoring. This is not needed for local development.
- `SES_RECIPIENT_EMAIL`: Email address(es) for recipient(s) who should receive the credit card slips email. Multiple email addresses should be separated by a space, e.g. `[email protected] [email protected]`. This value can either be set in ENV or passed directly to the command line as an option.
- `SES_SEND_FROM_EMAIL`: Verified email address for sending emails via SES. This value can either be set in ENV or passed directly to the command as an option.
```shell
ALMA_API_TIMEOUT=### Request timeout for Alma API calls. Defaults to 30 seconds.
SES_RECIPIENT_EMAIL=### Email addresses for recipients of the the credit card slips email. Multiple email addresses should be separated by a space, e.g. '[email protected] [email protected]'. This value can also be passed directly to the CLI command via the -r/--recipient-email option.
SES_SEND_FROM_EMAIL=### Verified email address for sending emails via SES. This value can also be passed directly to the CLI command via the -s/--source-email option.
```
14 changes: 6 additions & 8 deletions ccslips/polines.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import xml.etree.ElementTree as ET
from collections.abc import Generator, Iterator
from copy import deepcopy
from datetime import datetime
from decimal import Decimal
from xml.etree import ElementTree

from ccslips.alma import AlmaClient

Expand Down Expand Up @@ -120,21 +120,19 @@ def get_account_number_from_fund(client: AlmaClient, fund: dict) -> str | None:

def generate_credit_card_slips_html(po_line_data: Iterator[dict]) -> str:
"""Create credit card slips HTML from a set of credit card slip data."""
template_tree = ElementTree.parse( # noqa: S314
"config/credit_card_slip_template.xml"
)
template_tree = ET.parse("config/credit_card_slip_template.xml") # noqa: S314
xml_template = template_tree.getroot()
output = ElementTree.fromstring("<html></html>") # noqa: S314
output = ET.fromstring("<html></html>") # noqa: S314
for line in po_line_data:
output.append(populate_credit_card_slip_xml_fields(deepcopy(xml_template), line))
if len(output) == 0:
return "<html><p>No credit card orders on this date</p></html>"
return ElementTree.tostring(output, encoding="unicode", method="xml")
return ET.tostring(output, encoding="unicode", method="xml")


def populate_credit_card_slip_xml_fields(
credit_card_slip_xml_template: ElementTree.Element, credit_card_slip_data: dict
) -> ElementTree.Element:
credit_card_slip_xml_template: ET.Element, credit_card_slip_data: dict
) -> ET.Element:
"""Populate credit card slip XML template with data extracted from a PO line.
The credit_card_slip_data keys must correspond to their associated element classes
Expand Down
27 changes: 15 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ exclude = ["tests/"]
log_level = "INFO"

[tool.ruff]
target-version = "py311"
target-version = "py312"

# set max line length
line-length = 90

# enumerate all fixed violations
show-fixes = true

[tool.ruff.lint]
select = ["ALL", "PT"]

ignore = [
Expand All @@ -29,6 +37,7 @@ ignore = [
"D102",
"D103",
"D104",
"G004",
"PLR0912",
"PLR0913",
"PLR0915",
Expand All @@ -39,27 +48,21 @@ ignore = [
# allow autofix behavior for specified rules
fixable = ["E", "F", "I", "Q"]

# set max line length
line-length = 90

# enumerate all fixed violations
show-fixes = true

[tool.ruff.flake8-annotations]
[tool.ruff.lint.flake8-annotations]
mypy-init-return = true

[tool.ruff.flake8-pytest-style]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"tests/**/*" = [
"ANN",
"ARG001",
"S101",
]

[tool.ruff.pycodestyle]
[tool.ruff.lint.pycodestyle]
max-doc-length = 90

[tool.ruff.pydocstyle]
[tool.ruff.lint.pydocstyle]
convention = "google"

0 comments on commit da3d1d9

Please sign in to comment.