From 600eee2b4c7544095a939c33af26ae6978169695 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 27 Jan 2025 15:54:37 +0100 Subject: [PATCH] csp detail page --- .gitignore | 1 + .../cloud_provider_logos/cloudscale.png | Bin .../cloudscale_oZ4GYnx.png | Bin 0 -> 60424 bytes .../exoscale-RGB-logo-fullcolor-whiteBG.jpg | Bin .../service_logos/postgresql.png | Bin .../service_logos/postgresql_Nq7hKyN.png | Bin hub/services/admin.py | 3 +- .../migrations/0004_cloudprovider_slug.py | 40 +++++++++++ hub/services/models.py | 9 +++ hub/services/templates/services/base.html | 15 ++++- .../templates/services/provider_detail.html | 62 ++++++++++++++++++ .../templates/services/service_detail.html | 11 +++- .../templates/services/service_list.html | 14 +++- hub/services/urls.py | 1 + hub/services/views.py | 10 +++ 15 files changed, 159 insertions(+), 7 deletions(-) rename hub/{media => media_blubb}/cloud_provider_logos/cloudscale.png (100%) create mode 100644 hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png rename hub/{media => media_blubb}/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg (100%) rename hub/{media => media_blubb}/service_logos/postgresql.png (100%) rename hub/{media => media_blubb}/service_logos/postgresql_Nq7hKyN.png (100%) create mode 100644 hub/services/migrations/0004_cloudprovider_slug.py create mode 100644 hub/services/templates/services/provider_detail.html diff --git a/.gitignore b/.gitignore index 85f96b4..37218ad 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ wheels/ # Project specifics .env hub/db.sqlite3 +hub/media/ \ No newline at end of file diff --git a/hub/media/cloud_provider_logos/cloudscale.png b/hub/media_blubb/cloud_provider_logos/cloudscale.png similarity index 100% rename from hub/media/cloud_provider_logos/cloudscale.png rename to hub/media_blubb/cloud_provider_logos/cloudscale.png diff --git a/hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png b/hub/media_blubb/cloud_provider_logos/cloudscale_oZ4GYnx.png new file mode 100644 index 0000000000000000000000000000000000000000..04c0658cc6b363a1a0b7e9ee250f8dc0ff5d5e7b GIT binary patch literal 60424 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

9QO89yzuCBXd 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 YqfP^7V!Z literal 0 HcmV?d00001 diff --git a/hub/media/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg b/hub/media_blubb/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg similarity index 100% rename from hub/media/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg rename to hub/media_blubb/cloud_provider_logos/exoscale-RGB-logo-fullcolor-whiteBG.jpg diff --git a/hub/media/service_logos/postgresql.png b/hub/media_blubb/service_logos/postgresql.png similarity index 100% rename from hub/media/service_logos/postgresql.png rename to hub/media_blubb/service_logos/postgresql.png diff --git a/hub/media/service_logos/postgresql_Nq7hKyN.png b/hub/media_blubb/service_logos/postgresql_Nq7hKyN.png similarity index 100% rename from hub/media/service_logos/postgresql_Nq7hKyN.png rename to hub/media_blubb/service_logos/postgresql_Nq7hKyN.png diff --git a/hub/services/admin.py b/hub/services/admin.py index 4481180..2cebb43 100644 --- a/hub/services/admin.py +++ b/hub/services/admin.py @@ -14,8 +14,9 @@ class CategoryAdmin(admin.ModelAdmin): @admin.register(CloudProvider) class CloudProviderAdmin(admin.ModelAdmin): - list_display = ("name", "logo_preview") + list_display = ("name", "slug", "logo_preview") search_fields = ("name",) + prepopulated_fields = {"slug": ("name",)} def logo_preview(self, obj): if obj.logo: diff --git a/hub/services/migrations/0004_cloudprovider_slug.py b/hub/services/migrations/0004_cloudprovider_slug.py new file mode 100644 index 0000000..c273610 --- /dev/null +++ b/hub/services/migrations/0004_cloudprovider_slug.py @@ -0,0 +1,40 @@ +# Generated by Django 5.1.5 on 2025-01-27 14:49 + +from django.db import migrations, models +from django.utils.text import slugify + + +def generate_provider_slugs(apps, schema_editor): + CloudProvider = apps.get_model("services", "CloudProvider") + for provider in CloudProvider.objects.all(): + provider.slug = slugify(provider.name) + counter = 1 + while CloudProvider.objects.filter(slug=provider.slug).exists(): + provider.slug = f"{slugify(provider.name)}-{counter}" + counter += 1 + provider.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("services", "0003_category_service_categories"), + ] + + operations = [ + migrations.AddField( + model_name="cloudprovider", + name="slug", + field=models.SlugField(unique=True, null=True), + preserve_default=False, + ), + migrations.RunPython( + generate_provider_slugs, reverse_code=migrations.RunPython.noop + ), + migrations.AlterField( + model_name="cloudprovider", + name="slug", + field=models.SlugField(unique=True), + preserve_default=False, + ), + ] diff --git a/hub/services/models.py b/hub/services/models.py index c6338ae..dc94232 100644 --- a/hub/services/models.py +++ b/hub/services/models.py @@ -46,6 +46,7 @@ class Category(models.Model): class CloudProvider(models.Model): name = models.CharField(max_length=100) + slug = models.SlugField(unique=True) description = ProseEditorField(blank=True) logo = models.ImageField( upload_to="cloud_provider_logos/", @@ -57,6 +58,14 @@ class CloudProvider(models.Model): def __str__(self): return self.name + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.name) + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("services:provider_detail", kwargs={"slug": self.slug}) + class Country(models.Model): name = models.CharField(max_length=100) diff --git a/hub/services/templates/services/base.html b/hub/services/templates/services/base.html index 7383a28..0a455a0 100644 --- a/hub/services/templates/services/base.html +++ b/hub/services/templates/services/base.html @@ -11,7 +11,18 @@

