Skip to main content

Core spatial analytics, spatial statistics, and urban resilience engines for the PlanX ecosystem.

Project description

PlanX SDK (Software Development Kit)

CI PyPI version Python version support License: MIT GitHub Code Formatting

PlanX SDK is the official core library of the PlanX QGIS plugin ecosystem, containing spatial analysis, spatial statistics, network routing, and urban resilience computation engines. It is designed to run headless, independent of the QGIS interface, and is powered entirely by pure Python, NumPy, and SciPy.

With this SDK, you can:

  1. Run Headless & Independent: Execute complex spatial algorithms without launching QGIS (e.g., in Jupyter Notebooks, standalone scripts, servers, or web applications).
  2. Perform Fast Testing: Easily run unit tests via pytest in a headless environment.
  3. Ensure Centralized Management: Keep all plugins and analytical tools consistent by updating a single core package (installable via pip install planx-sdk).

๐Ÿ“‚ Project Structure (Directory Structure)

planx_sdk/
  โ”œโ”€โ”€ .github/workflows/          # GitHub Actions: CI (tests/lint/types) + PyPI publishing
  โ”œโ”€โ”€ pyproject.toml              # Modern package metadata, dependencies, and settings (PEP 517/621)
  โ”œโ”€โ”€ README.md                   # Project documentation
  โ”œโ”€โ”€ LICENSE                     # License file
  โ”œโ”€โ”€ .gitignore                  # Git ignore rules for caches and builds
  โ”œโ”€โ”€ src/                        # Source code (Standard src-layout)
  โ”‚   โ””โ”€โ”€ planx/                  # Main package directory
  โ”‚       โ”œโ”€โ”€ __init__.py         # Versioning and package level definitions
  โ”‚       โ”œโ”€โ”€ spatial/            # Core spatial network analysis and space syntax engine
  โ”‚       โ”‚   โ”œโ”€โ”€ __init__.py
  โ”‚       โ”‚   โ”œโ”€โ”€ paths.py        # Shortest path algorithms (Dijkstra, SciPy integration)
  โ”‚       โ”‚   โ”œโ”€โ”€ centrality.py   # Closeness, Betweenness, Eigenvector, and Criticality centrality
  โ”‚       โ”‚   โ””โ”€โ”€ accessibility.py# Hansen Gravity and Cumulative Opportunities accessibility models
  โ”‚       โ”œโ”€โ”€ geostats/           # Spatial statistics and spatial autocorrelation engines
  โ”‚       โ”‚   โ”œโ”€โ”€ __init__.py
  โ”‚       โ”‚   โ”œโ”€โ”€ stats_engines.py# Getis-Ord Gi*, Local/Global Moran's I, OLS, GWR, SDE, k-means
  โ”‚       โ”‚   โ”œโ”€โ”€ interpolation.py# Spatial interpolation algorithms (e.g. IDW, nearest neighbor)
  โ”‚       โ”‚   โ””โ”€โ”€ weights.py      # Spatial weights matrix generation (e.g. k-NN, distance band)
  โ”‚       โ”œโ”€โ”€ suitability/        # Raster-based MCDA (Multi-Criteria Decision Analysis) engine
  โ”‚       โ”‚   โ”œโ”€โ”€ __init__.py
  โ”‚       โ”‚   โ”œโ”€โ”€ mcda.py         # Normalization methods (Sigmoid, Gaussian, Min-Max) and WLC
  โ”‚       โ”‚   โ”œโ”€โ”€ facility.py     # Facility location optimization (MCLP, p-Median, LSCP)
  โ”‚       โ”‚   โ””โ”€โ”€ weights.py      # Decision matrix weighting methods (AHP, Entropy, CRITIC, PCA)
  โ”‚       โ””โ”€โ”€ resilience/         # Urban resilience, disaster risk, and hazard simulation engines
  โ”‚           โ”œโ”€โ”€ __init__.py
  โ”‚           โ”œโ”€โ”€ seismic.py      # Monte Carlo seismic structural damage and debris propagation simulation
  โ”‚           โ”œโ”€โ”€ flood.py        # DEM-based pluvial (surface water) and connected coastal flood models
  โ”‚           โ”œโ”€โ”€ landslide.py    # Terrain slope (Horn's method), soil and LULC landslide screening
  โ”‚           โ”œโ”€โ”€ wildfire.py     # Terrain slope/aspect and fuel-based wildfire risk index
  โ”‚           โ”œโ”€โ”€ social.py       # Social Vulnerability Index (SVI) screening and analysis
  โ”‚           โ”œโ”€โ”€ heat.py         # Urban heat comfort risk and green space deficit screening model
  โ”‚           โ”œโ”€โ”€ synthesis.py    # Multi-hazard composite index and equity-oriented priority synthesis
  โ”‚           โ””โ”€โ”€ infrastructure.py# Infrastructure disruption, service loss, and bottleneck analysis
  โ””โ”€โ”€ tests/                      # Unit tests

