Skip to content

Commit

Permalink
add ci pipeline and linting
Browse files Browse the repository at this point in the history
  • Loading branch information
dtrai2 committed Jun 22, 2024
1 parent f357a2c commit d4c92c8
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 47 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: CI Pipeline

on:
pull_request:
types: [opened, synchronize]
push:
branches: [main]

jobs:
code-quality:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install dependencies
run: |
pip install --upgrade pip pylint
pip install .[dev]
- name: check black formating
run: |
black --check --diff --config ./pyproject.toml .
- name: lint changed and added files
run: |
pylint --rcfile=.pylintrc --fail-under 9.5 shiny_invoice/
38 changes: 38 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Upload Python Package

on:
release:
types: [published]

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/logprep
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
- name: Build package
run: pip wheel --no-deps --wheel-dir ./dist .
- name: Publish package
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
5 changes: 5 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[FORMAT]
max-line-length=100

[MESAGES CONTROL]
disable=too-few-public-methods
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ packages = ["shiny_invoice"]
name = "shiny-invoice"
description = "Simply manage invoices"
dynamic = ["version"]
requires-python = ">=3.10"
requires-python = ">=3.12"
readme = "README.md"
license = { file = "LICENSE" }
classifiers = [
Expand Down
17 changes: 13 additions & 4 deletions shiny_invoice/shiny_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@

@click.group(name="shiny-invoice")
def cli():
...
"""Shiny Invoice CLI"""


@cli.command(short_help="Run Shiny Invoice")
@click.option("--config", type=click.Path(exists=True), required=True, help="Path to the configuration yaml file.")
@click.option("--host", type=str, default="0.0.0.0", help="Host used for the server, defaults to '0.0.0.0'.")
@click.option("--port", type=int, default=8000, help="Port used for the server, defaults to '8000'.")
@click.option(
"--config",
type=click.Path(exists=True),
required=True,
help="Path to the configuration yaml file.",
)
@click.option(
"--host", type=str, default="0.0.0.0", help="Host used for the server, defaults to '0.0.0.0'."
)
@click.option(
"--port", type=int, default=8000, help="Port used for the server, defaults to '8000'."
)
def run(config: Path, host: str, port: int):
"""Run shiny invoice"""
with open(config, "r", encoding="utf8") as file:
Expand Down
15 changes: 7 additions & 8 deletions shiny_invoice/ui_config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""
This module contains the ui and server for the configuration view. It simply displays the configuration inside the
shiny invoice application
This module contains the ui and server for the configuration view. It simply displays the
configuration inside the shiny invoice application
"""

import io

from ruamel.yaml import YAML
Expand All @@ -12,16 +13,14 @@

@module.ui
def config_ui():
return ui.div(
ui.card(
ui.card_header("Configuration"),
ui.output_code("config_output")
)
)
"""Defines the shiny ui for the configuration"""
return ui.div(ui.card(ui.card_header("Configuration"), ui.output_code("config_output")))


@module.server
def config_server(input, output, session, config):
"""Contains the shiny server for the configuration view"""

@render.text
def config_output():
"""Dump the configuration into a string and return it"""
Expand Down
57 changes: 32 additions & 25 deletions shiny_invoice/ui_existing_invoices.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,56 +10,59 @@

@module.ui
def existing_invoices_ui():
"""Defines the shiny ui for existing invoices"""
return ui.div(
ui.card(
ui.card_header("Filter"),
ui.layout_columns(
ui.tooltip(
ui.input_text("invoice_numbers", "Filter by invoices", placeholder="13,21,37"),
"Comma separated"
"Comma separated",
),
ui.input_date_range(
id="daterange",
label="Filter by Date range",
start=f"{datetime.date.today().year}-01-01"
start=f"{datetime.date.today().year}-01-01",
),
ui.input_checkbox_group(
id="paid_status", label=
"Paid Status",
id="paid_status",
label="Paid Status",
choices={"paid": "Paid", "unpaid": "Unpaid"},
inline=True
)
)
inline=True,
),
),
),
ui.card(
ui.layout_column_wrap(
ui.card(
ui.card_header("List of filtered invoices"),
ui.output_data_frame("invoice_list")
ui.output_data_frame("invoice_list"),
),
ui.card(
ui.card_header("Selected Invoice"),
ui.output_ui("selected_invoice")
)
ui.card(ui.card_header("Selected Invoice"), ui.output_ui("selected_invoice")),
)
)
),
)


@module.server
def existing_invoices_server(input, output, session, config):
"""Contains the Shiny Server for existing invoices"""
@reactive.calc
def get_filtered_invoices() -> pd.DataFrame | str:
"""Retrieve all invoices from the configured directories and parse them into a DataFrame. The input filters
will then be applied to the dataframe such that only the desired results will be returned."""
"""Retrieve all invoices from the configured directories and parse them into a DataFrame.
The input filters will then be applied to the dataframe such that only the desired results
will be returned.
"""
paid_records, unpaid_records = _get_invoice_records()
df = pd.DataFrame.from_records(paid_records + unpaid_records)
if len(df) == 0:
return df
duplicate_numbers = df[df.duplicated(["Invoice"], keep="last")]
if len(duplicate_numbers) > 0:
duplicate_ids = ", ".join(duplicate_numbers['Invoice'].to_list())
ui.notification_show(f"Found duplicate invoice ids: {duplicate_ids}", type="warning", duration=2)
duplicate_ids = ", ".join(duplicate_numbers["Invoice"].to_list())
ui.notification_show(
f"Found duplicate invoice ids: {duplicate_ids}", type="warning", duration=2
)
df = _filter_invoices(df)
return df

Expand Down Expand Up @@ -90,18 +93,22 @@ def _create_invoice_records(file_paths, status):
for invoice_path in file_paths:
parts = invoice_path.split("/")
name_parts = parts[-1].split("-")
date = datetime.date(year=int(name_parts[0]), month=int(name_parts[1]), day=int(name_parts[2]))
date = datetime.date(
year=int(name_parts[0]), month=int(name_parts[1]), day=int(name_parts[2])
)
invoice_number = name_parts[3]
customer = name_parts[-1].replace(".html", "")
root_dir = config.get("paths").get("invoices_root_dir")
invoice_path = invoice_path.replace(root_dir, "")
records.append({
"Date": date,
"Invoice": invoice_number,
"Status": status,
"Customer": customer,
"Link": ui.a("Download", href=invoice_path, target="_blank"),
})
records.append(
{
"Date": date,
"Invoice": invoice_number,
"Status": status,
"Customer": customer,
"Link": ui.a("Download", href=invoice_path, target="_blank"),
}
)
return records

@render.data_frame
Expand Down
24 changes: 15 additions & 9 deletions shiny_invoice/ui_new_invoice.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""This module contains the ui and the server for creating a new invoice."""

import datetime
import io
from pathlib import Path
Expand All @@ -10,6 +11,7 @@

@module.ui
def new_invoice_ui(config):
"""Defines the shiny ui for new invoices"""
invoice_defaults = config.get("invoice_defaults")

return ui.layout_column_wrap(
Expand All @@ -18,13 +20,15 @@ def new_invoice_ui(config):
ui.input_text(id="invoice_number", label="Invoice Number", value="1", width="100%"),
ui.input_date(id="created_at_date", label="Created At", width="100%"),
ui.output_ui(id="due_date_ui", width="100%"),
ui.input_text(id="introduction", label="Introduction", value="Dear Sir or Madam,", width="100%"),
ui.input_text(
id="introduction", label="Introduction", value="Dear Sir or Madam,", width="100%"
),
ui.input_text_area(
id="recipient_address",
label="Recipient Address",
value=invoice_defaults.get("recipient"),
rows=3,
width="100%"
width="100%",
),
ui.tooltip(
ui.input_text_area(
Expand All @@ -33,17 +37,17 @@ def new_invoice_ui(config):
value=invoice_defaults.get("items"),
rows=6,
width="100%",
spellcheck=True
spellcheck=True,
),
"Should be in csv format. The last column will be used to calculate the total price."
"The values should be before taxes."
"Should be in csv format. The last column will be used to calculate the"
"total price. The values should be before taxes.",
),
ui.download_button(id="download_button", label="Download Invoice", width="100%")
ui.download_button(id="download_button", label="Download Invoice", width="100%"),
),
ui.card(
ui.card_header("Rendered Invoice"),
ui.output_ui(id="rendered_invoice_ui", width="100%")
)
),
)


Expand All @@ -65,7 +69,9 @@ def convert_invoice_csv_to_html() -> str:
def calculate_totals():
items = parse_invoice_items()
last_column = items.columns[-1]
items[last_column] = items[last_column].str.replace(".", "").str.replace("€", "").astype(float)
items[last_column] = (
items[last_column].str.replace(".", "").str.replace("€", "").astype(float)
)
return items[last_column].sum()

@render.ui
Expand Down Expand Up @@ -114,7 +120,7 @@ def render_invoice():
"invoice_items": convert_invoice_csv_to_html(),
"total_net": f"{total_net:n} €",
"tax": f"{tax:n} €",
"total_gross": f"{total_gross:n} €"
"total_gross": f"{total_gross:n} €",
}
return html_template.substitute(substitutions)

Expand Down

0 comments on commit d4c92c8

Please sign in to comment.