@@ -28,10 +39,12 @@ overflow-wrap: break-word; word-wrap: break-word; } + .rich-text-content img { max-width: 100%; height: auto; } + .description-preview img { max-width: 100%; height: auto; diff --git a/hub/services/templates/services/provider_detail.html b/hub/services/templates/services/provider_detail.html new file mode 100644 index 0000000..1ace4cd --- /dev/null +++ b/hub/services/templates/services/provider_detail.html @@ -0,0 +1,62 @@ +{% extends 'services/base.html' %} + +{% block content %} +
+
+
+ {% if provider.logo %} + {{ provider.name }} logo + {% endif %} +
+

{{ provider.name }}

+
+ {{ provider.description|safe }} +
+
+
+ +

Available Services

+
+ {% for service in services %} +
+
+
+
+ {% if service.logo %} + {{ service.name }} logo + {% endif %} +
+
{{ service.name }}
+
+
+
+ {{ service.description|safe|truncatewords_html:30 }} +
+
+ {% for category in service.categories.all %} + {{ category.full_path }} + {% endfor %} +
+

+ + Service Level: {{ service.service_level.name }}
+ Price: ${{ service.price }} +
+

+ View Details +
+
+
+ {% empty %} +
+
+ No services available from this provider yet. +
+
+ {% endfor %} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/hub/services/templates/services/service_detail.html b/hub/services/templates/services/service_detail.html index 6bb0d5d..82ad62f 100644 --- a/hub/services/templates/services/service_detail.html +++ b/hub/services/templates/services/service_detail.html @@ -17,9 +17,16 @@

{{ service.name }}

{% if service.cloud_provider.logo %} - {{ service.cloud_provider.name }} logo + + {{ service.cloud_provider.name }} logo + + {% else %} +
+ {{ service.cloud_provider.name }} +
{% endif %} -
{{ service.cloud_provider.name }}
diff --git a/hub/services/templates/services/service_list.html b/hub/services/templates/services/service_list.html index 3944f3b..d6db25e 100644 --- a/hub/services/templates/services/service_list.html +++ b/hub/services/templates/services/service_list.html @@ -86,18 +86,26 @@
{{ service.name }}
{% if service.cloud_provider.logo %} - {{ service.cloud_provider.name }} logo + + {{ service.cloud_provider.name }} logo + + {% else %} +
+ {{ service.cloud_provider.name }} +
{% endif %} -
{{ service.cloud_provider.name }}
-

{{ service.description|safe|truncatewords:30 }}

{% for category in service.categories.all %} {{ category.full_path }} {% endfor %}
+

{{ service.description|safe|truncatewords:30 }}

+

Service Level: {{ service.service_level.name }}
diff --git a/hub/services/urls.py b/hub/services/urls.py index 6964fe3..88673da 100644 --- a/hub/services/urls.py +++ b/hub/services/urls.py @@ -6,4 +6,5 @@ app_name = "services" urlpatterns = [ path("", views.service_list, name="service_list"), path("service//", views.service_detail, name="service_detail"), + path("provider//", views.provider_detail, name="provider_detail"), ] diff --git a/hub/services/views.py b/hub/services/views.py index 134cae3..efa2df0 100644 --- a/hub/services/views.py +++ b/hub/services/views.py @@ -49,3 +49,13 @@ def service_list(request): def service_detail(request, pk): service = get_object_or_404(Service, pk=pk) return render(request, "services/service_detail.html", {"service": service}) + + +def provider_detail(request, slug): + provider = get_object_or_404(CloudProvider, slug=slug) + services = Service.objects.filter(cloud_provider=provider) + context = { + "provider": provider, + "services": services, + } + return render(request, "services/provider_detail.html", context)