๐Ÿ’ก Core Features and Usage Examples

1. Urban Accessibility Analysis (planx.spatial)

Calculates accessibility from origin points (e.g., residential areas) to destination points (e.g., hospitals) using travel distance matrices, gravity (attraction) weights, or cumulative opportunities.

import numpy as np
from planx.spatial import gravity_accessibility

# Distance Matrix (O x D): Distance from 2 origins to 3 destinations (in meters)
dists = np.array([
    [150.0, 300.0, 900.0],
    [500.0, 100.0, 1200.0]
])
# Destination capacity/attraction weights (e.g., hospital bed capacity)
weights = np.array([50.0, 100.0, 250.0])

# Gravity accessibility using exponential decay function (Hansen Index)
accessibility = gravity_accessibility(
    dists, weights, decay_method="exponential", beta=0.002, cutoff=1000.0
)
print("Accessibility Scores:", accessibility)

# 2. E2SFCA (Enhanced Two-Step Floating Catchment Area) and Gini Inequality Index
from planx.spatial import enhanced_2sfca, spatial_equity_gini

d_matrix = np.array([[10.0, 50.0], [30.0, 10.0]])
hospitals_supply = np.array([10.0, 20.0])  # Bed capacities
neighborhoods_demand = np.array([100.0, 200.0])  # Population

# E2SFCA scores with 40.0 min/km cutoff and linear decay method
access_scores = enhanced_2sfca(
    d_matrix, hospitals_supply, neighborhoods_demand, cutoff=40.0, decay_method="linear"
)
print("E2SFCA Accessibility Scores:", access_scores)  # [0.06, 0.12]

# Spatial accessibility equity analysis using population-weighted Gini coefficient
gini_coeff = spatial_equity_gini(access_scores, neighborhoods_demand)
print("Accessibility Gini Coefficient:", gini_coeff)

# 3. Service Area and Population Coverage Analysis (Isochrone / Service Area)
from planx.spatial import service_area_coverage

# Simple road network representation (CSR format, 3 nodes)
ind_ptr = np.array([0, 1, 3, 4], dtype=np.int64)
adj_nodes = np.array([1, 0, 2, 1], dtype=np.int64)
edge_w = np.array([1.5, 1.5, 2.5, 2.5], dtype=np.float64)  # Travel time in minutes

node_pop = np.array([100.0, 200.0, 300.0])  # Node population
facilities = np.array([0])                  # Index of facility node
thresholds = [1.0, 2.0, 5.0]                # Service time thresholds (minutes)

service_areas = service_area_coverage(
    ind_ptr, adj_nodes, edge_w, n=3, facilities=facilities,
    thresholds=thresholds, node_population=node_pop
)
print("Reachable Nodes at 2.0 Minutes:", service_areas[2.0]["reachable_nodes"])  # [0, 1]
print("Total Covered Population at 2.0 Minutes:", service_areas[2.0]["population_covered"])  # 300.0

2. Facility Location Optimization (planx.suitability)

Optimizes the placement of facilities (e.g., emergency assembly areas, shelters) to maximize coverage (MCLP, LSCP) or minimize average travel distance (p-Median).

import numpy as np
from planx.suitability import greedy_mclp, greedy_p_median, greedy_lscp

