add yaml output for exoscale marketplace
This commit is contained in:
parent
86df11505f
commit
bfb64efdec
3 changed files with 178 additions and 1 deletions
|
@ -1,5 +1,10 @@
|
||||||
|
import re
|
||||||
|
import pyaml
|
||||||
|
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.template.loader import render_to_string
|
||||||
from hub.services.models import (
|
from hub.services.models import (
|
||||||
ServiceOffering,
|
ServiceOffering,
|
||||||
CloudProvider,
|
CloudProvider,
|
||||||
|
@ -8,8 +13,8 @@ from hub.services.models import (
|
||||||
ComputePlan,
|
ComputePlan,
|
||||||
VSHNAppCatPrice,
|
VSHNAppCatPrice,
|
||||||
)
|
)
|
||||||
import re
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from markdownify import markdownify
|
||||||
|
|
||||||
|
|
||||||
def natural_sort_key(name):
|
def natural_sort_key(name):
|
||||||
|
@ -79,6 +84,10 @@ def offering_detail(request, provider_slug, service_slug):
|
||||||
service__slug=service_slug,
|
service__slug=service_slug,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check if Exoscale marketplace YAML is requested
|
||||||
|
if request.GET.get("exo_marketplace") == "true":
|
||||||
|
return generate_exoscale_marketplace_yaml(offering)
|
||||||
|
|
||||||
pricing_data_by_group_and_service_level = None
|
pricing_data_by_group_and_service_level = None
|
||||||
|
|
||||||
# Generate pricing data for VSHN offerings
|
# Generate pricing data for VSHN offerings
|
||||||
|
@ -92,6 +101,92 @@ def offering_detail(request, provider_slug, service_slug):
|
||||||
return render(request, "services/offering_detail.html", context)
|
return render(request, "services/offering_detail.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_exoscale_marketplace_yaml(offering):
|
||||||
|
"""Generate YAML structure for Exoscale marketplace"""
|
||||||
|
|
||||||
|
# Create service name slug for YAML key
|
||||||
|
service_slug = offering.service.slug.replace("-", "")
|
||||||
|
yaml_key = f"marketplace_PRODUCTS_servala-{service_slug}"
|
||||||
|
|
||||||
|
# Generate product overview content from service description (convert HTML to Markdown)
|
||||||
|
product_overview = ""
|
||||||
|
if offering.service.description:
|
||||||
|
product_overview = markdownify(
|
||||||
|
offering.service.description, heading_style="ATX"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate highlights content from offering description and offer_description (convert HTML to Markdown)
|
||||||
|
highlights = ""
|
||||||
|
if offering.description:
|
||||||
|
highlights += markdownify(offering.description, heading_style="ATX")
|
||||||
|
if offering.offer_description:
|
||||||
|
if highlights:
|
||||||
|
highlights += "\n\n"
|
||||||
|
highlights += markdownify(
|
||||||
|
offering.offer_description.get_full_text(), heading_style="ATX"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build YAML structure
|
||||||
|
yaml_structure = {
|
||||||
|
yaml_key: {
|
||||||
|
"page_class": "tmpl-marketplace-product",
|
||||||
|
"html_title": f"Managed {offering.service.name} by VSHN via Servala",
|
||||||
|
"meta_desc": "Servala is the Open Cloud Native Service Hub. It connects businesses, developers, and cloud service providers on one unique hub with secure, scalable, and easy-to-use cloud-native services.",
|
||||||
|
"page_header_title": f"Managed {offering.service.name} by VSHN via Servala",
|
||||||
|
"provider_key": "vshn",
|
||||||
|
"slug": f"servala-managed-{offering.service.slug}",
|
||||||
|
"title": f"Managed {offering.service.name} by VSHN via Servala",
|
||||||
|
"logo": f"img/servala-{offering.service.slug}.svg",
|
||||||
|
"list_display": [],
|
||||||
|
"meta": [
|
||||||
|
{"key": "exoscale-iaas", "value": True},
|
||||||
|
{"key": "availability", "zones": "all"},
|
||||||
|
],
|
||||||
|
"action_link": f"https://servala.com/offering/{offering.cloud_provider.slug}/{offering.service.slug}/?source=exoscale_marketplace",
|
||||||
|
"action_link_text": "Subscribe now",
|
||||||
|
"blobs": [
|
||||||
|
{
|
||||||
|
"key": "product-overview",
|
||||||
|
"blob": (
|
||||||
|
product_overview.strip()
|
||||||
|
if product_overview
|
||||||
|
else "Service description not available."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "highlights",
|
||||||
|
"blob": (
|
||||||
|
highlights.strip()
|
||||||
|
if highlights
|
||||||
|
else "Offering highlights not available."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"editor",
|
||||||
|
{
|
||||||
|
"key": "pricing",
|
||||||
|
"blob": f"Find all the pricing information on the [Servala website](https://servala.com/offering/{offering.cloud_provider.slug}/{offering.service.slug}/?source=exoscale_marketplace#plans)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "service-and-support",
|
||||||
|
"blob": "Servala is operated by VSHN AG in Zurich, Switzerland.\n\nSeveral SLAs are available on request, offering support 24/7.\n\nMore details can be found in the [VSHN Service Levels Documentation](https://products.vshn.ch/service_levels.html).",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "terms-of-service",
|
||||||
|
"blob": "- [Product Description](https://products.vshn.ch/servala/index.html)\n- [General Terms and Conditions](https://products.vshn.ch/legal/gtc_en.html)\n- [SLA](https://products.vshn.ch/service_levels.html)\n- [DPA](https://products.vshn.ch/legal/dpa_en.html)\n- [Privacy Policy](https://products.vshn.ch/legal/privacy_policy_en.html)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate YAML response for browser display
|
||||||
|
yaml_content = pyaml.dump(yaml_structure, sort_dicts=False)
|
||||||
|
|
||||||
|
# Return as plain text for browser display
|
||||||
|
response = HttpResponse(yaml_content, content_type="text/plain")
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def generate_pricing_data(offering):
|
def generate_pricing_data(offering):
|
||||||
"""Generate pricing data for a specific offering and cloud provider"""
|
"""Generate pricing data for a specific offering and cloud provider"""
|
||||||
# Fetch compute plans for this cloud provider
|
# Fetch compute plans for this cloud provider
|
||||||
|
|
|
@ -14,8 +14,11 @@ dependencies = [
|
||||||
"django-schema-viewer>=0.5.2",
|
"django-schema-viewer>=0.5.2",
|
||||||
"djangorestframework>=3.15.2",
|
"djangorestframework>=3.15.2",
|
||||||
"environs[django]~=14.0",
|
"environs[django]~=14.0",
|
||||||
|
"markdownify>=1.1.0",
|
||||||
"odoorpc>=0.10.1",
|
"odoorpc>=0.10.1",
|
||||||
"pillow>=11.1.0",
|
"pillow>=11.1.0",
|
||||||
|
"pyaml>=25.5.0",
|
||||||
|
"pyyaml>=6.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|
79
uv.lock
generated
79
uv.lock
generated
|
@ -11,6 +11,19 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" },
|
{ url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "beautifulsoup4"
|
||||||
|
version = "4.13.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "soupsieve" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff-match-patch"
|
name = "diff-match-patch"
|
||||||
version = "20241021"
|
version = "20241021"
|
||||||
|
@ -202,6 +215,19 @@ django = [
|
||||||
{ name = "django-cache-url" },
|
{ name = "django-cache-url" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdownify"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "beautifulsoup4" },
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2f/78/c48fed23c7aebc2c16049062e72de1da3220c274de59d28c942acdc9ffb2/markdownify-1.1.0.tar.gz", hash = "sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd", size = 17127, upload-time = "2025-03-05T11:54:40.574Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/11/b751af7ad41b254a802cf52f7bc1fca7cabe2388132f2ce60a1a6b9b9622/markdownify-1.1.0-py3-none-any.whl", hash = "sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef", size = 13901, upload-time = "2025-03-05T11:54:39.454Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "marshmallow"
|
name = "marshmallow"
|
||||||
version = "3.26.0"
|
version = "3.26.0"
|
||||||
|
@ -290,6 +316,18 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651, upload-time = "2025-01-02T08:12:53.356Z" },
|
{ url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651, upload-time = "2025-01-02T08:12:53.356Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyaml"
|
||||||
|
version = "25.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c1/40/94f10f32ab952c5cca713d9ac9d8b2fdc37392d90eea403823eeac674c24/pyaml-25.5.0.tar.gz", hash = "sha256:5799560c7b1c9daf35a7a4535f53e2c30323f74cbd7cb4f2e715b16dd681a58a", size = 29812, upload-time = "2025-05-29T05:34:05.292Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/7d/1b5061beff826f902285827261485a058b943332eba8a5532a0164735205/pyaml-25.5.0-py3-none-any.whl", hash = "sha256:b9e0c4e58a5e8003f8f18e802db49fd0563ada587209b13e429bdcbefa87d035", size = 26422, upload-time = "2025-05-29T05:34:03.594Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -308,6 +346,23 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/43/a2/b6a5cbd5822b4d049adfedf496ce0908480e5a41722fda7b7ffaacb086d6/python_monkey_business-1.1.0-py2.py3-none-any.whl", hash = "sha256:15b4f603c749ba9a7b4f1acd36af023a6c5ba0f7e591c945f8253f0ef44bf389", size = 4670, upload-time = "2024-07-11T16:34:58.565Z" },
|
{ url = "https://files.pythonhosted.org/packages/43/a2/b6a5cbd5822b4d049adfedf496ce0908480e5a41722fda7b7ffaacb086d6/python_monkey_business-1.1.0-py2.py3-none-any.whl", hash = "sha256:15b4f603c749ba9a7b4f1acd36af023a6c5ba0f7e591c945f8253f0ef44bf389", size = 4670, upload-time = "2024-07-11T16:34:58.565Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servala-fe"
|
name = "servala-fe"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -322,8 +377,11 @@ dependencies = [
|
||||||
{ name = "django-schema-viewer" },
|
{ name = "django-schema-viewer" },
|
||||||
{ name = "djangorestframework" },
|
{ name = "djangorestframework" },
|
||||||
{ name = "environs", extra = ["django"] },
|
{ name = "environs", extra = ["django"] },
|
||||||
|
{ name = "markdownify" },
|
||||||
{ name = "odoorpc" },
|
{ name = "odoorpc" },
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
|
{ name = "pyaml" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
|
@ -343,11 +401,32 @@ requires-dist = [
|
||||||
{ name = "django-schema-viewer", specifier = ">=0.5.2" },
|
{ name = "django-schema-viewer", specifier = ">=0.5.2" },
|
||||||
{ name = "djangorestframework", specifier = ">=3.15.2" },
|
{ name = "djangorestframework", specifier = ">=3.15.2" },
|
||||||
{ name = "environs", extras = ["django"], specifier = "~=14.0" },
|
{ name = "environs", extras = ["django"], specifier = "~=14.0" },
|
||||||
|
{ name = "markdownify", specifier = ">=1.1.0" },
|
||||||
{ name = "odoorpc", specifier = ">=0.10.1" },
|
{ name = "odoorpc", specifier = ">=0.10.1" },
|
||||||
{ name = "pillow", specifier = ">=11.1.0" },
|
{ name = "pillow", specifier = ">=11.1.0" },
|
||||||
|
{ name = "pyaml", specifier = ">=25.5.0" },
|
||||||
|
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||||
]
|
]
|
||||||
provides-extras = ["dev"]
|
provides-extras = ["dev"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soupsieve"
|
||||||
|
version = "2.7"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparse"
|
name = "sqlparse"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue