+ {% for plan in offering.plans.all %}
+
+
+
+
{{ plan.name }}
+ {% if plan.plan_description %}
+
+ {{ plan.plan_description.text|safe }}
-
- {% endfor %}
-
- {% elif offering.plans.all %}
-
-
Available Plans
-
- {% for plan in offering.plans.all %}
-
-
-
-
{{ plan.name }}
- {% if plan.plan_description %}
-
- {{ plan.plan_description.text|safe }}
-
- {% endif %}
- {% if plan.description %}
-
- {{ plan.description|safe }}
-
- {% endif %}
- {% if plan.pricing %}
-
- {{ plan.pricing|safe }}
-
- {% endif %}
+ {% endif %}
+ {% if plan.description %}
+
+ {{ plan.description|safe }}
+ {% endif %}
+ {% if plan.pricing %}
+
+ {{ plan.pricing|safe }}
+
+ {% endif %}
- {% empty %}
-
-
-
No plans available yet.
-
I'm interested in this offering
- {% embedded_contact_form source="Offering Interest" service=offering.service offering_id=offering.id %}
-
-
- {% endfor %}
- {% else %}
-
+ {% empty %}
-
I'm interested in this offering
- {% load contact_tags %}
- {% embedded_contact_form source="Offering Interest" service=offering.service offering_id=offering.id %}
+
+
No plans available yet.
+
I'm interested in this offering
+ {% embedded_contact_form source="Offering Interest" service=offering.service offering_id=offering.id %}
+
+ {% endfor %}
+
+ {% else %}
+
+
I'm interested in this offering
+ {% load contact_tags %}
+ {% embedded_contact_form source="Offering Interest" service=offering.service offering_id=offering.id %}
+
{% endif %}
{% if offering.plans.exists %}
diff --git a/hub/services/templates/services/pricelist.html b/hub/services/templates/services/pricelist.html
deleted file mode 100644
index ca865ae..0000000
--- a/hub/services/templates/services/pricelist.html
+++ /dev/null
@@ -1,440 +0,0 @@
-{% extends 'base.html' %}
-{% load static %}
-
-{% block title %}Complete Price List{% endblock %}
-
-{% block extra_js %}
-
-{% endblock %}
-
-{% block content %}
-
-
-
-
Complete Price List - All Service Variants
-
-
-
-
-
- {% if filter_cloud_provider or filter_service or filter_compute_plan_group or filter_service_level %}
-
- Active Filters:
- {% if filter_cloud_provider %}Cloud Provider: {{ filter_cloud_provider }}{% endif %}
- {% if filter_service %}Service: {{ filter_service }}{% endif %}
- {% if filter_compute_plan_group %}Group: {{ filter_compute_plan_group }}{% endif %}
- {% if filter_service_level %}Service Level: {{ filter_service_level }}{% endif %}
-
- {% endif %}
-
- {% if pricing_data_by_group_and_service_level %}
- {% for group_name, service_levels in pricing_data_by_group_and_service_level.items %}
-
-
{{ group_name }}
-
- {# Display group description and node_label from first available plan #}
- {% for service_level, pricing_data in service_levels.items %}
- {% if pricing_data and forloop.first %}
- {% with pricing_data.0 as representative_plan %}
- {% if representative_plan.compute_plan_group_description %}
-
Description: {{ representative_plan.compute_plan_group_description }}
- {% endif %}
- {% if representative_plan.compute_plan_group_node_label %}
-
Node Label: {{ representative_plan.compute_plan_group_node_label }}
- {% endif %}
-
- {# Display storage pricing for this cloud provider #}
- {% if representative_plan.storage_plans %}
-
-
Storage Options:
-
-
-
-
- Storage Plan |
- Term |
- Unit |
- Prices |
-
-
-
- {% for storage_plan in representative_plan.storage_plans %}
-
- {{ storage_plan.name }} |
- {{ storage_plan.get_term_display }} |
- {{ storage_plan.get_unit_display }} |
-
- {% for price in storage_plan.prices.all %}
- {{ price.amount }} {{ price.currency }}
- {% empty %}
- No prices
- {% endfor %}
- |
-
- {% endfor %}
-
-
-
-
- {% endif %}
- {% endwith %}
- {% endif %}
- {% endfor %}
-
- {% for service_level, pricing_data in service_levels.items %}
-
-
SLA: {{ service_level }}
- {% if pricing_data %}
- {# Display common values for this service level #}
- {% with pricing_data.0 as first_row %}
-
-
- Cloud Provider: {{ first_row.cloud_provider }}
-
-
- Service: {{ first_row.service }}
-
-
- CPU/Memory Ratio: {{ first_row.cpu_mem_ratio }}
-
-
- Variable Unit: {{ first_row.variable_unit }}
-
-
- Replica Enforce: {{ first_row.replica_enforce }}
-
-
- {% endwith %}
-
-
-
-
-
- Compute Plan |
- Cloud Provider |
- vCPUs |
- RAM (GB) |
- Term |
- Currency |
- Compute Plan Price |
- Units |
- SLA Base |
- SLA Per Unit |
- SLA Price |
- {% if show_discount_details %}
- Discount Model |
- Discount Details |
- {% endif %}
- {% if show_price_comparison %}
- External Comparisons |
- {% endif %}
-
-
-
-
- {% for row in pricing_data %}
-
- {{ row.compute_plan }} |
- {{ row.cloud_provider }} |
- {{ row.vcpus }} |
- {{ row.ram }} |
- {{ row.term }} |
- {{ row.currency }} |
- {{ row.compute_plan_price|floatformat:2 }} |
- {{ row.units }} |
- {{ row.sla_base|floatformat:2 }} |
- {{ row.sla_per_unit|floatformat:4 }} |
- {{ row.sla_price|floatformat:2 }} |
- {% if show_discount_details %}
-
- {% if row.has_discount %}
- {{ row.discount_model }}
- {% else %}
- None
- {% endif %}
- |
-
- {% if row.has_discount %}
-
- Total Units: {{ row.total_units }}
- Standard Price: {{ row.standard_sla_price|floatformat:2 }}
- Discounted Price: {{ row.discounted_sla_price|floatformat:2 }}
- Savings: {{ row.discount_savings|floatformat:2 }} ({{ row.discount_percentage|floatformat:1 }}%)
- {% if row.discount_breakdown %}
- Breakdown:
- {% for tier in row.discount_breakdown %}
- {{ tier.tier_range }} units: {{ tier.units }} × {{ tier.rate|floatformat:4 }} = {{ tier.subtotal|floatformat:2 }}
- {% endfor %}
- {% endif %}
-
- {% else %}
- No discount applied
- {% endif %}
- |
- {% endif %}
- {% if show_price_comparison %}
-
- -
- |
- {% endif %}
- {{ row.final_price|floatformat:2 }} |
-
- {% if show_price_comparison and row.external_comparisons %}
- {% for comparison in row.external_comparisons %}
-
- {{ comparison.plan_name }} |
- {{ comparison.provider }} |
-
- {% if comparison.vcpus %}{{ comparison.vcpus }}{% else %}-{% endif %}
- |
-
- {% if comparison.ram %}{{ comparison.ram }}{% else %}-{% endif %}
- |
- {{ row.term }} |
- {{ comparison.currency }} |
- - |
- - |
- - |
- - |
- - |
- {% if show_discount_details %}
- - |
- - |
- {% endif %}
-
-
- {% if comparison.source %}{{ comparison.provider }}{% else %}{{ comparison.provider }}{% endif %}
- {% if comparison.description %}
- {{ comparison.description }}
- {% endif %}
- {% if comparison.storage %}
- Storage: {{ comparison.storage }} GB
- {% endif %}
- {% if comparison.replicas %}
- Replicas: {{ comparison.replicas }}
- {% endif %}
- {% if comparison.ratio %}
- Price ratio: {{ comparison.ratio|floatformat:2 }}x
- {% endif %}
-
- |
-
- {{ comparison.amount|floatformat:2 }} {{ comparison.currency }}
- {% if comparison.difference > 0 %}
- +{{ comparison.difference|floatformat:2 }}
- {% elif comparison.difference < 0 %}
- {{ comparison.difference|floatformat:2 }}
- {% endif %}
- |
-
- {% endfor %}
- {% endif %}
- {% endfor %}
-
-
-
-
- {# Price Chart #}
-
-
Price Chart - Units vs Final Price
-
-
-
-
-
-
{{ pricing_data|length }} variants for {{ service_level }} in {{ group_name }}
- {% else %}
-
No pricing variants available for {{ service_level }} in {{ group_name }}.
- {% endif %}
-
- {% empty %}
-
No service levels with pricing data found for group: {{ group_name }}.
- {% endfor %}
-
- {% endfor %}
- {% else %}
-
-
No pricing data available
-
{% if filter_cloud_provider or filter_service or filter_compute_plan_group or filter_service_level %}No data matches the selected filters. Try adjusting your filter criteria.{% else %}Please ensure you have active compute plans with prices and VSHNAppCat price configurations.{% endif %}
-
- {% endif %}
-
-
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/hub/services/urls.py b/hub/services/urls.py
index 552ead5..dc4111e 100644
--- a/hub/services/urls.py
+++ b/hub/services/urls.py
@@ -22,9 +22,4 @@ urlpatterns = [
path("contact/thank-you/", views.thank_you, name="thank_you"),
path("contact-form/", views.contact_form, name="contact_form"),
path("subscribe/", views.subscribe, name="subscribe"),
- path(
- "pricelist/",
- views.pricelist,
- name="pricelist",
- ),
]
diff --git a/hub/services/views/__init__.py b/hub/services/views/__init__.py
index 0af9c30..6215d7a 100644
--- a/hub/services/views/__init__.py
+++ b/hub/services/views/__init__.py
@@ -5,4 +5,3 @@ from .providers import *
from .services import *
from .pages import *
from .subscriptions import *
-from .pricelist import *
diff --git a/hub/services/views/offerings.py b/hub/services/views/offerings.py
index c6e13a9..2414c51 100644
--- a/hub/services/views/offerings.py
+++ b/hub/services/views/offerings.py
@@ -1,21 +1,6 @@
from django.shortcuts import render, get_object_or_404
from django.db.models import Q
-from hub.services.models import (
- ServiceOffering,
- CloudProvider,
- Category,
- Service,
- ComputePlan,
- VSHNAppCatPrice,
-)
-import re
-from collections import defaultdict
-
-
-def natural_sort_key(name):
- """Extract numeric part from compute plan name for natural sorting"""
- match = re.search(r"compute-std-(\d+)", name)
- return int(match.group(1)) if match else 0
+from hub.services.models import ServiceOffering, CloudProvider, Category, Service
def offering_list(request):
@@ -79,174 +64,7 @@ def offering_detail(request, provider_slug, service_slug):
service__slug=service_slug,
)
- pricing_data_by_group_and_service_level = None
-
- # Generate pricing data for VSHN offerings
- if offering.msp == "VS":
- pricing_data_by_group_and_service_level = generate_pricing_data(offering)
-
context = {
"offering": offering,
- "pricing_data_by_group_and_service_level": pricing_data_by_group_and_service_level,
}
return render(request, "services/offering_detail.html", context)
-
-
-def generate_pricing_data(offering):
- """Generate pricing data for a specific offering and cloud provider"""
- # Fetch compute plans for this cloud provider
- compute_plans = (
- ComputePlan.objects.filter(active=True, cloud_provider=offering.cloud_provider)
- .select_related("cloud_provider", "group")
- .prefetch_related("prices")
- .order_by("group__order", "group__name")
- )
-
- # Apply natural sorting for compute plan names
- compute_plans = sorted(
- compute_plans,
- key=lambda x: (
- x.group.order if x.group else 999,
- x.group.name if x.group else "ZZZ",
- natural_sort_key(x.name),
- ),
- )
-
- # Fetch pricing for this specific service
- try:
- appcat_price = (
- VSHNAppCatPrice.objects.select_related("service", "discount_model")
- .prefetch_related("base_fees", "unit_rates", "discount_model__tiers")
- .get(service=offering.service)
- )
- except VSHNAppCatPrice.DoesNotExist:
- return None
-
- pricing_data_by_group_and_service_level = defaultdict(lambda: defaultdict(list))
- processed_combinations = set()
-
- # Generate pricing combinations for each compute plan
- for plan in compute_plans:
- plan_currencies = set(plan.prices.values_list("currency", flat=True))
-
- # Determine units based on variable unit type
- if appcat_price.variable_unit == VSHNAppCatPrice.VariableUnit.RAM:
- units = int(plan.ram)
- elif appcat_price.variable_unit == VSHNAppCatPrice.VariableUnit.CPU:
- units = int(plan.vcpus)
- else:
- continue
-
- base_fee_currencies = set(
- appcat_price.base_fees.values_list("currency", flat=True)
- )
-
- service_levels = appcat_price.unit_rates.values_list(
- "service_level", flat=True
- ).distinct()
-
- for service_level in service_levels:
- unit_rate_currencies = set(
- appcat_price.unit_rates.filter(service_level=service_level).values_list(
- "currency", flat=True
- )
- )
-
- # Find currencies that exist across all pricing components
- matching_currencies = plan_currencies.intersection(
- base_fee_currencies
- ).intersection(unit_rate_currencies)
-
- if not matching_currencies:
- continue
-
- for currency in matching_currencies:
- combination_key = (
- plan.name,
- service_level,
- currency,
- )
-
- # Skip if combination already processed
- if combination_key in processed_combinations:
- continue
-
- processed_combinations.add(combination_key)
-
- # Get pricing components
- compute_plan_price = plan.get_price(currency)
- base_fee = appcat_price.get_base_fee(currency)
- unit_rate = appcat_price.get_unit_rate(currency, service_level)
-
- # Skip if any pricing component is missing
- if any(
- price is None for price in [compute_plan_price, base_fee, unit_rate]
- ):
- continue
-
- # Calculate replica enforcement based on service level
- if service_level == VSHNAppCatPrice.ServiceLevel.GUARANTEED:
- replica_enforce = appcat_price.ha_replica_min
- else:
- replica_enforce = 1
-
- total_units = units * replica_enforce
- standard_sla_price = base_fee + (total_units * unit_rate)
-
- # Apply discount if available
- if appcat_price.discount_model and appcat_price.discount_model.active:
- discounted_price = appcat_price.discount_model.calculate_discount(
- unit_rate, total_units
- )
- sla_price = base_fee + discounted_price
- else:
- sla_price = standard_sla_price
-
- final_price = compute_plan_price + sla_price
- service_level_display = dict(VSHNAppCatPrice.ServiceLevel.choices)[
- service_level
- ]
-
- group_name = plan.group.name if plan.group else "No Group"
-
- # Add pricing data to the grouped structure
- pricing_data_by_group_and_service_level[group_name][
- service_level_display
- ].append(
- {
- "compute_plan": plan.name,
- "compute_plan_group": group_name,
- "compute_plan_group_description": (
- plan.group.description if plan.group else ""
- ),
- "vcpus": plan.vcpus,
- "ram": plan.ram,
- "currency": currency,
- "compute_plan_price": compute_plan_price,
- "sla_price": sla_price,
- "final_price": final_price,
- }
- )
-
- # Order groups correctly, placing "No Group" last
- ordered_groups = {}
- all_group_names = list(pricing_data_by_group_and_service_level.keys())
-
- if "No Group" in all_group_names:
- all_group_names.remove("No Group")
- all_group_names.append("No Group")
-
- for group_name_key in all_group_names:
- ordered_groups[group_name_key] = pricing_data_by_group_and_service_level[
- group_name_key
- ]
-
- # Convert defaultdicts to regular dicts for the template
- final_context_data = {}
- for group_key, service_levels_dict in ordered_groups.items():
- final_context_data[group_key] = {
- sl_key: list(plans_list)
- for sl_key, plans_list in service_levels_dict.items()
- }
-
- return final_context_data
diff --git a/hub/services/views/pricelist.py b/hub/services/views/pricelist.py
deleted file mode 100644
index 6d59c79..0000000
--- a/hub/services/views/pricelist.py
+++ /dev/null
@@ -1,357 +0,0 @@
-import re
-
-from django.shortcuts import render
-from collections import defaultdict
-from hub.services.models import (
- ComputePlan,
- VSHNAppCatPrice,
- ExternalPricePlans,
- StoragePlan,
-)
-from django.contrib.admin.views.decorators import staff_member_required
-from django.db import models
-
-
-def natural_sort_key(name):
- """Extract numeric part from compute plan name for natural sorting"""
- match = re.search(r"compute-std-(\d+)", name)
- return int(match.group(1)) if match else 0
-
-
-def get_external_price_comparisons(plan, appcat_price, currency, service_level):
- """Get external price comparisons for a specific compute plan and service"""
- try:
- # Filter by service level if external price has one set, ignore currency for comparison
- external_prices = ExternalPricePlans.objects.filter(
- compare_to=plan, service=appcat_price.service
- ).select_related("cloud_provider")
-
- # Filter by service level if the external price has it configured
- if service_level:
- external_prices = external_prices.filter(
- models.Q(service_level=service_level)
- | models.Q(service_level__isnull=True)
- )
-
- return external_prices
- except Exception:
- return []
-
-
-@staff_member_required
-def pricelist(request):
- """Generate comprehensive price list grouped by compute plan groups and service levels"""
- # Get filter parameters from request
- show_discount_details = request.GET.get("discount_details", "").lower() == "true"
- show_price_comparison = request.GET.get("price_comparison", "").lower() == "true"
- filter_cloud_provider = request.GET.get("cloud_provider", "")
- filter_service = request.GET.get("service", "")
- filter_compute_plan_group = request.GET.get("compute_plan_group", "")
- filter_service_level = request.GET.get("service_level", "")
-
- # Fetch all active compute plans with related data
- compute_plans = (
- ComputePlan.objects.filter(active=True)
- .select_related("cloud_provider", "group")
- .prefetch_related("prices")
- .order_by("group__order", "group__name", "cloud_provider__name")
- )
-
- # Apply compute plan filters
- if filter_cloud_provider:
- compute_plans = compute_plans.filter(cloud_provider__name=filter_cloud_provider)
- if filter_compute_plan_group:
- if filter_compute_plan_group == "No Group":
- compute_plans = compute_plans.filter(group__isnull=True)
- else:
- compute_plans = compute_plans.filter(group__name=filter_compute_plan_group)
-
- # Apply natural sorting for compute plan names
- compute_plans = sorted(
- compute_plans,
- key=lambda x: (
- x.group.order if x.group else 999, # No group plans at the end
- x.group.name if x.group else "ZZZ",
- x.cloud_provider.name,
- natural_sort_key(x.name),
- ),
- )
-
- # Fetch all appcat price configurations
- appcat_prices = (
- VSHNAppCatPrice.objects.all()
- .select_related("service", "discount_model")
- .prefetch_related("base_fees", "unit_rates", "discount_model__tiers")
- .order_by("service__name")
- )
-
- # Apply service filter
- if filter_service:
- appcat_prices = appcat_prices.filter(service__name=filter_service)
-
- pricing_data_by_group_and_service_level = defaultdict(lambda: defaultdict(list))
- processed_combinations = set()
-
- # Generate pricing combinations for each compute plan and service
- for plan in compute_plans:
- plan_currencies = set(plan.prices.values_list("currency", flat=True))
-
- for appcat_price in appcat_prices:
- # Determine units based on variable unit type
- if appcat_price.variable_unit == VSHNAppCatPrice.VariableUnit.RAM:
- units = int(plan.ram)
- elif appcat_price.variable_unit == VSHNAppCatPrice.VariableUnit.CPU:
- units = int(plan.vcpus)
- else:
- continue
-
- base_fee_currencies = set(
- appcat_price.base_fees.values_list("currency", flat=True)
- )
-
- service_levels = appcat_price.unit_rates.values_list(
- "service_level", flat=True
- ).distinct()
-
- # Apply service level filter
- if filter_service_level:
- service_levels = [
- sl
- for sl in service_levels
- if dict(VSHNAppCatPrice.ServiceLevel.choices)[sl]
- == filter_service_level
- ]
-
- for service_level in service_levels:
- unit_rate_currencies = set(
- appcat_price.unit_rates.filter(
- service_level=service_level
- ).values_list("currency", flat=True)
- )
-
- # Find currencies that exist across all pricing components
- matching_currencies = plan_currencies.intersection(
- base_fee_currencies
- ).intersection(unit_rate_currencies)
-
- if not matching_currencies:
- continue
-
- for currency in matching_currencies:
- combination_key = (
- plan.cloud_provider.name,
- plan.name,
- appcat_price.service.name,
- service_level,
- currency,
- )
-
- # Skip if combination already processed
- if combination_key in processed_combinations:
- continue
-
- processed_combinations.add(combination_key)
-
- # Get pricing components
- compute_plan_price = plan.get_price(currency)
- base_fee = appcat_price.get_base_fee(currency)
- unit_rate = appcat_price.get_unit_rate(currency, service_level)
-
- # Skip if any pricing component is missing
- if any(
- price is None
- for price in [compute_plan_price, base_fee, unit_rate]
- ):
- continue
-
- # Calculate replica enforcement based on service level
- if service_level == VSHNAppCatPrice.ServiceLevel.GUARANTEED:
- replica_enforce = appcat_price.ha_replica_min
- else:
- replica_enforce = 1
-
- total_units = units * replica_enforce
- standard_sla_price = base_fee + (total_units * unit_rate)
-
- # Apply discount if available
- discount_breakdown = None
- if (
- appcat_price.discount_model
- and appcat_price.discount_model.active
- ):
- discounted_price = (
- appcat_price.discount_model.calculate_discount(
- unit_rate, total_units
- )
- )
- sla_price = base_fee + discounted_price
- discount_savings = standard_sla_price - sla_price
- discount_percentage = (
- (discount_savings / standard_sla_price) * 100
- if standard_sla_price > 0
- else 0
- )
- discount_breakdown = (
- appcat_price.discount_model.get_discount_breakdown(
- unit_rate, total_units
- )
- )
- else:
- sla_price = standard_sla_price
- discounted_price = total_units * unit_rate
- discount_savings = 0
- discount_percentage = 0
-
- final_price = compute_plan_price + sla_price
- service_level_display = dict(VSHNAppCatPrice.ServiceLevel.choices)[
- service_level
- ]
-
- # Get external price comparisons if enabled
- external_comparisons = []
- if show_price_comparison:
- external_prices = get_external_price_comparisons(
- plan, appcat_price, currency, service_level
- )
- for ext_price in external_prices:
- # Calculate price difference using external price currency
- difference = ext_price.amount - final_price
- ratio = (
- ext_price.amount / final_price if final_price > 0 else 0
- )
-
- external_comparisons.append(
- {
- "plan_name": ext_price.plan_name,
- "provider": ext_price.cloud_provider.name,
- "description": ext_price.description,
- "amount": ext_price.amount,
- "currency": ext_price.currency, # Use external price currency
- "vcpus": ext_price.vcpus,
- "ram": ext_price.ram,
- "storage": ext_price.storage,
- "replicas": ext_price.replicas,
- "difference": difference,
- "ratio": ratio,
- "source": ext_price.source,
- "date_retrieved": ext_price.date_retrieved,
- }
- )
-
- group_name = plan.group.name if plan.group else "No Group"
-
- # Get storage plans for this cloud provider
- storage_plans = StoragePlan.objects.filter(
- cloud_provider=plan.cloud_provider
- ).prefetch_related("prices")
-
- # Add pricing data to the grouped structure
- pricing_data_by_group_and_service_level[group_name][
- service_level_display
- ].append(
- {
- "cloud_provider": plan.cloud_provider.name,
- "service": appcat_price.service.name,
- "compute_plan": plan.name,
- "compute_plan_group": group_name,
- "compute_plan_group_description": (
- plan.group.description if plan.group else ""
- ),
- "compute_plan_group_node_label": (
- plan.group.node_label if plan.group else ""
- ),
- "storage_plans": storage_plans,
- "vcpus": plan.vcpus,
- "ram": plan.ram,
- "cpu_mem_ratio": plan.cpu_mem_ratio,
- "term": plan.get_term_display(),
- "currency": currency,
- "compute_plan_price": compute_plan_price,
- "variable_unit": appcat_price.get_variable_unit_display(),
- "units": units,
- "replica_enforce": replica_enforce,
- "total_units": total_units,
- "service_level": service_level_display,
- "sla_base": base_fee,
- "sla_per_unit": unit_rate,
- "sla_price": sla_price,
- "standard_sla_price": standard_sla_price,
- "discounted_sla_price": (
- base_fee + discounted_price
- if appcat_price.discount_model
- and appcat_price.discount_model.active
- else None
- ),
- "discount_savings": discount_savings,
- "discount_percentage": discount_percentage,
- "discount_breakdown": discount_breakdown,
- "final_price": final_price,
- "discount_model": (
- appcat_price.discount_model.name
- if appcat_price.discount_model
- else None
- ),
- "has_discount": bool(
- appcat_price.discount_model
- and appcat_price.discount_model.active
- ),
- "external_comparisons": external_comparisons,
- }
- )
-
- # Order groups correctly, placing "No Group" last
- ordered_groups_intermediate = {}
- all_group_names = list(pricing_data_by_group_and_service_level.keys())
-
- if "No Group" in all_group_names:
- all_group_names.remove("No Group")
- all_group_names.append("No Group")
-
- for group_name_key in all_group_names:
- ordered_groups_intermediate[group_name_key] = (
- pricing_data_by_group_and_service_level[group_name_key]
- )
-
- # Convert defaultdicts to regular dicts for the template
- final_context_data = {}
- for group_key, service_levels_dict in ordered_groups_intermediate.items():
- final_context_data[group_key] = {
- sl_key: list(plans_list)
- for sl_key, plans_list in service_levels_dict.items()
- }
-
- # Get filter options for dropdowns
- all_cloud_providers = (
- ComputePlan.objects.filter(active=True)
- .values_list("cloud_provider__name", flat=True)
- .distinct()
- .order_by("cloud_provider__name")
- )
- all_services = (
- VSHNAppCatPrice.objects.values_list("service__name", flat=True)
- .distinct()
- .order_by("service__name")
- )
- all_compute_plan_groups = list(
- ComputePlan.objects.filter(active=True, group__isnull=False)
- .values_list("group__name", flat=True)
- .distinct()
- .order_by("group__name")
- )
- all_compute_plan_groups.append("No Group") # Add option for plans without groups
- all_service_levels = [choice[1] for choice in VSHNAppCatPrice.ServiceLevel.choices]
-
- context = {
- "pricing_data_by_group_and_service_level": final_context_data,
- "show_discount_details": show_discount_details,
- "show_price_comparison": show_price_comparison,
- "filter_cloud_provider": filter_cloud_provider,
- "filter_service": filter_service,
- "filter_compute_plan_group": filter_compute_plan_group,
- "filter_service_level": filter_service_level,
- "all_cloud_providers": all_cloud_providers,
- "all_services": all_services,
- "all_compute_plan_groups": all_compute_plan_groups,
- "all_service_levels": all_service_levels,
- }
- return render(request, "services/pricelist.html", context)
diff --git a/hub/settings.py b/hub/settings.py
index 7ffec5a..dad9ff9 100644
--- a/hub/settings.py
+++ b/hub/settings.py
@@ -1,6 +1,5 @@
from pathlib import Path
from environs import Env
-from import_export.formats.base_formats import CSV
env = Env()
env.read_env()
@@ -41,7 +40,6 @@ SECRET_KEY = env.str("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool("DEBUG", default=False)
-INTERNAL_IPS = ["127.0.0.1"]
# Get all allowed hosts
original_hosts = env.list("ALLOWED_HOSTS", default=[])
@@ -81,7 +79,6 @@ INSTALLED_APPS = [
"schema_viewer",
"nested_admin",
"adminsortable2",
- "import_export",
# local
"hub.services",
"hub.broker",
@@ -247,13 +244,4 @@ JAZZMIN_SETTINGS = {
],
"show_sidebar": True,
"navigation_expanded": True,
- "hide_apps": ["broker"],
- "order_with_respect_to": ["services", "auth"],
- "changeform_format_overrides": {
- "services.ProgressiveDiscountModel": "single",
- "services.VSHNAppCatPrice": "single",
- },
- "related_modal_active": True,
}
-
-IMPORT_EXPORT_FORMATS = [CSV]
diff --git a/pyproject.toml b/pyproject.toml
index c1cd69f..84f0dd2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,6 @@ requires-python = ">=3.13"
dependencies = [
"django>=5.2",
"django-admin-sortable2>=2.2.4",
- "django-import-export>=4.3.7",
"django-jazzmin>=3.0.1",
"django-nested-admin>=4.1.1",
"django-prose-editor[sanitize]>=0.10.3",
diff --git a/uv.lock b/uv.lock
index 9f8cfae..5184ef3 100644
--- a/uv.lock
+++ b/uv.lock
@@ -11,15 +11,6 @@ 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" },
]
-[[package]]
-name = "diff-match-patch"
-version = "20241021"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0e/ad/32e1777dd57d8e85fa31e3a243af66c538245b8d64b7265bec9a61f2ca33/diff_match_patch-20241021.tar.gz", hash = "sha256:beae57a99fa48084532935ee2968b8661db861862ec82c6f21f4acdd6d835073", size = 39962, upload-time = "2024-10-21T19:41:21.094Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f7/bb/2aa9b46a01197398b901e458974c20ed107935c26e44e37ad5b0e5511e44/diff_match_patch-20241021-py3-none-any.whl", hash = "sha256:93cea333fb8b2bc0d181b0de5e16df50dd344ce64828226bda07728818936782", size = 43252, upload-time = "2024-10-21T19:41:19.914Z" },
-]
-
[[package]]
name = "dj-database-url"
version = "2.3.0"
@@ -90,20 +81,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/48/90/01755e4a42558b763f7021e9369aa6aa94c2ede7313deed56cb7483834ab/django_cache_url-3.4.5-py2.py3-none-any.whl", hash = "sha256:5f350759978483ab85dc0e3e17b3d53eed3394a28148f6bf0f53d11d0feb5b3c", size = 4760, upload-time = "2023-12-04T17:19:44.355Z" },
]
-[[package]]
-name = "django-import-export"
-version = "4.3.7"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "diff-match-patch" },
- { name = "django" },
- { name = "tablib" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/57/ae/52275e8a49a963468f9f807c24df17416fad0220169a8e5d7bfd4778f17f/django_import_export-4.3.7.tar.gz", hash = "sha256:bd3fe0aa15a2bce9de4be1a2f882e2c4539fdbfdfa16f2052c98dd7aec0f085c", size = 2222150, upload-time = "2025-02-25T12:38:47.076Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9d/ad/b1f4aef18fd4ab86ce68f3f0fff0e9c14e5fd2c866c47f0b1cfb9ccd85c8/django_import_export-4.3.7-py3-none-any.whl", hash = "sha256:5514d09636e84e823a42cd5e79292f70f20d6d2feed117a145f5b64a5b44f168", size = 142815, upload-time = "2025-02-25T12:38:43.654Z" },
-]
-
[[package]]
name = "django-jazzmin"
version = "3.0.1"
@@ -315,7 +292,6 @@ source = { virtual = "." }
dependencies = [
{ name = "django" },
{ name = "django-admin-sortable2" },
- { name = "django-import-export" },
{ name = "django-jazzmin" },
{ name = "django-nested-admin" },
{ name = "django-prose-editor", extra = ["sanitize"] },
@@ -336,7 +312,6 @@ requires-dist = [
{ name = "django", specifier = ">=5.2" },
{ name = "django-admin-sortable2", specifier = ">=2.2.4" },
{ name = "django-browser-reload", marker = "extra == 'dev'", specifier = "~=1.13" },
- { name = "django-import-export", specifier = ">=4.3.7" },
{ name = "django-jazzmin", specifier = ">=3.0.1" },
{ name = "django-nested-admin", specifier = ">=4.1.1" },
{ name = "django-prose-editor", extras = ["sanitize"], specifier = ">=0.10.3" },
@@ -357,15 +332,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" },
]
-[[package]]
-name = "tablib"
-version = "3.8.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/09/cc/fe19d9c2ac1088794a51fc72f49b7226f88a0361f924fb3d17a9ec80e657/tablib-3.8.0.tar.gz", hash = "sha256:94d8bcdc65a715a0024a6d5b701a5f31e45bd159269e62c73731de79f048db2b", size = 122247, upload-time = "2025-01-22T15:29:27.276Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/95/6542f54ebd90539b12ed6189cb54a6550a28407b1c503c2e55190c29a4c9/tablib-3.8.0-py3-none-any.whl", hash = "sha256:35bdb9d4ec7052232f8803908f9c7a9c3c65807188b70618fa7a7d8ccd560b4d", size = 47935, upload-time = "2025-01-22T15:28:44.499Z" },
-]
-
[[package]]
name = "typing-extensions"
version = "4.12.2"