candidates = np.array([[0.0, 0.0], [10.0, 10.0], [20.0, 20.0]])  # Candidate shelter coordinates
demands = np.array([[1.0, 1.0], [11.0, 11.0], [25.0, 25.0]])     # Building demand coordinates
populations = np.array([100.0, 250.0, 500.0])                    # Demand population

# 1. MCLP: Select K=2 shelters to maximize coverage within 15.0 units walking distance
selected_mclp, added_pop, cum_pop = greedy_mclp(
    candidates, demands, populations, max_distance=15.0, k=2
)
print("MCLP Selected Facility Indices:", selected_mclp)  # [2, 1]

# 2. p-Median: Select P=2 facilities to minimize total weighted distance cost
selected_pmed, costs = greedy_p_median(
    candidate_coords=candidates, demand_coords=demands, demand_pop=populations, p=2
)
print("p-Median Selected Facility Indices:", selected_pmed)  # [2, 1]
print("Cumulative Step Costs:", costs)

# 3. LSCP: Find min facilities to cover at least 80% of population within 15.0 units distance
selected_lscp, final_coverage = greedy_lscp(
    candidates, demands, demand_pop=populations, max_distance=15.0, target_coverage=0.8
)
print("LSCP Selected Facility Indices:", selected_lscp)  # [2] (One facility covers 87.5%)

# 4. Capacitated Location Allocation: Assign demands to closest facilities with capacity limits
from planx.suitability import capacitated_location_allocation

facilities = np.array([[0.0, 0.0], [10.0, 0.0]])
capacities = np.array([150.0, 200.0])
demands_coords = np.array([[1.0, 0.0], [9.0, 0.0], [2.0, 0.0]])
demands_pop = np.array([100.0, 150.0, 80.0])

allocations, unassigned, usage = capacitated_location_allocation(
    facilities, capacities, demands_coords, demands_pop
)
print("Allocations per facility:", allocations)
print("Unassigned demands:", unassigned)

3. Seismic Damage and Debris Propagation (planx.resilience)

Runs a stochastic Monte Carlo simulation to evaluate building collapse probabilities based on building attributes (construction year, number of floors) and earthquake magnitude. Computes debris volume and outward collapse radius.

import numpy as np
from planx.resilience import simulate_seismic_debris

areas = np.array([120.0, 200.0, 80.0])  # Building footprint areas (m2)
floors = np.array([4.0, 8.0, 2.0])      # Number of floors
years = np.array([1990, 2005, 2021])     # Construction years

# Earthquake scenario (7.2 Mw)
probs, collapsed, radii, volumes = simulate_seismic_debris(
    areas, floors, years, magnitude=7.2, seed=42
)
print("Building Collapse State (0: Intact, 1: Collapsed):", collapsed)
print("Debris Radii (m):", radii)

4. Social Vulnerability Index (SVI) (planx.resilience)

Normalizes and aggregates demographic indicators (e.g., elderly share, low-income population, disability rate) using Min-Max scaling and Weighted Linear Combination to produce vulnerability ranks.

import numpy as np
from planx.resilience import social_vulnerability_index

# Demographic data for 3 neighborhoods
indicators = {
    "elderly": np.array([10.0, 50.0, 100.0]),
    "low_income": np.array([200.0, 100.0, 50.0])
}
# Indicator weights
weights = {
    "elderly": 0.5,
    "low_income": 0.5
}

scores, classes = social_vulnerability_index(indicators, weights)
print("Social Vulnerability Scores:", scores)
print("Vulnerability Classes:", classes)  # ['Moderate', 'Moderate', 'Moderate']

5. Urban Heat Comfort Risk (planx.resilience)

Calculates urban heat exposure/risk scores (0-100) by combining impervious surface share, building density, green space deficit, walking distance to cooling areas, and vulnerable population hotspots.

import numpy as np
from planx.resilience import urban_heat_comfort_risk

# Data for a 2x2 grid
impervious = np.array([[0.8, 0.2], [0.5, 0.1]])          # Impervious share [0-1]
buildings = np.array([[0.6, 0.1], [0.4, 0.05]])          # Building footprint share [0-1]
green = np.array([[0.1, 0.8], [0.3, 0.9]])               # Green cover share [0-1]
cooling_dists = np.array([[300.0, 50.0], [200.0, 20.0]])  # Distance to cooling area (meters)
vuln_assets = np.array([[2, 0], [1, 0]])                  # Vulnerable assets count (e.g., schools)

