diff --git a/.d4g-tools/bin/check.sh b/.d4g-tools/bin/check.sh new file mode 100755 index 0000000..22d776c --- /dev/null +++ b/.d4g-tools/bin/check.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +set -Eeuo pipefail + +# Folder containing this script +RUN_DIR="$(dirname "${BASH_SOURCE[0]}")" +SELF_PATH="$(basename "${BASH_SOURCE[0]}")" +echo "RUN_DIR: $RUN_DIR" +echo "SELF_PATH: $SELF_PATH" +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +source "$VENV_DIR"/bin/activate + +# Array of services to check +services=("nginx" "apache2" "uwsgi") + +# Loop through the services +for service in "${services[@]}"; do + # Check if the service is installed + if dpkg -l | grep -q "$service"; then + echo "$service is installed." + + # Print the service's binary location + bin_location=$(which "$service") + info "Binary location for $service: $bin_location" + + # Print the port the service uses + if [ "$service" == "nginx" ] || [ "$service" == "apache2" ]; then + port=$(sudo lsof -i -P -n | grep "$service" | awk '{print $9}' | cut -d: -f2) + echo "Port for $service: $port" + elif [ "$service" == "uwsgi" ]; then + echo "uwsgi does not listen on a port. It is a protocol implemented over TCP." + fi + else + echo "$service is not installed." + fi +done +deactivate +cleanup diff --git a/.d4g-tools/bin/init.sh b/.d4g-tools/bin/init.sh new file mode 100755 index 0000000..b622341 --- /dev/null +++ b/.d4g-tools/bin/init.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# Function to display usage +usage() { + echo "Usage: $0 --platform=python|taipy" + exit 1 +} + +# Parse command line arguments +for i in "$@"; do + case $i in + --platform=*) + PLATFORM="${i#*=}" + shift + ;; + *) + usage + ;; + esac +done + +# Check if PLATFORM is set +if [ -z "$PLATFORM" ]; then + usage +fi + +# Create project directory if it doesn't exist +if [ ! -d "$PROJECT_NAME" ]; then + mkdir "$PROJECT_NAME" + echo "Created directory: $PROJECT_NAME" +fi + +# Execute scripts based on platform +if [ "$PLATFORM" == "python" ] || [ "$PLATFORM" == "taipy" ]; then + ./python.sh + echo "Executed python.sh" + if [ "$PLATFORM" == "taipy" ]; then + ./taipy.sh + echo "Executed taipy.sh" + fi +fi + +echo "Initialization complete for platform: $PLATFORM" diff --git a/.d4g-tools/bin/locust/locust.sh b/.d4g-tools/bin/locust/locust.sh new file mode 100644 index 0000000..a0f3fae --- /dev/null +++ b/.d4g-tools/bin/locust/locust.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 + +set -Eeuo pipefail + +# IMPORTANT AND NECESSARY: Load dependencies +source "$LIB_DIR"/common.sh + +usage() { + cat </dev/null && pwd -P) + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose) + DEBUG="true" + ;; + --dummy-flag*) + DUMMY_FLAG="true" + ;; + --dummy-param=*) + DUMMY_PARAM="${1#*=}" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + return 0 +} + +parse_params "$@" + +echo -n "Ready to rumble." diff --git a/.d4g-tools/bin/locust/readme.md b/.d4g-tools/bin/locust/readme.md new file mode 100644 index 0000000..815da25 --- /dev/null +++ b/.d4g-tools/bin/locust/readme.md @@ -0,0 +1,21 @@ +# Tests de charge + +Pour vérifier le bon fonctionnement du site avec plusieurs utilisateurs des tests de charge ont été mis en place, à l'aide de [Locust](https://locust.io/), un outil de test de charge. + + + +Lancement : +```shell +cd load_testing +source .venv/bin/activate +locust -f load_testing.py +``` + +Puis se rendre à l'adresse http://0.0.0.0:8089 pour lancer un test avec le nombre d'utilisateurs souhaités. + diff --git a/.d4g-tools/bin/memory.sh b/.d4g-tools/bin/memory.sh new file mode 100755 index 0000000..1fa5a6a --- /dev/null +++ b/.d4g-tools/bin/memory.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 +set -Eeuo pipefail + +usage() { + cat < + +Report memory usage (linux, WSL2) + +Supported parameters : +-h, --help : display this message +-v, --verbose : enable enhanced logging + +: start, stop, show +EOF + exit 1 +} + +parse_params() { + if [ $# -lt 1 ]; then + echo "Some parameters are missing" + usage + elif [ $# -gt 2 ]; then + echo "Too many parameters provided" + usage + fi + + RUN_SCRIPT="${BASH_SOURCE[0]}" + RUN_DIR="$(dirname "${RUN_SCRIPT}")" + + source "$LIB_DIR/common.sh" + # Format the date as YYYYMMDDHHMM + date=$(date +"%Y%m%d") + USAGE_LOG="$LOG_DIR"/usage.$date.log + USAGE_PLOT="$LOG_DIR"/usage.$date.png + + # Parameters + ACTION="false" + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose) + DEBUG="true" + ;; + --action=*) + ACTION="${1#*=}" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + debug "ACTION: $ACTION" + debug "Log file: $USAGE_LOG" + + return 0 +} + +parse_params "$@" + +# Function to get memory usage +get_memory_usage() { + free -m | awk '/Mem/{printf("%.2f"), $3/$2*100}' +} + +# Function to get CPU usage +get_cpu_usage() { + top -bn1 | grep "Cpu(s)" | + sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | + awk '{print 100 - $1"%"}' +} + +case "$ACTION" in +"record") + # Report memory usage + time=$(date +"%Y-%m-%d-%H:%M:%S") + + # Get memory usage in MB and CPU usage + memory_usage=$(free -m | awk 'NR==2{printf "%.2f", $3 }') + cpu_usage=$(top -bn1 | grep load | awk '{printf "%.2f", $(NF-2)}') + # memory_usage=$(vmstat -s | grep "used memory" | awk '{print $1/1024}') + # cpu_usage=$(vmstat 1 2 | tail -1 | awk '{print $13+$14}') + + echo "$time $memory_usage $cpu_usage" >>"$USAGE_LOG" + "$PROJECT_DIR/d4g" --memory=show + ;; +"start") + "$PROJECT_DIR/d4g" --memory=record + # Add to user crontab if not already added + if ! crontab -l | grep -q "$PROJECT_DIR/d4g --memory=record"; then + ( + crontab -l >/dev/null 2>&1 + echo "* * * * * $PROJECT_DIR/d4g --memory=record" + ) | crontab - + fi + + ;; +"show") + gnuplot <tmpcron + grep -v "$PROJECT_DIR/d4g --memory=record" tmpcron >cron || true + crontab cron + ;; +*) ;; +esac +# set title "System Usage Over Time" +# set xlabel "Time" +# set ylabel "Usage (%)" +# set xdata time +# set timefmt "%Y-%m-%d %H:%M:%S" +# set format x "%H:%M" +# set autoscale +# set term png +# set output "usage.png" +# plot "/tmp/usage_data.txt" using 1:2 title 'Memory' with lines, \ +# "/tmp/usage_data.txt" using 1:3 title 'CPU' with lines +cleanup diff --git a/.d4g-tools/bin/nginx/nginx.sh b/.d4g-tools/bin/nginx/nginx.sh new file mode 100644 index 0000000..17ddce5 --- /dev/null +++ b/.d4g-tools/bin/nginx/nginx.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 + +set -Eeuo pipefail + +RUN_DIR="$(dirname "${BASH_SOURCE[0]}")" +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +# Create a self-signed SSL certificate +# sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/certs/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -sha256 -days 3650 -nodes -subj "/CN=localhost" +# Update the Nginx configuration to expose the application + +NGINX_CONF="$PROJECT_NAME.$STAGE.nginx.conf" + +if [ ! -f "$GENERATED/$NGINX_CONF" ]; then + error "Missing $GENERATED/$NGINX_CONF" + exit 1 +fi + +# Check if nginx is installed, if not then install it +if ! command -v nginx &>/dev/null; then + info "Installing nginx..." + # TODO use brew? + sudo apt install -y nginx +fi + +sudo systemctl stop nginx + +# Format the date as YYYYMMDDHHMM +date=$(date +"%Y%m%d") + +nginx_sites_available="/etc/nginx/sites-available" +nginx_sites_enabled="/etc/nginx/sites-enabled" + +if [ -f "$nginx_sites_available/$PROJECT_NAME" ]; then + sudo mv "$nginx_sites_available/$PROJECT_NAME" "$nginx_sites_available/$PROJECT_NAME.$date.bak" +fi +sudo cp "$GENERATED/$NGINX_CONF" "$nginx_sites_available/$PROJECT_NAME" + +default_link="$nginx_sites_enabled/default" +if [ -L "${default_link}" ]; then + rm "$default_link" +fi + +sudo ln -fs "$nginx_sites_available/$PROJECT_NAME" "$nginx_sites_enabled/$PROJECT_NAME" + +# Restart Nginx to apply the changes +sudo systemctl start nginx + +echo "GOOD JOB, YOU ARE NOW RUNNING THE APP IN DEV MODE that mimics production." +echo "Running services:" + +echo "VISIT http://$DOMAIN:$HTTP_PORT via installed nginx proxy " +# echo " or http://$DOMAIN:$APP_PORT, directly via installed uwsgi server" + +cleanup diff --git a/.d4g-tools/bin/pull/pull.sh b/.d4g-tools/bin/pull/pull.sh new file mode 100755 index 0000000..b2c9300 --- /dev/null +++ b/.d4g-tools/bin/pull/pull.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +trap cleanup SIGINT SIGTERM ERR EXIT + +usage() { + cat </dev/null && pwd -P) + SOURCE=$(dirname "$RUN_DIR") + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose | --debug) + DEBUG="true" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + return 0 +} + +parse_params "$@" + +sudo /bin/systemctl stop taxplorer-dev.uwsgi.service +cd "$SOURCE" && git pull +sudo /bin/systemctl start taxplorer-dev.uwsgi.service + +exit 0 diff --git a/.d4g-tools/bin/pull/pull_cron.sh b/.d4g-tools/bin/pull/pull_cron.sh new file mode 100755 index 0000000..8a99340 --- /dev/null +++ b/.d4g-tools/bin/pull/pull_cron.sh @@ -0,0 +1,190 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +trap cleanup SIGINT SIGTERM ERR EXIT + +usage() { + cat </dev/null +} + +deploy_lock() { + ACTION=$1 + SHA=$2 + debug "ACTION: $ACTION" + debug "SHA: $SHA" + if [ "${ACTION}" == "acquire" ]; then + if [ -f "$LOCK_LOCATION" ]; then + error "Deploy lock already exists at $LOCK_LOCATION. Exiting." + error "$(cat "$LOCK_LOCATION")" + exit 1 + else + # Create lock with timestamp and sha as content + echo "$(date +"%Y-%m-%dT%H:%M:%S%:z") - $SHA" >"$LOCK_LOCATION" + fi + elif [ "${ACTION}" == "release" ]; then + if [ -f "$LOCK_LOCATION" ]; then + rm "$LOCK_LOCATION" + else + error "Deploy lock does not exist. Exiting." + exit 1 + fi + else + error "Invalid action. Exiting." + exit 1 + fi +} + +info() { + gum style --foreground=4 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +} + +warning() { + gum style --foreground=3 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +} + +success() { + gum style --bold --foreground=2 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +} + +error() { + gum style --bold --foreground=1 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +} + +debug() { + if [ "$DEBUG" == 'true' ]; then + gum style --faint "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" + fi +} + +parse_params() { + # Sane defaults + DEBUG="false" + RUN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + SOURCE=$(dirname "$RUN_DIR") + REPOSITORY_NAME="" + BRANCH="main" + DEPLOY_SCRIPT="${SOURCE}/pull/pull.sh" + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose) + DEBUG="true" + ;; + --repository-name=*) + REPOSITORY_NAME="${1#*=}" + REPO_CANONICAL_NAME=$(echo "$REPOSITORY_NAME" | tr '/' '_') + LOCK_LOCATION="/tmp/deploy_${REPO_CANONICAL_NAME}.lock" + ;; + --branch=*) + BRANCH="${1#*=}" + ;; + --deploy-script=*) + DEPLOY_SCRIPT="${1#*=}" + ;; + --lock-location=*) + LOCK_LOCATION="${1#*=}" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + debug "DEBUG: $DEBUG" + debug "RUN_DIR: $RUN_DIR" + debug "REPOSITORY_NAME: $REPOSITORY_NAME" + debug "BRANCH: $BRANCH" + debug "DEPLOY_SCRIPT: $DEPLOY_SCRIPT" + debug "LOCK_LOCATION: $LOCK_LOCATION" + + if [ -z "${REPOSITORY_NAME}" ]; then + error "Repository name is required." + usage + fi + + return 0 +} + +parse_params "$@" + +# Check if a new commit exists +HEAD_COMMIT=$(curl -s "https://api.github.com/repos/${REPOSITORY_NAME}/commits/${BRANCH}" | jq -r '.sha') +LOCAL_COMMIT=$(cd "$SOURCE" && git rev-parse HEAD) + +debug "HEAD_COMMIT: $HEAD_COMMIT" +debug "LOCAL_COMMIT: $LOCAL_COMMIT" + +if [ "${HEAD_COMMIT}" != "${LOCAL_COMMIT}" ]; then + info "New commit found. Running deploy script." + # Acquire deploy lock + deploy_lock acquire "$HEAD_COMMIT" + report_github_status "$HEAD_COMMIT" "pending" "Deployment in progress." + # Measure time taken to deploy + START_TIME=$(date +%s) + ${DEPLOY_SCRIPT} + END_TIME=$(date +%s) + DEPLOY_TIME=$((END_TIME - START_TIME)) + # get deploy script exit code + DEPLOY_EXIT_CODE=$? + if [ $DEPLOY_EXIT_CODE -eq 0 ]; then + report_github_status "$HEAD_COMMIT" "success" "Deployment successful." + deploy_lock release "$HEAD_COMMIT" + success "Deployment successful, took ${DEPLOY_TIME}s 🚀." + else + report_github_status "$HEAD_COMMIT" "failure" + deploy_lock release "$HEAD_COMMIT" "Deployment failed." + error "An error occurred while deploying. Exiting." + exit 1 + fi +else + success "No new commit found. Exiting." +fi + +cleanup diff --git a/.d4g-tools/bin/pull/readme.md b/.d4g-tools/bin/pull/readme.md new file mode 100644 index 0000000..917d085 --- /dev/null +++ b/.d4g-tools/bin/pull/readme.md @@ -0,0 +1,15 @@ +# Install gum + +``` +sudo mkdir -p /etc/apt/keyrings +curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg +echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list +sudo apt update && sudo apt install gum +``` + +# Content of cron file on sytem: + + +```bash +* * * * * /opt/d4g/12_taxobservatory_dataviz_dev/.d4g-tools/deploy/pull/pull_cron.sh --repository-name=dataforgoodfr/12_taxobservatory_dataviz_dev --branch=dev >> /opt/d4g/12_taxobservatory_dataviz_dev/pull_cron.log 2>&1 +``` diff --git a/.d4g-tools/bin/python/__init__.py b/.d4g-tools/bin/python/__init__.py new file mode 100644 index 0000000..cfd7c68 --- /dev/null +++ b/.d4g-tools/bin/python/__init__.py @@ -0,0 +1 @@ +from os import path diff --git a/.d4g-tools/bin/python/pyproject.toml.dist b/.d4g-tools/bin/python/pyproject.toml.dist new file mode 100644 index 0000000..e23e3b4 --- /dev/null +++ b/.d4g-tools/bin/python/pyproject.toml.dist @@ -0,0 +1,114 @@ +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "$PROJECT_NAME" +version = "0.1.0" +description = "$PROJECT_DESCRIPTION" +authors = ["DataForGood", "$PROJECT_AUTHORS"] +license = " MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +humanize = "^4.9.0" +# TODO +taipy = "^3.1.1" +taipy-gui = "^3.1.2" +plotly = "^5.22.0" +numpy = "^1.26.4" +pillow = "^10.4.0" + +[tool.poetry.group.dev.dependencies] +pre-commit = "^3.7.1" +pytest = "^8.2.2" +tox = "^4.4.8" +ruff = "^0.5.0" +locust = "^2.29.0" + +# D4G: Python Linting and Formatting +[tool.ruff] +force-exclude = true +fix = true +show-fixes = true +# Same as Black. +line-length = 96 +target-version = "py310" + +cache-dir = ".cache/ruff" +# Output-format: "full" | "concise" | "grouped" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure" +# Group violations by containing file. +output-format = "grouped" + +extend-include = ["*.ipynb"] +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + # D4G specific: needed for d4g-tools + "**/.generated/**", + "**/.gi-**", + ".cache", +] + + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +# Numpy2.0:NPY201 + +select = ["E4", "E7", "E9", "F", "B", "NPY201"] +ignore = ["E501", "E712"] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.lint.mccabe] +max-complexity = 10 + +[tool.ruff.lint.extend-per-file-ignores] +# Also ignore `F401` in all `__init__.py` files. +# Diable `F401` to keep unsed imports from `__init__.py` files. +"__init__.py" = ["F401"] + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" diff --git a/d4g-utils/install_poetry.sh b/.d4g-tools/bin/python/python.sh similarity index 58% rename from d4g-utils/install_poetry.sh rename to .d4g-tools/bin/python/python.sh index 0635980..076aa27 100755 --- a/d4g-utils/install_poetry.sh +++ b/.d4g-tools/bin/python/python.sh @@ -1,21 +1,27 @@ -#!/bin/bash +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +set -Eeuo pipefail -# Function to check if a command is available -command_exists() { - command -v "$1" &> /dev/null -} +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +# TODO INSTALL PYTHON ? # Check if Poetry is installed -if command_exists poetry && poetry --version &> /dev/null; then - echo "Poetry is already installed. Version: $(poetry --version)" +if command_exists poetry && poetry --version &>/dev/null; then + info "Poetry is already installed. Version: $(poetry --version)" else - echo "Poetry is not installed. Installing now..." - + info "Poetry is not installed. Installing now..." + # Install Poetry curl -sSL https://install.python-poetry.org | python3 - - + # Add poetry to PATH - echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc + # shellcheck disable=SC2016 + # TODO: Add poetry to PATH . Check compatibility with macos, linux and wsl2! + echo 'export PATH="$HOME/.local/bin:$PATH"' >>~/.bashrc + # shellcheck disable=SC1090 source ~/.bashrc echo "Poetry has been installed." @@ -28,9 +34,6 @@ poetry config virtualenvs.in-project true echo "Poetry is now configured to use a virtual environment in the project directory." -# Navigate to the project directory -project_dir=$(pwd) - # Check if the virtual environment exists in the project directory if [ -d ".venv" ]; then echo "Virtual environment in project directory exists." @@ -39,7 +42,8 @@ fi # Analyze project content and create pyproject.toml file if not exists if [ ! -f pyproject.toml ]; then echo "Creating pyproject.toml file..." - poetry init --no-interaction + # TODO pass the project name, authors and some other option as argument (see d4g.ini) + poetry init #--no-interaction else echo "pyproject.toml file already exists. Skipping initialization." fi diff --git a/.d4g-tools/bin/taipy/taipy.sh b/.d4g-tools/bin/taipy/taipy.sh new file mode 100755 index 0000000..6a0d941 --- /dev/null +++ b/.d4g-tools/bin/taipy/taipy.sh @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 + +RUN_DIR="$(dirname "${BASH_SOURCE[0]}")" +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +parse_params() { + if [ $# -gt 3 ]; then + echo "Too many parameters provided" + usage + fi + + PROD="false" + PORT="false" + + while :; do + case "${1-}" in + --prod=*) + PROD="${1#*=}" + ;; + --port=*) + PORT="${1#*=}" + echo "Running on port $PORT" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + return 0 +} + +parse_params "$@" + +# Check if the virtual environment exists in the project directory +if [ ! -d "$VENV_DIR" ]; then + echo "Virtual environment not found in project directory!" + exit 1 +fi + +# Update the package lists for upgrades and new package installations +sudo apt update -y &>/dev/null +# Ensure necessary dependencies are installed +sudo apt install build-essential python3-dev libssl-dev &>/dev/null + +# Activate the virtual environment +source "$VENV_DIR"/bin/activate +# Install uwsgi and gevent using pip +pip install uwsgi gevent >/dev/null 2>&1 +# not recommended: asyncio greenlet +# Deactivate the virtual environment +deactivate + +#--http-socket :$APP_PORT +UWSGI_SERVICE="$PROJECT_NAME.$STAGE.uwsgi.service" +# Generate a Systemd file for uWSGI +echo """ +[Unit] +Description=$PROJECT_NAME UWSGI Server +After=syslog.target + +[Service] +ExecStart=$VENV_DIR/bin/uwsgi --ini .d4g-tools/bin/taipy/uwsgi.ini:$STAGE --socket /tmp/$PROJECT_NAME.$STAGE.uwsgi.sock --module $PROJECT_NAME.main:web_app +# ExecStart=$VENV_DIR/bin/uwsgi --ini .d4g-tools/bin/taipy/uwsgi.ini:$STAGE --http-socket :$APP_PORT +WorkingDirectory=$PROJECT_DIR +Restart=always +KillSignal=SIGINT +Type=notify +StandardError=syslog +NotifyAccess=all +User=$(whoami) + +[Install] +WantedBy=multi-user.target +""" >"$GENERATED/$UWSGI_SERVICE" + +# if [ "$PROD" == "true" ]; then + +# Move the Systemd file to the correct directory +sudo cp "$GENERATED/$UWSGI_SERVICE" "/etc/systemd/system/$UWSGI_SERVICE" +sudo systemctl daemon-reload + +# Start the uWSGI service +sudo systemctl restart "$UWSGI_SERVICE" + +# Enable the uWSGI service to start on boot +# sudo systemctl enable "$UWSGI_SERVICE" + +# Check if nginx is installed, if not then install it +if ! command -v nginx &>/dev/null; then + sudo apt install -y nginx +fi +# Create a self-signed SSL certificate +# sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/certs/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -sha256 -days 3650 -nodes -subj "/CN=localhost" +# Update the Nginx configuration to expose the application + +NGINX_CONF="$PROJECT_NAME.$STAGE.nginx.conf" + +if [ "$PROD" == "false" ]; then + echo """ + server { + listen $HTTP_PORT; + #listen 443 ssl; + + server_name localhost; + + # SECURITY HEADERS + add_header 'X-Frame-Options' 'SAMEORIGIN'; + add_header 'X-XSS-Protection' '1; mode=block'; + add_header 'X-Content-Type-Options' 'nosniff'; + add_header 'Referrer-Policy' 'same-origin'; + add_header 'Strict-Transport-Security' 'max-age=63072000'; + + #ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + #ssl_certificate_key /etc/ssl/certs/nginx-selfsigned.key; + + # location / { + # include proxy_params; + # proxy_set_header Upgrade \$http_upgrade; + # proxy_set_header Connection 'upgrade'; + # proxy_set_header Host \$host; + # proxy_set_header X-Forwarded-Host \$host; + # proxy_pass http://localhost:$APP_PORT; + # } + + location / { + include uwsgi_params + uwsgi_pass unix:/tmp/$PROJECT_NAME.$STAGE.uwsgi.sock; + } + + }""" | sudo tee "$GENERATED/$NGINX_CONF" + + sudo systemctl stop nginx + + # Format the date as YYYYMMDDHHMM + date=$(date +"%Y%m%d") + + nginx_sites_available="/etc/nginx/sites-available" + nginx_sites_enabled="/etc/nginx/sites-enabled" + + if [ -f "$nginx_sites_available/$PROJECT_NAME" ]; then + sudo mv "$nginx_sites_available/$PROJECT_NAME" "$nginx_sites_available/$PROJECT_NAME.$date.bak" + fi + sudo cp "$GENERATED/$NGINX_CONF" "$nginx_sites_available/$PROJECT_NAME" + + default_link="$nginx_sites_enabled/default" + if [ -L "${default_link}" ]; then + rm "$default_link" + fi + + sudo ln -fs "$nginx_sites_available/$PROJECT_NAME" "$nginx_sites_enabled/$PROJECT_NAME" + + # Restart Nginx to apply the changes + sudo systemctl start nginx + + echo "GOOD JOB, YOU ARE NOW RUNNING THE APP IN DEV MODE that mimics production." + echo "Running services:" + + echo "VISIT http://$DOMAIN:$HTTP_PORT via installed nginx proxy " + # echo " or http://$DOMAIN:$APP_PORT, directly via installed uwsgi server" + +fi + +cleanup diff --git a/.d4g-tools/bin/taipy/test.load.py.bak b/.d4g-tools/bin/taipy/test.load.py.bak new file mode 100644 index 0000000..56ad13e --- /dev/null +++ b/.d4g-tools/bin/taipy/test.load.py.bak @@ -0,0 +1,67 @@ +from locust import HttpUser, between, task + + +class HomeTest(HttpUser): + host = "https://carbonbombs-git-loadtesting-carbonbombs.vercel.app" + + headers = { + "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0", + "Accept": "*/*", + "Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", + "Accept-Encoding": "gzip, deflate, br", + "Referer": "https://carbonbombs-git-loadtesting-carbonbombs.vercel.app/map", + "Content-Type": "text/plain;charset=UTF-8", + "Origin": "https://carbonbombs-git-loadtesting-carbonbombs.vercel.app", + "Connection": "keep-alive", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + "TE": "trailers", + } + + # Set the time to wait between two requests for one user + wait_time = between(1, 5) + + def on_start(self): + """ + On start we clear the API cache + """ + pass + + @task + def index(self): + self.client.get("/") + + @task + def map(self): + self.client.get("/map") + payload = { + "query": "MATCH (p:carbon_bomb) WITH collect(properties(p)) as bombs RETURN bombs" + } + with self.client.post( + "/api/neo4j", + json=payload, + catch_response=True, + headers=self.headers, + ) as response: + if response.status_code != 200: + response.failure( + f"map - Response code not 200 : {response.status_code} {response}" + ) + + @task + def filter_map(self): + payload = { + "query": 'MATCH (p:carbon_bomb)-[:OPERATES]-(c:company) WHERE c.name IN ["TotalEnergies SE"] WITH collect(properties(p)) as bombs RETURN bombs' + } + + with self.client.post( + "/api/neo4j", + json=payload, + catch_response=True, + headers=self.headers, + ) as response: + if response.status_code != 200: + response.failure( + f"filter_map - Response code not 200 : {response.status_code}" + ) diff --git a/.d4g-tools/bin/taipy/uwsgi.ini b/.d4g-tools/bin/taipy/uwsgi.ini new file mode 100644 index 0000000..ea6a84e --- /dev/null +++ b/.d4g-tools/bin/taipy/uwsgi.ini @@ -0,0 +1,63 @@ +[uwsgi] +# http-websockets = true +chmod-socket = 777 +module = app.main:web_app +master = true +gevent = 1000 + +need-app = true ; Start fails if app not loaded +strict = true ; Enable strict mode to fully respect config +enable-threads = true ; Multithreading +vacuum = true ; Delete sockets during shutdown +single-interpreter = true ; Enable single-interpreter mode +die-on-term = true ; Shutdown when receiving SIGTERM (default is respawn) +no-orphans = true ; automatically kill workers if master dies + +;disable-logging = true ; Disable built-in logging +;log-4xx = true ; but log 4xx's anyway +;log-5xx = true ; and 5xx's +;harakiri = 60 ; forcefully kill workers after ... seconds +; py-callos-afterfork = true ; allow workers to trap signals (UNKNOWN in strict mode) + +log-4xx = true ; but log 4xx's anyway +log-5xx = true ; and 5xx's + +# workers = 8 ; Number of workers (=processes) to spawn +# cheaper-algo = spare +# cheaper = 2 ; Minimum number of workers allowed +# cheaper-initial = 2 ; Workers created at startup +# cheaper-step = 2 ; How many workers to spawn at a time +# cheaper-overload = 30 ; Length of a cycle in seconds +# worker-reload-mercy = 60 ; How long to wait before forcefully killing workers in seconds + +; max-requests = 1000 ; Restart workers after this many requests +; max-worker-lifetime = 3600 ; Restart workers after this many seconds +; reload-on-rss = 2048 ; Restart workers after this much resident memory + +## Setting memory limits +# soft limit will prevent cheaper from spawning new workers +# if workers total rss memory is equal or higher +# we use 128MB soft limit below (values are in bytes) +;cheaper-rss-limit-soft = 134217728 + +# Optional: hard limit will force cheaper to cheap single worker +# if workers total rss memory is equal or higher +# we use 160MB hard limit below (values are in bytes) +;;cheaper-rss-limit-hard = 167772160 + + +[prod] +ini = :uwsgi +# http-socket = :5000 +logto = %d/uwsgi.prod.log +disable-logging = true ; Disable built-in logging + +[dev] +ini = :uwsgi +# http-socket = :5001 +logto = %d/uwsgi.dev.log +;DEBUG +disable-logging = false +memory-report = true +; stats = :9000 ; Stats server +; stats-http = true diff --git a/.d4g-tools/bin/uwsgi/uwsgi.ini b/.d4g-tools/bin/uwsgi/uwsgi.ini new file mode 100644 index 0000000..baa7a9b --- /dev/null +++ b/.d4g-tools/bin/uwsgi/uwsgi.ini @@ -0,0 +1,63 @@ +[uwsgi] +; http-websockets = true +; module = app.main:web_app +chmod-socket = 777 +master = true +gevent = 1000 + +need-app = true ; Start fails if app not loaded +strict = true ; Enable strict mode to fully respect config +enable-threads = true ; Multithreading +vacuum = true ; Delete sockets during shutdown +single-interpreter = true ; Enable single-interpreter mode +die-on-term = true ; Shutdown when receiving SIGTERM (default is respawn) +no-orphans = true ; automatically kill workers if master dies + +;disable-logging = true ; Disable built-in logging +;log-4xx = true ; but log 4xx's anyway +;log-5xx = true ; and 5xx's +;harakiri = 60 ; forcefully kill workers after ... seconds +; py-callos-afterfork = true ; allow workers to trap signals (UNKNOWN in strict mode) + +log-4xx = true ; but log 4xx's anyway +log-5xx = true ; and 5xx's + +; workers = 8 ; Number of workers (=processes) to spawn +; cheaper-algo = spare +; cheaper = 2 ; Minimum number of workers allowed +; cheaper-initial = 2 ; Workers created at startup +; cheaper-step = 2 ; How many workers to spawn at a time +; cheaper-overload = 30 ; Length of a cycle in seconds +; worker-reload-mercy = 60 ; How long to wait before forcefully killing workers in seconds + +; max-requests = 1000 ; Restart workers after this many requests +; max-worker-lifetime = 3600 ; Restart workers after this many seconds +; reload-on-rss = 2048 ; Restart workers after this much resident memory + +## Setting memory limits +# soft limit will prevent cheaper from spawning new workers +# if workers total rss memory is equal or higher +# we use 128MB soft limit below (values are in bytes) +;cheaper-rss-limit-soft = 134217728 + +# Optional: hard limit will force cheaper to cheap single worker +# if workers total rss memory is equal or higher +# we use 160MB hard limit below (values are in bytes) +;;cheaper-rss-limit-hard = 167772160 + + +[prod] +ini = :uwsgi +# http-socket = :5000 +logto = %d/uwsgi.prod.log +disable-logging = true ; Disable built-in logging + +[dev] +ini = :uwsgi +# http-socket = :5001 +logto = %d/uwsgi.dev.log +;DEBUG +disable-logging = false +memory-report = true +; stats = :9000 ; Stats server +; stats-http = true diff --git a/.d4g-tools/bin/uwsgi/uwsgi.sh b/.d4g-tools/bin/uwsgi/uwsgi.sh new file mode 100755 index 0000000..7df1b9d --- /dev/null +++ b/.d4g-tools/bin/uwsgi/uwsgi.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 + +set -Eeuo pipefail + +RUN_DIR="$(dirname "${BASH_SOURCE[0]}")" +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +# Check if the virtual environment exists in the project directory +if [ ! -d "$VENV_DIR" ]; then + echo "Virtual environment not found in project directory!" + exit 1 +fi + +# Update the package lists for upgrades and new package installations +sudo apt update -y >/dev/null 2>&1 +# Ensure necessary dependencies are installed +sudo apt install build-essential python3-dev libssl-dev >/dev/null 2>&1 + +# Activate the virtual environment +source "$VENV_DIR"/bin/activate +# Install uwsgi and gevent using pip +pip install uwsgi gevent >/dev/null 2>&1 +# not recommended: asyncio greenlet +# Deactivate the virtual environment +deactivate + +#--http-socket :$APP_PORT +UWSGI_SERVICE="$PROJECT_NAME.$STAGE.uwsgi.service" +# Stop the uWSGI service +sudo systemctl stop "$UWSGI_SERVICE" &>/dev/null + +# Generate a Systemd file for uWSGI +echo """ +[Unit] +Description=$PROJECT_NAME UWSGI Server +After=syslog.target + +[Service] +ExecStart=$VENV_DIR/bin/uwsgi --ini $BIN_DIR/uwsgi/uwsgi.ini:$STAGE --socket /tmp/$PROJECT_NAME.$STAGE.uwsgi.sock --module $PROJECT_NAME.main:web_app +# ExecStart=$VENV_DIR/bin/uwsgi --ini $BIN_DIR/uwsgi/uwsgi.ini:$STAGE --http-socket :$APP_PORT +WorkingDirectory=$PROJECT_DIR +Restart=always +KillSignal=SIGINT +Type=notify +StandardError=syslog +NotifyAccess=all +User=$(whoami) + +[Install] +WantedBy=multi-user.target +""" >"$GENERATED/$UWSGI_SERVICE" + +# Move the Systemd file to the correct directory +sudo cp "$GENERATED/$UWSGI_SERVICE" "/etc/systemd/system/$UWSGI_SERVICE" +sudo systemctl daemon-reload + +# Start the uWSGI service +sudo systemctl start "$UWSGI_SERVICE" + +# Enable the uWSGI service to start on boot +# sudo systemctl enable "$UWSGI_SERVICE" + +# Create a self-signed SSL certificate +# sudo openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/certs/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -sha256 -days 3650 -nodes -subj "/CN=localhost" +# Update the Nginx configuration to expose the application + +NGINX_CONF="$PROJECT_NAME.$STAGE.nginx.conf" + +if [ "$PROD" == "false" ]; then + echo """ + server { + listen $HTTP_PORT; + server_name localhost; + + location / { + include uwsgi_params + uwsgi_pass unix:/tmp/$PROJECT_NAME.$STAGE.uwsgi.sock; + } + + }""" | sudo tee "$GENERATED/$NGINX_CONF" + + "$BIN_DIR"/nginx/nginx.sh +fi + +cleanup diff --git a/.d4g-tools/lib/.from-scratch-sh b/.d4g-tools/lib/.from-scratch-sh new file mode 100644 index 0000000..a0f3fae --- /dev/null +++ b/.d4g-tools/lib/.from-scratch-sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 + +set -Eeuo pipefail + +# IMPORTANT AND NECESSARY: Load dependencies +source "$LIB_DIR"/common.sh + +usage() { + cat </dev/null && pwd -P) + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose) + DEBUG="true" + ;; + --dummy-flag*) + DUMMY_FLAG="true" + ;; + --dummy-param=*) + DUMMY_PARAM="${1#*=}" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + return 0 +} + +parse_params "$@" + +echo -n "Ready to rumble." diff --git a/.d4g-tools/lib/.from-scratch-sh.initial b/.d4g-tools/lib/.from-scratch-sh.initial new file mode 100644 index 0000000..940d889 --- /dev/null +++ b/.d4g-tools/lib/.from-scratch-sh.initial @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +trap cleanup SIGINT SIGTERM ERR EXIT + +usage() { + cat < /dev/null +} + +parse_params() { + if [ $# -gt 2 ]; then + echo "Too many parameters provided" + usage + fi + + # Sane defaults + DEBUG="false" + RUN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + + while :; do + case "${1-}" in + -h | --help) + usage + ;; + -v | --verbose) + DEBUG="true" + ;; + --dummy-flag*) + DUMMY_FLAG="true" + ;; + --dummy-param=*) + DUMMY_PARAM="${1#*=}" + ;; + -?*) + echo "Unknown option: $1" + usage + ;; + *) + break + ;; + esac + shift + done + + return 0 +} + +setup_colors +parse_params "$@" + + + + +echo -n "Ready to rumble." diff --git a/.d4g-tools/lib/common.sh b/.d4g-tools/lib/common.sh new file mode 100755 index 0000000..3607d77 --- /dev/null +++ b/.d4g-tools/lib/common.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 + +set -Eeuo pipefail + +# IMPORTANT AND NECESSARY: Load dependencies +source "$LIB_DIR"/depends.sh + +# cleanup on exit +cleanup() { + trap - SIGINT SIGTERM SIGQUIT EXIT + # script cleanup here + exit 0 +} + +# Handle error on signal ERR +err() { + error "Error occurred in $0: line $1" + error "$(awk 'NR>L-1 && NR>> ":""),$0 }' L="$1" "$0")" +} + +# Trap cleanup and err functions, signals +trap 'err "${LINENO}"' ERR +trap cleanup SIGINT SIGTERM SIGQUIT EXIT + +# Function to check if a command is available +command_exists() { + command -v "$1" &>/dev/null +} + +setup_colors() { + if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then + NOCOLOR='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m' + else + NOCOLOR='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW='' + fi +} +# Actually set colors +setup_colors + +info() { + echo -e "${GREEN}[INFO] $*${NOCOLOR}" +} + +warning() { + echo -e "${YELLOW}[WARN] $*${NOCOLOR}" +} + +error() { + echo -e "${RED}[ERROR] $*${NOCOLOR}" >&2 +} + +debug() { + if [ "$DEBUG" == 'true' ]; then + echo -e "${BLUE}[DEBUG] $*${NOCOLOR}" + fi +} + +# TODO: should we use gum (see depends.sh) +# info() { +# gum style --foreground=4 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +# } + +# warning() { +# gum style --foreground=3 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +# } + +# success() { +# gum style --bold --foreground=2 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +# } + +# error() { +# gum style --bold --foreground=1 "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +# } + +# debug() { +# if [ "$DEBUG" == 'true' ]; then +# gum style --faint "$(date +"%Y-%m-%dT%H:%M:%S%:z") $*" +# fi +# } + +declare case_sensitive_sections=true +declare case_sensitive_keys=true +declare show_config_warnings=false +declare show_config_errors=true + +source "$LIB_DIR"/ini_parser.sh +source "$LIB_DIR"/utils.sh + +# Function to read ini file located in $PROJECT_DIR +read_ini() { + debug "Reading INI file: $1" + process_ini_file "$1" + export_config_to_env "default" + display_config_by_section "default" + export_config_to_env "dev" + display_config_by_section "dev" +} + +if [ -z "${INI_FILE}" ]; then + error "INI file 'd4g.ini' not found" + exit 1 +fi + +if [ -z "$D4G" ]; then + + read_ini "$INI_FILE" + + export D4G="true" +fi diff --git a/.d4g-tools/lib/depends.sh b/.d4g-tools/lib/depends.sh new file mode 100755 index 0000000..c3f31a9 --- /dev/null +++ b/.d4g-tools/lib/depends.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# Dependencies used by d4g-tools +set -Eeuo pipefail + +# if ! command -v brew &>/dev/null; then +# echo "Installing Homebrew 'OS: $OSTYPE'" +# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +# Script to recognize OS and install Homebrew if it doesn't exist +# if [[ "$OSTYPE" == "linux-gnu"* ]]; then +# # Linux or WSL +# # shellcheck disable=SC2016 +# echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >>/home/"$USER"/.profile +# eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" +# fi +# fi + +# Install crudini to simply read ini files +# if ! command -v crudini &>/dev/null; then +# brew install crudini +# fi + +# Install gum to format messages and more +# sudo mkdir -p /etc/apt/keyrings +# curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg +# echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list +# sudo apt install gum + +# Start by making sure node is installed +# we won't install because the user might have strong opinions about how to install node +if ! command -v node &>/dev/null; then + error "Please install node >=18 before running this script." + exit 1 +fi +# Make sure node version is >18 +NODE_VERSION=$(node -v) +if [ "$(echo "$NODE_VERSION" | cut -c 2-3)" -lt 18 ]; then + error "Please install node version 18 or higher before running this script." + exit 1 +fi + +# Install brew if not installed. +# We will use brew to install dependencies on macOS and linux +if ! command -v brew &>/dev/null; then + error "Please install brew before running this script. Brew is compatible with macOS, linux, and WSL2 (https://docs.brew.sh/Homebrew-on-Linux)." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + # exit 1 +fi + +# # Technically bitwarden-cli is available on brew, +# # but it still depends on node, so we will install it using npm. +# if ! command -v bw &>/dev/null; then +# info "Installing bw cli" +# npm install -g @bitwarden/cli +# fi + +# TODO lefthook?? +for package in jq gum lefthook; do + if ! command -v $package &>/dev/null; then + info "Installing $package" + brew install $package + else + success "$package is already installed" + fi +done + +# Install curl +if ! command -v curl &>/dev/null; then + if ! command -v brew &>/dev/null; then + sudo apt install -y curl &>/dev/null + else + brew install curl &>/dev/null + fi +fi + +# Install gnuplot +if ! command -v gnuplot &>/dev/null; then + if ! command -v brew &>/dev/null; then + sudo apt install -y gnuplot &>/dev/null + else + brew install gnuplot &>/dev/null + fi +fi + +# TODO loop between messages (info, debug,...) and dependencies => to discuss with pg +# info "Installed all dependencies, preparing repo." + +# # Install the pre-commit hooks +# lefthook install + +# # Prompt user to create .env +# gum confirm "Would you like to create the .env file from .env.dist ? This *WILL* overwrite your existing .env file." && cp .env.dist .env + +# tfenv install +# tfenv use + +# # All done. +# success "All done ! Please refer to the README.md for further instructions." diff --git a/.d4g-tools/lib/ini_parser.sh b/.d4g-tools/lib/ini_parser.sh new file mode 100755 index 0000000..7f35fa7 --- /dev/null +++ b/.d4g-tools/lib/ini_parser.sh @@ -0,0 +1,388 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 +set -Eeuo pipefail + +# Based on https://github.com/DevelopersToolbox/ini-file-parser +# -------------------------------------------------------------------------------- # +# Description # +# -------------------------------------------------------------------------------- # +# A 'complete' ini file parsers written in pure bash (4), it was written for no # +# other reason that one did not exist. It is completely pointless apart from some # +# clever tricks. # +# -------------------------------------------------------------------------------- # + +# -------------------------------------------------------------------------------- # +# Global Variables # +# -------------------------------------------------------------------------------- # +# Global variables which can be set by the calling script, but need to be declared # +# here also to ensure the script is clean and error free. # +# # +# case_sensitive_sections - should section names be case sensitive # +# case_sensitive_keys - should key names be case sensitive # +# show_config_warnings - should we show config warnings # +# show_config_errors - should we show config errors # +# -------------------------------------------------------------------------------- # + +declare case_sensitive_sections +declare case_sensitive_keys +declare show_config_warnings +declare show_config_errors + +# -------------------------------------------------------------------------------- # +# Default Section # +# -------------------------------------------------------------------------------- # +# Any values that are found outside of a defined section need to be put somewhere # +# so they can be recalled as needed. Sections is set up with a 'default' for this # +# purpose. # +# -------------------------------------------------------------------------------- # + +DEFAULT_SECTION='default' + +sections=("${DEFAULT_SECTION}") + +# -------------------------------------------------------------------------------- # +# Local Variables # +# -------------------------------------------------------------------------------- # +# The local variables which can be overridden by the global variables above. # +# # +# local_case_sensitive_sections - should section names be case sensitive # +# local_case_sensitive_keys - should key names be case sensitive # +# local_show_config_warnings - should we show config warnings # +# local_show_config_errors - should we show config errors # +# -------------------------------------------------------------------------------- # + +local_case_sensitive_sections=true +local_case_sensitive_keys=true +local_show_config_warnings=true +local_show_config_errors=true + +# -------------------------------------------------------------------------------- # +# Set Global Variables # +# -------------------------------------------------------------------------------- # +# Check to see if the global overrides are set and if so, override the defaults. # +# # +# Error checking is in place to ensure that the override contains a valid value of # +# true or false, anything else is ignored. +# -------------------------------------------------------------------------------- # + +function setup_global_variables { + if [[ -n "${case_sensitive_sections}" ]] && [[ "${case_sensitive_sections}" = false || "${case_sensitive_sections}" = true ]]; then + local_case_sensitive_sections=${case_sensitive_sections} + fi + + if [[ -n "${case_sensitive_keys}" ]] && [[ "${case_sensitive_keys}" = false || "${case_sensitive_keys}" = true ]]; then + local_case_sensitive_keys=${case_sensitive_keys} + fi + + if [[ -n "${show_config_warnings}" ]] && [[ "${show_config_warnings}" = false || "${show_config_warnings}" = true ]]; then + local_show_config_warnings=${show_config_warnings} + fi + + if [[ -n "${show_config_errors}" ]] && [[ "${show_config_errors}" = false || "${show_config_errors}" = true ]]; then + local_show_config_errors=${show_config_errors} + fi +} + +# -------------------------------------------------------------------------------- # +# in Array # +# -------------------------------------------------------------------------------- # +# A function to check to see if a given value exists in a given array. # +# -------------------------------------------------------------------------------- # + +function in_array() { + local haystack="${1}[@]" + local needle=${2} + + for i in ${haystack}; do + if [[ ${i} == "${needle}" ]]; then + return 0 + fi + done + return 1 +} + +# -------------------------------------------------------------------------------- # +# Show Warning # +# -------------------------------------------------------------------------------- # +# A wrapper to display any configuration warnings, taking into account if the # +# local_show_config_warnings flag is set to true. # +# -------------------------------------------------------------------------------- # + +function show_warning() { + if [[ "${local_show_config_warnings}" = true ]]; then + format=$1 + shift + + # shellcheck disable=SC2059 + printf "[ WARNING ] ${format}" "$@" + fi +} + +# -------------------------------------------------------------------------------- # +# Show Error # +# -------------------------------------------------------------------------------- # +# A wrapper to display any configuration errors, taking into account if the # +# local_show_config_errorss flag is set to true. # +# -------------------------------------------------------------------------------- # + +function show_error() { + if [[ "${local_show_config_errors}" = true ]]; then + format=$1 + shift + + # shellcheck disable=SC2059 + printf "[ ERROR ] ${format}" "$@" >&2 + fi +} + +# -------------------------------------------------------------------------------- # +# Process Section Name # +# -------------------------------------------------------------------------------- # +# Once we have located a section name within the given config file, we need to # +# 'cleanse' the value. # +# -------------------------------------------------------------------------------- # + +function process_section_name() { + local section=$1 + + section="${section##*( )}" # Remove leading spaces + section="${section%%*( )}" # Remove trailing spaces + section=$(echo -e "${section}" | tr -s '[:punct:] [:blank:]' '_') # Replace all :punct: and :blank: with underscore and squish + section=$(echo -e "${section}" | sed 's/[^a-zA-Z0-9_]//g') # Remove non-alphanumberics (except underscore) + + if [[ "${local_case_sensitive_sections}" = false ]]; then + section=$(echo -e "${section}" | tr '[:upper:]' '[:lower:]') # Lowercase the section name + fi + echo "${section}" +} + +# -------------------------------------------------------------------------------- # +# Process Key Name # +# -------------------------------------------------------------------------------- # +# Once we have located a key name on a given line, we need to 'cleanse' the value. # +# -------------------------------------------------------------------------------- # + +function process_key_name() { + local key=$1 + + key="${key##*( )}" # Remove leading spaces + key="${key%%*( )}" # Remove trailing spaces + key=$(echo -e "${key}" | tr -s '[:punct:] [:blank:]' '_') # Replace all :punct: and :blank: with underscore and squish + key=$(echo -e "${key}" | sed 's/[^a-zA-Z0-9_]//g') # Remove non-alphanumberics (except underscore) + + if [[ "${local_case_sensitive_keys}" = false ]]; then + key=$(echo -e "${key}" | tr '[:upper:]' '[:lower:]') # Lowercase the section name + fi + echo "${key}" +} + +# -------------------------------------------------------------------------------- # +# Process Value # +# -------------------------------------------------------------------------------- # +# Once we have located a value attached to a key, we need to 'cleanse' the value. # +# -------------------------------------------------------------------------------- # + +function process_value() { + local value=$1 + + value="${value%%\;*}" # Remove in line right comments + value="${value%%\#*}" # Remove in line right comments + value="${value##*( )}" # Remove leading spaces + value="${value%%*( )}" # Remove trailing spaces + + value=$(escape_string "${value}") + + echo "${value}" +} + +# -------------------------------------------------------------------------------- # +# Escape string # +# -------------------------------------------------------------------------------- # +# Replace ' with SINGLE_QUOTE to avoid issues with eval. # +# -------------------------------------------------------------------------------- # + +function escape_string() { + local clean + + clean=${1//\'/SINGLE_QUOTE} + echo "${clean}" +} + +# -------------------------------------------------------------------------------- # +# Un-Escape string # +# -------------------------------------------------------------------------------- # +# Convert SINGLE_QUOTE back to ' when returning the value to the caller. # +# -------------------------------------------------------------------------------- # + +function unescape_string() { + local orig + + orig=${1//SINGLE_QUOTE/\'} + echo "${orig}" +} + +# -------------------------------------------------------------------------------- # +# Parse ini file # +# -------------------------------------------------------------------------------- # +# Read a named file line by line and process as required. # +# -------------------------------------------------------------------------------- # + +function process_ini_file() { + local line_number=0 + local section="${DEFAULT_SECTION}" + local key_array_name='' + + setup_global_variables + + shopt -s extglob + + while read -r line; do + line_number=$((line_number + 1)) + + if [[ ${line} =~ ^# || ${line} =~ ^\; || -z ${line} ]]; then # Ignore comments / empty lines + continue + fi + + if [[ ${line} =~ ^"["(.+)"]"$ ]]; then # Match pattern for a 'section' + section=$(process_section_name "${BASH_REMATCH[1]}") + + if ! in_array sections "${section}"; then + eval "${section}_keys=()" # Use eval to declare the keys array + eval "${section}_values=()" # Use eval to declare the values array + sections+=("${section}") # Add the section name to the list + fi + elif [[ ${line} =~ ^(.*)"="(.*) ]]; then # Match patter for a key=value pair + key=$(process_key_name "${BASH_REMATCH[1]}") + value=$(process_value "${BASH_REMATCH[2]}") + + if [[ -z ${key} ]]; then + show_error 'line %d: No key name\n' "${line_number}" + elif [[ -z ${value} ]]; then + show_error 'line %d: No value\n' "${line_number}" + else + if [[ "${section}" == "${DEFAULT_SECTION}" ]]; then + show_warning '%s=%s - Defined on line %s before first section - added to "%s" group\n' "${key}" "${value}" "${line_number}" "${DEFAULT_SECTION}" + fi + + eval key_array_name="${section}_keys" + + if in_array "${key_array_name}" "${key}"; then + show_warning 'key %s - Defined multiple times within section %s\n' "${key}" "${section}" + fi + eval "${section}_keys+=(${key})" # Use eval to add to the keys array + eval "${section}_values+=('${value}')" # Use eval to add to the values array + eval "${section}_${key}='${value}'" # Use eval to declare a variable + fi + fi + done <"$1" +} + +# -------------------------------------------------------------------------------- # +# Get Value # +# -------------------------------------------------------------------------------- # +# Retrieve a value for a specific key from a named section. # +# -------------------------------------------------------------------------------- # + +function get_value() { + local section='' + local key='' + local value='' + local keys='' + local values='' + + section=$(process_section_name "${1}") + key=$(process_key_name "${2}") + + eval "keys=( \"\${${section}_keys[@]}\" )" + eval "values=( \"\${${section}_values[@]}\" )" + + for i in "${!keys[@]}"; do + if [[ "${keys[${i}]}" = "${key}" ]]; then + orig=$(unescape_string "${values[${i}]}") + printf '%s' "${orig}" + fi + done +} + +# -------------------------------------------------------------------------------- # +# Display Config # +# -------------------------------------------------------------------------------- # +# Display all of the post processed configuration. # +# # +# NOTE: This is without comments etec. # +# -------------------------------------------------------------------------------- # + +function display_config() { + local section='' + local key='' + local value='' + + for s in "${!sections[@]}"; do + section=${sections[${s}]} + + printf '[%s]\n' "${section}" + + eval "keys=( \"\${${section}_keys[@]}\" )" + eval "values=( \"\${${section}_values[@]}\" )" + + for i in "${!keys[@]}"; do + orig=$(unescape_string "${values[${i}]}") + printf '%s=%s\n' "${keys[${i}]}" "${orig}" + done + printf '\n' + done +} + +# -------------------------------------------------------------------------------- # +# Display Config by Section # +# -------------------------------------------------------------------------------- # +# Display all of the post processed configuration for a given section. # +# # +# NOTE: This is without comments etec. # +# -------------------------------------------------------------------------------- # + +function display_config_by_section() { + local section=$1 + local key='' + local value='' + local keys='' + local values='' + + printf '[%s]\n' "${section}" + + eval "keys=( \"\${${section}_keys[@]}\" )" + eval "values=( \"\${${section}_values[@]}\" )" + + for i in "${!keys[@]}"; do + orig=$(unescape_string "${values[${i}]}") + printf '%s=%s\n' "${keys[${i}]}" "${orig}" + done + printf '\n' +} + +# D4G: Function to export section config to env variables +function export_config_to_env() { + local section=$1 + local key='' + local value='' + local keys='' + local values='' + + eval "keys=( \"\${${section}_keys[@]}\" )" + eval "values=( \"\${${section}_values[@]}\" )" + + for i in "${!keys[@]}"; do + orig=$(unescape_string "${values[${i}]}") + export "${keys[${i}]}"="${orig//\"/}" + done + +} + +export -f export_config_to_env +# -------------------------------------------------------------------------------- # +# End of Script # +# -------------------------------------------------------------------------------- # +# This is the end - nothing more to see here. # +# -------------------------------------------------------------------------------- # diff --git a/.d4g-tools/lib/platforms.ini b/.d4g-tools/lib/platforms.ini new file mode 100644 index 0000000..3aa1465 --- /dev/null +++ b/.d4g-tools/lib/platforms.ini @@ -0,0 +1,9 @@ +# Not used (TODO? or hard code in "platform".sh) +; [taipy] +; python=true +; uwsgi=true +; nginx=true + +; [streamlit] + +; [vercel] diff --git a/.d4g-tools/lib/user-agents.json b/.d4g-tools/lib/user-agents.json new file mode 100644 index 0000000..540ca56 --- /dev/null +++ b/.d4g-tools/lib/user-agents.json @@ -0,0 +1,26 @@ +[ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/125.0.2535.92", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 14.5; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 14.5; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/111.0.0.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/125.0.2535.92", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edge/44.18363.8131", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/111.0.0.0", + "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/111.0.0.0", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "Mozilla/5.0 (X11; Linux i686; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0", + "Mozilla/5.0 (X11; Linux i686; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:115.0) Gecko/20100101 Firefox/115.0", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/111.0.0.0" +] diff --git a/.d4g-tools/lib/utils.sh b/.d4g-tools/lib/utils.sh new file mode 100755 index 0000000..da5edd2 --- /dev/null +++ b/.d4g-tools/lib/utils.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 +# shellcheck disable=SC2034 + +set -Eeuo pipefail + +# Function to replace @@var with its corresponding environment variable value +replace_env_vars() { + template_file=$1 + output_file="${1%.dist}" + temp_file="temp.txt" + rm -f $temp_file + while IFS= read -r line; do + for word in $line; do + if [[ $word == @@* ]]; then + var_name=${word#@@} + var_value=${!var_name} + if [ -n "$var_value" ]; then + line=${line/$word/$var_value} + fi + fi + done + echo "$line" >>$temp_file + done <"$template_file" + mv $temp_file "$output_file" +} + +# Export the function so it can be used in other scripts +export -f replace_env_vars diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ca248b8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,ipynb}] +indent_size = 4 +max_line_length = 96 + +[*.{js,ts}] +indent_size = 4 + +[*.{md,mdx}] +max_line_length = off +trim_trailing_whitespace = false + +[*.{html,css,yml,yaml,json,toml,tml}] +indent_size = 2 diff --git a/.github/workflows/d4g-utils.yml b/.github/workflows/d4g-utils.yml index 3ce96b5..0779e73 100644 --- a/.github/workflows/d4g-utils.yml +++ b/.github/workflows/d4g-utils.yml @@ -3,19 +3,20 @@ name: Copy File to Organization Repos on: push: paths: - - 'd4g-utils/**' # Trigger the workflow only when changes are made in the d4g-utils directory + - ".d4g-utils/**" # Trigger the workflow only when changes are made in the d4g-utils directory branches: - main - + # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: copy: runs-on: ubuntu-latest + if: ${{ contains(github.event.repository.name, 'd4g-project-template') }} steps: - name: Checkout template repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Git run: | @@ -31,7 +32,7 @@ jobs: # Fetch repositories from the organization repos=`gh repo list $ORG_NAME --limit 500 | awk ' {print $1}'` - + echo "::set-output name=repos::$repos" env: GH_TOKEN: ${{ secrets.D4GTECH_TOKEN }} @@ -49,20 +50,19 @@ jobs: done env: GH_TOKEN: ${{ secrets.D4GTECH_TOKEN }} - - + # for repo in ${{ steps.org_repos.outputs.repos }}; do # # Clone the repository # git clone "https://github.com/$repo" target_repo - + # # Copy all files from d4g-utils to target repository d4g-utlis # rsync -av --exclude='.git' d4g-utils/ target_repo/d4g-utils - + # # Commit and push changes # cd target_repo # git add . # git commit -m "Copy files from d4g-utils directory" # git push origin main - + # cd .. # done diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 639cfeb..7b1cec3 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -3,23 +3,28 @@ name: pre-commit on: pull_request: push: - branches: [main] jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - - name: Install poetry - run: | - python -m pip install poetry - poetry export --with dev --format=requirements.txt --output=requirements.txt - - name: set PY - run: echo "PY=$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV - - uses: actions/cache@v1 - with: - path: ~/.cache/pre-commit - key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} - - uses: pre-commit/action@v3.0.0 \ No newline at end of file + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: "poetry" + - name: Install poetry + run: | + python -m pip install poetry + # poetry export --with dev --format=requirements.txt --output=requirements.txt + - name: set PY + run: echo "PY=$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV + # - uses: actions/cache@v4 + # with: + # path: ~/.cache/pre-commit + # key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} + - uses: pre-commit/action@v3.0.1 + # - uses: pre-commit-ci/lite-action@v1.0.2 + # if: always() diff --git a/.gitignore b/.gitignore index b8fb0eb..f503de2 100644 --- a/.gitignore +++ b/.gitignore @@ -136,8 +136,10 @@ venv.bak/ .spyderproject .spyproject -# VS Code -.vscode +# VS Code is default reco from D4G +# We use it to help linting and formatting code +# Config setting and Extension recommandations +#.vscode # Rope project settings .ropeproject @@ -159,5 +161,15 @@ dmypy.json # Cython debug symbols cython_debug/ +# D4G # Precommit hooks: ruff cache -.ruff_cache \ No newline at end of file +.ruff_cache + +# Used by d4g-tools +# DO NOT REMOVE ANY OF THESE + +!.d4g-tools/** +.generated +.gi-* +.log + diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9baa775..17bc6be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,58 @@ repos: - - repo: https://github.com/charliermarsh/ruff-pre-commit - # Ruff version. - rev: "v0.2.1" - hooks: - - id: ruff - args: [--fix] - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-merge-conflict - - id: mixed-line-ending - #- repo: https://github.com/pycqa/bandit - # rev: 1.7.4 - # hooks: - # - id: bandit - # exclude: tests/ - - repo: https://github.com/Lucas-C/pre-commit-hooks-safety - rev: v1.3.1 - hooks: - - id: python-safety-dependencies-check +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-merge-conflict + # - id: mixed-line-ending + # - id: end-of-file-fixer + # - id: check-ast + # - id: check-builtin-literals + # - id: check-yaml + # - id: check-toml + # - id: end-of-file-fixer + # - id: trailing-whitespace + # - repo: https://github.com/astral-sh/ruff-pre-commit + # rev: v0.4.9 + # hooks: + # - id: ruff + # types_or: [python, pyi, jupyter] + # args: [--fix] + # - id: ruff-format + # # args: [--config, format.docstring-code-format=true, --config, format.docstring-code-line-length=72] + # types_or: [python, pyi, jupyter] + + + + +# ------------------------------------------------------------------------------- +# OLD VERSION +# repos: + +# - repo: https://github.com/charliermarsh/ruff-pre-commit +# # Ruff version. +# rev: "v0.4.9" +# hooks: +# - id: ruff +# args: [--fix, --exit-zero, --config /home/nicog/_work/d4g/d4g-project-template/pyproject.toml] + +# - repo: https://github.com/pre-commit/pre-commit-hooks +# rev: v4.3.0 +# hooks: +# - id: check-merge-conflict +# - id: mixed-line-ending + +# - repo: https://github.com/charliermarsh/ruff-pre-commit +# # Ruff version. +# rev: "v0.4.9" +# hooks: +# - id: ruff-format + +# #- repo: https://github.com/pycqa/bandit +# # rev: 1.7.4 +# # hooks: +# # - id: bandit +# # exclude: tests/ +# - repo: https://github.com/Lucas-C/pre-commit-hooks-safety +# rev: v1.3.3 +# hooks: +# - id: python-safety-dependencies-check diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22511f7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + "recommendations": [ + "editorconfig.editorconfig", + //default + "esbenp.prettier-vscode", + //python, jupyter + "charliermarsh.ruff", + //tomml + "tamasfe.even-better-toml", + //shell + "foxundermoon.shell-format" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..82935ee --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,35 @@ +{ + "editor.formatOnSave": true, + "editor.tabSize": 4, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff" + }, + "[shellscript]": { + "editor.tabSize": 2, + "editor.defaultFormatter": "foxundermoon.shell-format" + }, + // "[yaml]": { + // "editor.defaultFormatter": "kennylong.kubernetes-yaml-formatter", + // "editor.tabSize": 4 + // }, + "css.validate": false, + "typescript.preferences.importModuleSpecifier": "non-relative", + + // Python + "ruff.configurationPreference": "filesystemFirst", + // "pre-commit-helper.runOnSave": "all hooks", + + "ruff.fixAll": true, + "ruff.lint.run": "onSave", + "ruff.format.run": "onSave", + + "evenBetterToml.formatter.allowedBlankLines": 2, + "evenBetterToml.formatter.columnWidth": 96, + "[toml]": { + "editor.defaultFormatter": "tamasfe.even-better-toml" + } +} diff --git a/LICENSE b/LICENSE index 2411975..9c8f01c 100644 --- a/LICENSE +++ b/LICENSE @@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/PROJECT_TO_RENAME/__init__.py b/PROJECT_TO_RENAME/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/PROJECT_TO_RENAME/__init__.py @@ -0,0 +1 @@ + diff --git a/PROJECT_TO_RENAME/python.py b/PROJECT_TO_RENAME/python.py new file mode 100644 index 0000000..aa00423 --- /dev/null +++ b/PROJECT_TO_RENAME/python.py @@ -0,0 +1,16 @@ +import math +from typing import Union + +selector_company: list[str] = [] +selected_year: Union[str, None] = None +selector_year: list[str] = [] +company_sector: Union[str, None] = None +company_upe_name: str = "" + + +def calculate_circumference(radius): + circumference = 2 * math.pi * radius + print(f"The circumference of the circle with radius {radius} is {circumference}") + + +calculate_circumference(10) diff --git a/PROJECT_TO_RENAME/test.py b/PROJECT_TO_RENAME/test.py new file mode 100644 index 0000000..b57c348 --- /dev/null +++ b/PROJECT_TO_RENAME/test.py @@ -0,0 +1,18 @@ +import math +from typing import Union + +from python import calculate_circumference + +selector_company: list[str] = [] +selected_year: Union[str, None] = None +selector_year: list[str] = [] +company_sector: Union[str, None] = None +company_upe_name: str = "" + + +def calculate_circumference(radius): + circumference = 2 * math.pi * radius + print(f"The circumference of the circle with radius {radius} is {circumference}") + + +calculate_circumference(10) diff --git a/d4g b/d4g new file mode 100755 index 0000000..4524eaf --- /dev/null +++ b/d4g @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +# shellcheck disable=SC1090 + +set -Eeuo pipefail +# trap cleanup SIGINT SIGTERM ERR EXIT + +# Sane defaults +DEBUG="true" +RUN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) + +# Export ENV vars for shell execution pipe +# Init root execution with d4g tag to empty string +export D4G="" +# This is the absolute path of the project root directory +export PROJECT_DIR="$RUN_DIR" +# Get the absolute path of the ini file +export INI_FILE="$PROJECT_DIR/d4g.ini" +# Get the absolute path of the .venv directory +export VENV_DIR="$PROJECT_DIR/.venv" +# Get the absolute path of the .d4g-tools directory +export D4G_TOOLS="$PROJECT_DIR/.d4g-tools" + +export LIB_DIR="$D4G_TOOLS/lib" +export BIN_DIR="$D4G_TOOLS/bin" + +export LOG_DIR="$D4G_TOOLS/.log" +mkdir -p "$LOG_DIR" &>/dev/null +export GENERATED="$D4G_TOOLS/.generated" +mkdir -p "$GENERATED" &>/dev/null + +# IMPORTANT AND NECESSARY: Load common functions +source "$LIB_DIR"/common.sh + +usage() { + cat <=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] + +[[package]] +name = "cycler" +version = "0.12.1" +description = "Composable style cycles" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, +] + +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + [[package]] name = "distlib" version = "0.3.8" @@ -57,13 +135,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] @@ -71,29 +149,94 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.15.3" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.15.3-py3-none-any.whl", hash = "sha256:0151273e5b5d6cf753a61ec83b3a9b7d8821c39ae9af9d7ecf2f9e2f17404103"}, + {file = "filelock-3.15.3.tar.gz", hash = "sha256:e1199bf5194a2277273dacd50269f0d87d0682088a3c561c15674ea9005d8635"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "fonttools" +version = "4.53.0" +description = "Tools to manipulate font files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.53.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:52a6e0a7a0bf611c19bc8ec8f7592bdae79c8296c70eb05917fd831354699b20"}, + {file = "fonttools-4.53.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:099634631b9dd271d4a835d2b2a9e042ccc94ecdf7e2dd9f7f34f7daf333358d"}, + {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e40013572bfb843d6794a3ce076c29ef4efd15937ab833f520117f8eccc84fd6"}, + {file = "fonttools-4.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715b41c3e231f7334cbe79dfc698213dcb7211520ec7a3bc2ba20c8515e8a3b5"}, + {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74ae2441731a05b44d5988d3ac2cf784d3ee0a535dbed257cbfff4be8bb49eb9"}, + {file = "fonttools-4.53.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:95db0c6581a54b47c30860d013977b8a14febc206c8b5ff562f9fe32738a8aca"}, + {file = "fonttools-4.53.0-cp310-cp310-win32.whl", hash = "sha256:9cd7a6beec6495d1dffb1033d50a3f82dfece23e9eb3c20cd3c2444d27514068"}, + {file = "fonttools-4.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:daaef7390e632283051e3cf3e16aff2b68b247e99aea916f64e578c0449c9c68"}, + {file = "fonttools-4.53.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a209d2e624ba492df4f3bfad5996d1f76f03069c6133c60cd04f9a9e715595ec"}, + {file = "fonttools-4.53.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f520d9ac5b938e6494f58a25c77564beca7d0199ecf726e1bd3d56872c59749"}, + {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eceef49f457253000e6a2d0f7bd08ff4e9fe96ec4ffce2dbcb32e34d9c1b8161"}, + {file = "fonttools-4.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1f3e34373aa16045484b4d9d352d4c6b5f9f77ac77a178252ccbc851e8b2ee"}, + {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:28d072169fe8275fb1a0d35e3233f6df36a7e8474e56cb790a7258ad822b6fd6"}, + {file = "fonttools-4.53.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4a2a6ba400d386e904fd05db81f73bee0008af37799a7586deaa4aef8cd5971e"}, + {file = "fonttools-4.53.0-cp311-cp311-win32.whl", hash = "sha256:bb7273789f69b565d88e97e9e1da602b4ee7ba733caf35a6c2affd4334d4f005"}, + {file = "fonttools-4.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:9fe9096a60113e1d755e9e6bda15ef7e03391ee0554d22829aa506cdf946f796"}, + {file = "fonttools-4.53.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d8f191a17369bd53a5557a5ee4bab91d5330ca3aefcdf17fab9a497b0e7cff7a"}, + {file = "fonttools-4.53.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93156dd7f90ae0a1b0e8871032a07ef3178f553f0c70c386025a808f3a63b1f4"}, + {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bff98816cb144fb7b85e4b5ba3888a33b56ecef075b0e95b95bcd0a5fbf20f06"}, + {file = "fonttools-4.53.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:973d030180eca8255b1bce6ffc09ef38a05dcec0e8320cc9b7bcaa65346f341d"}, + {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4ee5a24e281fbd8261c6ab29faa7fd9a87a12e8c0eed485b705236c65999109"}, + {file = "fonttools-4.53.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd5bc124fae781a4422f61b98d1d7faa47985f663a64770b78f13d2c072410c2"}, + {file = "fonttools-4.53.0-cp312-cp312-win32.whl", hash = "sha256:a239afa1126b6a619130909c8404070e2b473dd2b7fc4aacacd2e763f8597fea"}, + {file = "fonttools-4.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:45b4afb069039f0366a43a5d454bc54eea942bfb66b3fc3e9a2c07ef4d617380"}, + {file = "fonttools-4.53.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:93bc9e5aaa06ff928d751dc6be889ff3e7d2aa393ab873bc7f6396a99f6fbb12"}, + {file = "fonttools-4.53.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2367d47816cc9783a28645bc1dac07f8ffc93e0f015e8c9fc674a5b76a6da6e4"}, + {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:907fa0b662dd8fc1d7c661b90782ce81afb510fc4b7aa6ae7304d6c094b27bce"}, + {file = "fonttools-4.53.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e0ad3c6ea4bd6a289d958a1eb922767233f00982cf0fe42b177657c86c80a8f"}, + {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:73121a9b7ff93ada888aaee3985a88495489cc027894458cb1a736660bdfb206"}, + {file = "fonttools-4.53.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ee595d7ba9bba130b2bec555a40aafa60c26ce68ed0cf509983e0f12d88674fd"}, + {file = "fonttools-4.53.0-cp38-cp38-win32.whl", hash = "sha256:fca66d9ff2ac89b03f5aa17e0b21a97c21f3491c46b583bb131eb32c7bab33af"}, + {file = "fonttools-4.53.0-cp38-cp38-win_amd64.whl", hash = "sha256:31f0e3147375002aae30696dd1dc596636abbd22fca09d2e730ecde0baad1d6b"}, + {file = "fonttools-4.53.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d6166192dcd925c78a91d599b48960e0a46fe565391c79fe6de481ac44d20ac"}, + {file = "fonttools-4.53.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef50ec31649fbc3acf6afd261ed89d09eb909b97cc289d80476166df8438524d"}, + {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f193f060391a455920d61684a70017ef5284ccbe6023bb056e15e5ac3de11d1"}, + {file = "fonttools-4.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba9f09ff17f947392a855e3455a846f9855f6cf6bec33e9a427d3c1d254c712f"}, + {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c555e039d268445172b909b1b6bdcba42ada1cf4a60e367d68702e3f87e5f64"}, + {file = "fonttools-4.53.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a4788036201c908079e89ae3f5399b33bf45b9ea4514913f4dbbe4fac08efe0"}, + {file = "fonttools-4.53.0-cp39-cp39-win32.whl", hash = "sha256:d1a24f51a3305362b94681120c508758a88f207fa0a681c16b5a4172e9e6c7a9"}, + {file = "fonttools-4.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:1e677bfb2b4bd0e5e99e0f7283e65e47a9814b0486cb64a41adf9ef110e078f2"}, + {file = "fonttools-4.53.0-py3-none-any.whl", hash = "sha256:6b4f04b1fbc01a3569d63359f2227c89ab294550de277fd09d8fca6185669fa4"}, + {file = "fonttools-4.53.0.tar.gz", hash = "sha256:c93ed66d32de1559b6fc348838c7572d5c0ac1e4a258e76763a5caddd8944002"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.1.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + [[package]] name = "identify" -version = "2.5.33" +version = "2.5.36" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, - {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -110,55 +253,431 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "kiwisolver" +version = "1.4.5" +description = "A fast implementation of the Cassowary constraint solver" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, + {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, + {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, + {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, + {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, + {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, + {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, + {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, + {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, + {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, + {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, + {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, + {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, + {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, + {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, + {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, + {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, + {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, + {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, + {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, + {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, + {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, + {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, + {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, +] + +[[package]] +name = "matplotlib" +version = "3.9.0" +description = "Python plotting package" +optional = false +python-versions = ">=3.9" +files = [ + {file = "matplotlib-3.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2bcee1dffaf60fe7656183ac2190bd630842ff87b3153afb3e384d966b57fe56"}, + {file = "matplotlib-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f988bafb0fa39d1074ddd5bacd958c853e11def40800c5824556eb630f94d3b"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe428e191ea016bb278758c8ee82a8129c51d81d8c4bc0846c09e7e8e9057241"}, + {file = "matplotlib-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaf3978060a106fab40c328778b148f590e27f6fa3cd15a19d6892575bce387d"}, + {file = "matplotlib-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e7f03e5cbbfacdd48c8ea394d365d91ee8f3cae7e6ec611409927b5ed997ee4"}, + {file = "matplotlib-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:13beb4840317d45ffd4183a778685e215939be7b08616f431c7795276e067463"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:063af8587fceeac13b0936c42a2b6c732c2ab1c98d38abc3337e430e1ff75e38"}, + {file = "matplotlib-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a2fa6d899e17ddca6d6526cf6e7ba677738bf2a6a9590d702c277204a7c6152"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550cdda3adbd596078cca7d13ed50b77879104e2e46392dcd7c75259d8f00e85"}, + {file = "matplotlib-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cce0f31b351e3551d1f3779420cf8f6ec0d4a8cf9c0237a3b549fd28eb4abb"}, + {file = "matplotlib-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c53aeb514ccbbcbab55a27f912d79ea30ab21ee0531ee2c09f13800efb272674"}, + {file = "matplotlib-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5be985db2596d761cdf0c2eaf52396f26e6a64ab46bd8cd810c48972349d1be"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c79f3a585f1368da6049318bdf1f85568d8d04b2e89fc24b7e02cc9b62017382"}, + {file = "matplotlib-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bdd1ecbe268eb3e7653e04f451635f0fb0f77f07fd070242b44c076c9106da84"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e85a1a6d732f645f1403ce5e6727fd9418cd4574521d5803d3d94911038e5"}, + {file = "matplotlib-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a490715b3b9984fa609116481b22178348c1a220a4499cda79132000a79b4db"}, + {file = "matplotlib-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8146ce83cbc5dc71c223a74a1996d446cd35cfb6a04b683e1446b7e6c73603b7"}, + {file = "matplotlib-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:d91a4ffc587bacf5c4ce4ecfe4bcd23a4b675e76315f2866e588686cc97fccdf"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:616fabf4981a3b3c5a15cd95eba359c8489c4e20e03717aea42866d8d0465956"}, + {file = "matplotlib-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd53c79fd02f1c1808d2cfc87dd3cf4dbc63c5244a58ee7944497107469c8d8a"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06a478f0d67636554fa78558cfbcd7b9dba85b51f5c3b5a0c9be49010cf5f321"}, + {file = "matplotlib-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81c40af649d19c85f8073e25e5806926986806fa6d54be506fbf02aef47d5a89"}, + {file = "matplotlib-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52146fc3bd7813cc784562cb93a15788be0b2875c4655e2cc6ea646bfa30344b"}, + {file = "matplotlib-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:0fc51eaa5262553868461c083d9adadb11a6017315f3a757fc45ec6ec5f02888"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bd4f2831168afac55b881db82a7730992aa41c4f007f1913465fb182d6fb20c0"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:290d304e59be2b33ef5c2d768d0237f5bd132986bdcc66f80bc9bcc300066a03"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff2e239c26be4f24bfa45860c20ffccd118d270c5b5d081fa4ea409b5469fcd"}, + {file = "matplotlib-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:af4001b7cae70f7eaacfb063db605280058246de590fa7874f00f62259f2df7e"}, + {file = "matplotlib-3.9.0.tar.gz", hash = "sha256:e6d29ea6c19e34b30fb7d88b7081f869a03014f66fe06d62cc77d5a6ea88ed7a"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +kiwisolver = ">=1.3.1" +numpy = ">=1.23" +packaging = ">=20.0" +pillow = ">=8" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" + +[package.extras] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] + [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" +[[package]] +name = "numpy" +version = "2.0.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:04494f6ec467ccb5369d1808570ae55f6ed9b5809d7f035059000a37b8d7e86f"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2635dbd200c2d6faf2ef9a0d04f0ecc6b13b3cad54f7c67c61155138835515d2"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:0a43f0974d501842866cc83471bdb0116ba0dffdbaac33ec05e6afed5b615238"}, + {file = "numpy-2.0.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:8d83bb187fb647643bd56e1ae43f273c7f4dbcdf94550d7938cfc32566756514"}, + {file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79e843d186c8fb1b102bef3e2bc35ef81160ffef3194646a7fdd6a73c6b97196"}, + {file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7696c615765091cc5093f76fd1fa069870304beaccfd58b5dcc69e55ef49c1"}, + {file = "numpy-2.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b4c76e3d4c56f145d41b7b6751255feefae92edbc9a61e1758a98204200f30fc"}, + {file = "numpy-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd3a644e4807e73b4e1867b769fbf1ce8c5d80e7caaef0d90dcdc640dfc9787"}, + {file = "numpy-2.0.0-cp310-cp310-win32.whl", hash = "sha256:cee6cc0584f71adefe2c908856ccc98702baf95ff80092e4ca46061538a2ba98"}, + {file = "numpy-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:ed08d2703b5972ec736451b818c2eb9da80d66c3e84aed1deeb0c345fefe461b"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad0c86f3455fbd0de6c31a3056eb822fc939f81b1618f10ff3406971893b62a5"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7f387600d424f91576af20518334df3d97bc76a300a755f9a8d6e4f5cadd289"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:34f003cb88b1ba38cb9a9a4a3161c1604973d7f9d5552c38bc2f04f829536609"}, + {file = "numpy-2.0.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b6f6a8f45d0313db07d6d1d37bd0b112f887e1369758a5419c0370ba915b3871"}, + {file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f64641b42b2429f56ee08b4f427a4d2daf916ec59686061de751a55aafa22e4"}, + {file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7039a136017eaa92c1848152827e1424701532ca8e8967fe480fe1569dae581"}, + {file = "numpy-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46e161722e0f619749d1cd892167039015b2c2817296104487cd03ed4a955995"}, + {file = "numpy-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0e50842b2295ba8414c8c1d9d957083d5dfe9e16828b37de883f51fc53c4016f"}, + {file = "numpy-2.0.0-cp311-cp311-win32.whl", hash = "sha256:2ce46fd0b8a0c947ae047d222f7136fc4d55538741373107574271bc00e20e8f"}, + {file = "numpy-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd6acc766814ea6443628f4e6751d0da6593dae29c08c0b2606164db026970c"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:354f373279768fa5a584bac997de6a6c9bc535c482592d7a813bb0c09be6c76f"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d2f62e55a4cd9c58c1d9a1c9edaedcd857a73cb6fda875bf79093f9d9086f85"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1e72728e7501a450288fc8e1f9ebc73d90cfd4671ebbd631f3e7857c39bd16f2"}, + {file = "numpy-2.0.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:84554fc53daa8f6abf8e8a66e076aff6ece62de68523d9f665f32d2fc50fd66e"}, + {file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73aafd1afca80afecb22718f8700b40ac7cab927b8abab3c3e337d70e10e5a2"}, + {file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49d9f7d256fbc804391a7f72d4a617302b1afac1112fac19b6c6cec63fe7fe8a"}, + {file = "numpy-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0ec84b9ba0654f3b962802edc91424331f423dcf5d5f926676e0150789cb3d95"}, + {file = "numpy-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:feff59f27338135776f6d4e2ec7aeeac5d5f7a08a83e80869121ef8164b74af9"}, + {file = "numpy-2.0.0-cp312-cp312-win32.whl", hash = "sha256:c5a59996dc61835133b56a32ebe4ef3740ea5bc19b3983ac60cc32be5a665d54"}, + {file = "numpy-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a356364941fb0593bb899a1076b92dfa2029f6f5b8ba88a14fd0984aaf76d0df"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e61155fae27570692ad1d327e81c6cf27d535a5d7ef97648a17d922224b216de"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4554eb96f0fd263041baf16cf0881b3f5dafae7a59b1049acb9540c4d57bc8cb"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:903703372d46bce88b6920a0cd86c3ad82dae2dbef157b5fc01b70ea1cfc430f"}, + {file = "numpy-2.0.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:3e8e01233d57639b2e30966c63d36fcea099d17c53bf424d77f088b0f4babd86"}, + {file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cde1753efe513705a0c6d28f5884e22bdc30438bf0085c5c486cdaff40cd67a"}, + {file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821eedb7165ead9eebdb569986968b541f9908979c2da8a4967ecac4439bae3d"}, + {file = "numpy-2.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a1712c015831da583b21c5bfe15e8684137097969c6d22e8316ba66b5baabe4"}, + {file = "numpy-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9c27f0946a3536403efb0e1c28def1ae6730a72cd0d5878db38824855e3afc44"}, + {file = "numpy-2.0.0-cp39-cp39-win32.whl", hash = "sha256:63b92c512d9dbcc37f9d81b123dec99fdb318ba38c8059afc78086fe73820275"}, + {file = "numpy-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:3f6bed7f840d44c08ebdb73b1825282b801799e325bcbdfa6bc5c370e5aecc65"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9416a5c2e92ace094e9f0082c5fd473502c91651fb896bc17690d6fc475128d6"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:17067d097ed036636fa79f6a869ac26df7db1ba22039d962422506640314933a"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ecb5b0582cd125f67a629072fed6f83562d9dd04d7e03256c9829bdec027ad"}, + {file = "numpy-2.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cef04d068f5fb0518a77857953193b6bb94809a806bd0a14983a8f12ada060c9"}, + {file = "numpy-2.0.0.tar.gz", hash = "sha256:cf5d1c9e6837f8af9f92b6bd3e86d513cdc11f60fd62185cc49ec7d1aba34864"}, +] [[package]] name = "packaging" -version = "23.2" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pandas" +version = "2.2.2" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + [[package]] name = "platformdirs" -version = "4.1.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -167,13 +686,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.21.0" +version = "3.7.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, + {file = "pre_commit-3.7.1-py2.py3-none-any.whl", hash = "sha256:fae36fd1d7ad7d6a5a1c0b0d5adb2ed1a3bda5a21bf6c3e5372073d7a11cd4c5"}, + {file = "pre_commit-3.7.1.tar.gz", hash = "sha256:8ca3ad567bc78a4972a3f1a477e94a79d4597e8140a6e0b651c5e33899c3654a"}, ] [package.dependencies] @@ -183,6 +702,20 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pyproject-api" version = "1.6.1" @@ -204,13 +737,13 @@ testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytes [[package]] name = "pytest" -version = "7.4.4" +version = "8.2.2" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, ] [package.dependencies] @@ -218,11 +751,53 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] [[package]] name = "pyyaml" @@ -249,6 +824,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -284,20 +860,41 @@ files = [ ] [[package]] -name = "setuptools" -version = "69.0.3" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +name = "ruff" +version = "0.4.10" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, + {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, + {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, + {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, + {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, + {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, ] -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] [[package]] name = "tomli" @@ -312,13 +909,13 @@ files = [ [[package]] name = "tox" -version = "4.12.1" +version = "4.15.1" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.12.1-py3-none-any.whl", hash = "sha256:c07ea797880a44f3c4f200ad88ad92b446b83079d4ccef89585df64cc574375c"}, - {file = "tox-4.12.1.tar.gz", hash = "sha256:61aafbeff1bd8a5af84e54ef6e8402f53c6a6066d0782336171ddfbf5362122e"}, + {file = "tox-4.15.1-py3-none-any.whl", hash = "sha256:f00a5dc4222b358e69694e47e3da0227ac41253509bca9f45aa8f012053e8d9d"}, + {file = "tox-4.15.1.tar.gz", hash = "sha256:53a092527d65e873e39213ebd4bd027a64623320b6b0326136384213f95b7076"}, ] [package.dependencies] @@ -337,15 +934,26 @@ virtualenv = ">=20.25" docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] @@ -354,10 +962,10 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "dfa02eb97b9cca088662d1190dd467753f7062e2bee710901cf9369da93e3af2" +content-hash = "f3f31c66f518eee103ea240bf60f0c361033f7f22d94ea88a82736b44a1c13b6" diff --git a/pyproject.toml b/pyproject.toml index b492563..4e5f496 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,55 +3,55 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry] -name = "python_template" +name = "D4GPROJECT-TO-RENAME" version = "0.1.0" -description = "Template" +description = "Project Description to be modified" authors = ["DataForGood"] license = " MIT" readme = "README.md" +# Majority of D4G projects are not used as packages +# 2 options: +# 1. This project is NOT/will NOT be used as a package +# package-mode = false +# 2. Replace with your project's main module/folder +packages = [{ include = "PROJECT_TO_RENAME" }] [tool.poetry.dependencies] python = "^3.10" -# numpy = "^1.21.1" -# pandas = "^1.1.1" -# jupyter = "^1.0.0" -# ipykernel = "^5.3.4" +# Data Science dependencies +[tool.poetry.group.data.dependencies] +pandas = "^2.0.0" +numpy = ">=1.26" +matplotlib = "^3.9.0" + +# Code Linting and Formatting dependencies [tool.poetry.group.dev.dependencies] -pre-commit = "^2.20.0" -pytest = "^7.2.0" +pre-commit = "^3.7.1" +ruff = "^0.4.10" + +# Test dependencies +[tool.poetry.group.test.dependencies] +pytest = "^8.0.0" +pytest-mock = "*" tox = "^4.4.8" + +# D4G: Python Linting and Formatting [tool.ruff] +force-exclude = true +fix = true +show-fixes = true # Same as Black. line-length = 96 target-version = "py310" -# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or -# McCabe complexity (`C901`) by default. -select = ["E4", "E7", "E9", "F", "B"] -ignore = ["E501"] - -# Allow fix for all enabled rules (when `--fix`) is provided. -fixable = ["ALL"] -unfixable = [] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" - -[tool.ruff.format] -# Like Black, use double quotes for strings. -quote-style = "double" - -# Like Black, indent with spaces, rather than tabs. -indent-style = "space" - -# Like Black, respect magic trailing commas. -skip-magic-trailing-comma = false - -# Like Black, automatically detect the appropriate line ending. -line-ending = "auto" +cache-dir = ".cache/ruff" +# Output-format: "full" | "concise" | "grouped" | "json" | "junit" | "github" | "gitlab" | "pylint" | "azure" +# Group violations by containing file. +output-format = "grouped" +extend-include = ["*.ipynb"] exclude = [ ".bzr", ".direnv", @@ -79,7 +79,46 @@ exclude = [ "node_modules", "site-packages", "venv", + # D4G specific: needed for d4g-tools + "**/.generated/**", + "**/.gi-**", + ".cache", ] -[tool.ruff.mccabe] + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +# Numpy2.0:NPY201 + +select = ["E4", "E7", "E9", "F", "B", "NPY201"] +ignore = ["E501"] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.lint.mccabe] max-complexity = 10 + +[tool.ruff.lint.extend-per-file-ignores] +# Also ignore `F401` in all `__init__.py` files. +# Diable `F401` to keep unsed imports from `__init__.py` files. +"__init__.py" = ["F401"] + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" diff --git a/python_template/.empty b/python_template/.empty deleted file mode 100644 index e69de29..0000000 diff --git a/python_template/__init__.py b/python_template/__init__.py deleted file mode 100644 index e69de29..0000000