scores, classes = urban_heat_comfort_risk(
    impervious, buildings, green, cooling_dists, vuln_assets, cooling_distance=400.0
)
print("Heat Comfort Risk Scores:\n", scores)

6. Decision Making and Weighting Methods (planx.suitability)

Provides analytic tools for Multi-Criteria Decision Analysis (MCDA) weighting, including AHP (Analytic Hierarchy Process), Entropy, CRITIC, and PCA. Includes helper functions to extract pairwise parameters.

import numpy as np
from planx.suitability import ahp_weights, entropy_weights

# 1. AHP (Analytic Hierarchy Process) with consistency ratio checking
# 3x3 pairwise comparison matrix
matrix = np.array([
    [1.0, 2.0, 3.0],
    [0.5, 1.0, 2.0],
    [0.33, 0.5, 1.0]
])
weights, cr = ahp_weights(matrix)
print("AHP Weights:", weights)
print("Consistency Ratio (CR):", cr)

# 2. Entropy Weighting Method
# Decision matrix with 4 alternatives (rows) and 3 criteria (columns)
decision_matrix = np.array([
    [10.0, 100.0, 0.1],
    [20.0, 50.0, 0.2],
    [15.0, 80.0, 0.15],
    [30.0, 20.0, 0.3]
])
ent_weights = entropy_weights(decision_matrix)
print("Entropy Weights:", ent_weights)

7. Multi-Hazard Synthesis and Equitable Prioritization (planx.resilience)

Synthesizes multiple hazard exposure layers into a single Composite Hazard Index. Integrates the Social Vulnerability Index (SVI) as a priority amplifier to identify high-hazard, high-vulnerability areas for targeted climate adaptation.

import numpy as np
from planx.resilience import multi_hazard_composite, equity_adjusted_priority

# 1. Multi-Hazard Composite Index
hazards = {
    "heat": np.array([80.0, 20.0, 50.0]),
    "flood": np.array([40.0, 10.0, np.nan])  # Automatically masks missing values
}
weights = {"heat": 0.6, "flood": 0.4}

scores, classes, dominant, diversity, drivers = multi_hazard_composite(hazards, weights)
print("Synthesized Composite Scores:", scores)  # [64. 16. 50.]
print("Dominant Hazards:", dominant)  # ['heat', 'heat', 'heat']
print("Multi-Stress Diversity Scores:", diversity)

# 2. Social Vulnerability (SVI) Equity-Adjusted Priority Scoring
svi = np.array([10.0, 90.0, 50.0])  # Social Vulnerability Index
eq_scores, raw, factors, eq_classes = equity_adjusted_priority(scores, svi, equity_weight=0.5)
print("Equity-Adjusted Priority Scores:", eq_scores)

8. Infrastructure and Network Resilience Analysis (planx.resilience)

Simulates street network blocking (e.g., due to seismic debris or flooding) and computes network isolation rates, delay metrics, network bottlenecks, and debris clearance scheduling.

import numpy as np
from planx.resilience import (
    simulate_network_disruption,
    infrastructure_service_loss,
    identify_critical_bottlenecks,
    prioritize_debris_clearance
)

# 3-node network represented in CSR format
indptr = np.array([0, 1, 3, 4], dtype=np.int64)
adj = np.array([1, 0, 2, 1], dtype=np.int64)
weights = np.array([1.5, 1.5, 2.5, 2.5], dtype=np.float64)  # Travel costs (minutes)

# 1. Simulate closure of intersection node 1
disrupted_weights = simulate_network_disruption(indptr, adj, weights, n=3, blocked_nodes=[1])
print("Post-disruption Edge Weights:", disrupted_weights)  # All edges will be inf

# 2. Analyze service loss statistics using pre- and post-disruption distance matrices
dists_pre = np.array([[10.0, 20.0], [15.0, 30.0]])
dists_post = np.array([[15.0, 20.0], [np.inf, np.inf]])  # Neighborhood 2 completely isolated

demands = np.array([100.0, 50.0])  # Population demand (Neighborhood 1: 100, Neighborhood 2: 50)
loss_stats = infrastructure_service_loss(dists_pre, dists_post, demands=demands)
print("Isolation Rate:", loss_stats["isolation_rate"])  # 0.333
print("Mean Delay (Minutes):", loss_stats["mean_delay"])  # 5.0
print("Service Vulnerability Index:", loss_stats["service_vulnerability_index"])

# 3. Identify critical network bottlenecks
pre_usage = np.array([10.0, 5.0, 20.0])
post_usage = np.array([12.0, 5.0, 35.0])
bottlenecks, increases = identify_critical_bottlenecks(pre_usage, post_usage, top_k=2)
print("Critical Bottleneck Edge Indices:", bottlenecks)  # [2, 0]
print("Traffic Load Increase Amounts:", increases)  # [15. 2.]

# 4. Prioritize debris clearance operations (Benefit/Cost ratio method)
blocked_roads = np.array([0, 2])                  # Blocked road indices
debris_volumes = np.array([15.0, 150.0])          # Debris volume on roads (m3)
edge_criticality = np.array([100.0, 20.0, 300.0, 50.0])  # Road network importance score

clearance_order, priority_scores = prioritize_debris_clearance(
    blocked_roads, debris_volumes, edge_criticality, cost_factor=1.0
)
print("Debris Clearance Priority Order:", clearance_order)  # [2, 0]
print("Clearance Priority Scores:", priority_scores)

9. Flood and Inundation Risk Analysis (planx.resilience)

Simulates pluvial (surface water runoff) susceptibility and coastal inundation scenarios to locate flood-prone regions and determine inundation depths.

import numpy as np
from planx.resilience import pluvial_flood_susceptibility, coastal_flood_inundation

# 1. Pluvial (Surface Water) Flood Susceptibility (using DEM grid and curvature proxy)
dem_grid = np.array([
    [10.0, 12.0, 15.0],
    [8.0, 9.0, 11.0],
    [5.0, 7.0, 8.0]
])
# Cell size 10m, local neighborhood relief radius 15m
scores, classes = pluvial_flood_susceptibility(dem_grid, cell_size=10.0, neighborhood_radius=15.0)
print("Pluvial Flood Susceptibility Scores:\n", scores)

# 2. Coastal Inundation & Sea Level Rise (Hydrologically Connected Bathtub Model)
# Sea rise of 8.0m, flooding spreads from top-left (0,0) seed cell (8-connectivity)
sea_mask = np.zeros((3, 3), dtype=bool)
sea_mask[0, 0] = True  # Sea seed start location

flooded, water_depth = coastal_flood_inundation(dem_grid, water_level=8.0, sea_mask=sea_mask)
print("Inundated Area Mask:\n", flooded)
print("Water Depth Matrix:\n", water_depth)

10. Landslide Susceptibility Analysis (planx.resilience)

Combines terrain slope (calculated using Horn's method), soil, and land use/land cover (LULC) factor weights to locate areas prone to slope stability failure.

import numpy as np
from planx.resilience import landslide_susceptibility

# 3x3 DEM grid with elevation changes
dem_grid = np.array([
    [100.0, 100.0, 100.0],
    [50.0, 50.0, 50.0],
    [0.0, 0.0, 0.0]
])

# Cell size of 10.0 meters
scores, classes = landslide_susceptibility(dem_grid, cell_size=10.0)
print("Landslide Susceptibility Scores:\n", scores)

11. Spatial Interpolation (planx.geostats)

Interpolates continuous values (e.g., temperature, rainfall, or pollution) from scattered point observations onto a grid or target point locations using Inverse Distance Weighting (IDW) powered by SciPy KDTree.

import numpy as np
from planx.geostats import idw_to_points, idw_to_grid

# 4 corner observations
src_coords = np.array([[0.0, 0.0], [10.0, 0.0], [0.0, 10.0], [10.0, 10.0]])
src_values = np.array([10.0, 20.0, 30.0, 40.0])

# 1. Interpolate at target points (e.g., center point (5, 5))
target_coords = np.array([[5.0, 5.0]])
interpolated_points = idw_to_points(src_coords, src_values, target_coords, power=2.0)
print("Interpolated values at points:", interpolated_points)  # Expected: [25.0]

# 2. Interpolate onto a 2x2 grid (grid bounding box xmin, ymin, xmax, ymax)
grid, x, y = idw_to_grid(src_coords, src_values, (0.0, 0.0, 10.0, 10.0), cell_size=5.0)
print("Interpolated grid:\n", grid)

12. Wildfire Risk Index (planx.resilience)

Calculates wildfire exposure/risk based on terrain slope (steeper slopes increase spread rate), aspect (sun exposure alignment drying fuel), and vegetation/fuel density.

import numpy as np
from planx.resilience import wildfire_risk_index

# 3x3 DEM grid
dem_grid = np.array([
    [10.0, 10.0, 10.0],
    [10.0, 10.0, 10.0],
    [10.0, 10.0, 10.0]
])
# 100% vegetation cover
veg_density = np.ones((3, 3))

scores, classes = wildfire_risk_index(dem_grid, cell_size=10.0, vegetation_density=veg_density)
print("Wildfire Risk Scores:\n", scores)

13. Spatial Weights Matrix Generation (planx.geostats)

Generates spatial neighbor relationships and weights matrices (k-NN or distance band) required for spatial autocorrelation (e.g. Moran's I, Getis-Ord Gi*) using fast KDTree.

import numpy as np
from planx.geostats import create_knn_weights, create_distance_band_weights

# 3 points along a line
coords = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
ids = [100, 101, 102]

# 1. Create k-Nearest Neighbors weights (k=2)
neighbors_knn, weights_knn = create_knn_weights(coords, ids, k=2)
print("KNN Neighbors:", neighbors_knn)
print("KNN Weights:", weights_knn)

# 2. Create Distance Band weights (threshold distance = 1.5)
neighbors_db, weights_db = create_distance_band_weights(coords, ids, threshold=1.5)
print("Distance Band Neighbors:", neighbors_db)

๐Ÿ› ๏ธ Installation & Development

1. Standard Installation

You can install the SDK directly from PyPI:

pip install planx-sdk

2. Developer Mode (Editable Install)

To clone the repository and run in editable mode for local development:

git clone https://github.com/YusufEminoglu/planx_sdk.git
cd planx_sdk
pip install -e .[dev]

๐Ÿงช Tests and Code Standards

All modules are verified using unit tests in pytest. To execute the test suite:

pytest

We follow standard code styling guidelines verified using ruff and black. To check and format code:

black .
ruff check .

๐Ÿ“ License

This project is licensed under the MIT License. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

planx_sdk-0.3.0.tar.gz (75.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

planx_sdk-0.3.0-py3-none-any.whl (69.8 kB view details)

Uploaded Python 3

File details

Details for the file planx_sdk-0.3.0.tar.gz.

File metadata

  • Download URL: planx_sdk-0.3.0.tar.gz
  • Upload date:
  • Size: 75.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for planx_sdk-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a5ff2760c4cd16ea0fefdea093c6681c985b0962ea370724446516860122966f
MD5 aad7a1ee602d472359bf10783f2d93eb
BLAKE2b-256 26df79b5b0cbf6f2ea17a0fa2738388a5b43925c17aaa888115a2b9778425cb8

See more details on using hashes here.

Provenance

The following attestation bundles were made for planx_sdk-0.3.0.tar.gz:

Publisher: publish.yml on YusufEminoglu/planx_sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file planx_sdk-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: planx_sdk-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 69.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for planx_sdk-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 884c880747b5ee53b0f94cc0418c0d491ce3f77bd33033a4414e9a0cea29836f
MD5 1da85c657d210bab48b8b8960f1b548d
BLAKE2b-256 7a87049143419990fe7a84e098b347e618f4c8d3bbfd54c6330c2d4c6f6888a4

See more details on using hashes here.

Provenance

The following attestation bundles were made for planx_sdk-0.3.0-py3-none-any.whl:

Publisher: publish.yml on YusufEminoglu/planx_sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page