Skip to content

Commit

Permalink
task/DES-2672: CMS plugins for app listings, related apps, and app va…
Browse files Browse the repository at this point in the history
…riants (#1197)

* Add CMS plugin for listing apps by category

* whitespace fixes

* remove extraneous loggging

* Add description field for app variants

* add plugin for related apps

* Add app variant selector

* filter apps/variants based on enabled status

* replace trick link

* format hrefs for app variants

* fix app href query string

* fix comments
  • Loading branch information
jarosenb authored Apr 4, 2024
1 parent c58838a commit e2abb16
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 0 deletions.
1 change: 1 addition & 0 deletions designsafe/apps/workspace/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def get_fieldsets(self, request, obj=None):
{
"fields": (
"label",
"description",
"enabled",
)
},
Expand Down
105 changes: 105 additions & 0 deletions designsafe/apps/workspace/cms_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""CMS plugins for Tools & Applications pages."""

import logging
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from designsafe.apps.workspace.models.app_entries import (
AppListingEntry,
)
from designsafe.apps.workspace.models.app_cms_plugins import (
AppCategoryListingPlugin,
RelatedAppsPlugin,
AppVariantsPlugin,
)

logger = logging.getLogger(__name__)


class AppCategoryListing(CMSPluginBase):
"""CMS plugin to render the list of apps for a given category."""

model = AppCategoryListingPlugin
name = "App Category Listing"
module = "Tools & Applications"
render_template = "designsafe/apps/workspace/app_listing_plugin.html"
cache = False

def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
listing_entries = AppListingEntry.objects.filter(
category=instance.app_category, enabled=True
)
serialized_listing = [
{
"label": entry.label,
"icon": entry.icon,
"description": entry.description,
"tags": [tag.name for tag in entry.tags.all()],
"is_popular": entry.is_popular,
"is_simcenter": entry.is_simcenter,
"license_type": (
"Open Source" if entry.license_type == "OS" else "Licensed"
),
"href": entry.href,
}
for entry in listing_entries
]
context["listing"] = serialized_listing
return context


plugin_pool.register_plugin(AppCategoryListing)


class RelatedApps(CMSPluginBase):
"""CMS plugin to render related apps."""

model = RelatedAppsPlugin
name = "Related Apps"
module = "Tools & Applications"
render_template = "designsafe/apps/workspace/related_apps_plugin.html"
cache = False

def render(self, context, instance: AppListingEntry, placeholder):
context = super().render(context, instance, placeholder)
listing_entries = instance.app.related_apps.filter(enabled=True)
serialized_listing = [
{
"label": entry.label,
"icon": entry.icon,
"description": entry.description,
"tags": [tag.name for tag in entry.tags.all()],
"is_popular": entry.is_popular,
"is_simcenter": entry.is_simcenter,
"license_type": (
"Open Source" if entry.license_type == "OS" else "Licensed"
),
"href": entry.href,
}
for entry in listing_entries
]
context["listing"] = serialized_listing
return context


plugin_pool.register_plugin(RelatedApps)


class AppVariants(CMSPluginBase):
"""CMS plugin to render an apps versions/variants."""

model = AppVariantsPlugin
name = "App Version Selection"
module = "Tools & Applications"
render_template = "designsafe/apps/workspace/app_variant_plugin.html"
cache = False

def render(self, context, instance: AppListingEntry, placeholder):
context = super().render(context, instance, placeholder)
app_variants = instance.app.appvariant_set.filter(enabled=True)
context["listing"] = app_variants

return context


plugin_pool.register_plugin(AppVariants)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 4.2.6 on 2024-04-02 17:54

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("cms", "0022_auto_20180620_1551"),
("workspace", "0004_initial_app_categories_and_tags"),
]

operations = [
migrations.CreateModel(
name="AppCategoryListingPlugin",
fields=[
(
"cmsplugin_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
related_name="%(app_label)s_%(class)s",
serialize=False,
to="cms.cmsplugin",
),
),
(
"app_category",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="workspace.apptraycategory",
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.6 on 2024-04-02 22:16

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("workspace", "0005_appcategorylistingplugin"),
]

operations = [
migrations.AddField(
model_name="appvariant",
name="description",
field=models.TextField(
blank=True,
help_text="App variant description text for version overview.",
null=True,
),
),
]
42 changes: 42 additions & 0 deletions designsafe/apps/workspace/migrations/0007_relatedappsplugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 4.2.6 on 2024-04-03 15:14

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("cms", "0022_auto_20180620_1551"),
("workspace", "0006_appvariant_description"),
]

operations = [
migrations.CreateModel(
name="RelatedAppsPlugin",
fields=[
(
"cmsplugin_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
related_name="%(app_label)s_%(class)s",
serialize=False,
to="cms.cmsplugin",
),
),
(
"app",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="workspace.applistingentry",
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
42 changes: 42 additions & 0 deletions designsafe/apps/workspace/migrations/0008_appvariantsplugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 4.2.6 on 2024-04-03 18:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("cms", "0022_auto_20180620_1551"),
("workspace", "0007_relatedappsplugin"),
]

operations = [
migrations.CreateModel(
name="AppVariantsPlugin",
fields=[
(
"cmsplugin_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
related_name="%(app_label)s_%(class)s",
serialize=False,
to="cms.cmsplugin",
),
),
(
"app",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="workspace.applistingentry",
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
37 changes: 37 additions & 0 deletions designsafe/apps/workspace/models/app_cms_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Models associated with CMS plugins for Tools & Applications"""

from cms.models.pluginmodel import CMSPlugin
from django.db import models
from designsafe.apps.workspace.models.app_entries import (
AppTrayCategory,
AppListingEntry,
)


class AppCategoryListingPlugin(CMSPlugin):
"""Model for listing apps by category."""

app_category = models.ForeignKey(
to=AppTrayCategory, on_delete=models.deletion.CASCADE
)

def __str__(self):
return self.app_category.category


class RelatedAppsPlugin(CMSPlugin):
"""Model for listing related apps."""

app = models.ForeignKey(to=AppListingEntry, on_delete=models.deletion.CASCADE)

def __str__(self):
return self.app.label


class AppVariantsPlugin(CMSPlugin):
"""Model for listing related apps."""

app = models.ForeignKey(to=AppListingEntry, on_delete=models.deletion.CASCADE)

def __str__(self):
return self.app.label
14 changes: 14 additions & 0 deletions designsafe/apps/workspace/models/app_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ class AppVariant(models.Model):
blank=True,
)

description = models.TextField(
help_text="App variant description text for version overview.",
blank=True,
null=True,
)

# Tapis Apps
version = models.CharField(
help_text="The version number of the app. The app id + version denotes a unique app.",
Expand All @@ -190,6 +196,14 @@ class AppVariant(models.Model):
help_text="App variant visibility in app tray.", default=True
)

@property
def href(self):
"""Retrieve the app's URL in the Tools & Applications space"""
app_href = f"/rw/workspace/applications/{self.app_id}"
if self.version:
app_href += f"?version={self.version}"
return app_href

def __str__(self):
return f"{self.bundle.label} {self.app_id} {self.version} ({'ENABLED' if self.enabled else 'DISABLED'})"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<a class="c-app-card" href="{{app.href}}" style="width: 230px">
<h3 class="c-app-card__title"><i class="icon icon-hazmapper"></i> {{app.label}}</h3>

<p class="c-app-card__desc">{{app.description}}</p>

<ul class="c-app-card__types">
{% for tag in app.tags %}
<li>{{tag}}</li>
{% endfor %}
</ul>

<ul class="c-app-card__flags">
{% if app.is_popular %}
<li><strong>Popular</strong></li>
{% endif %}

{% if not app.is_popular and app.is_simcenter %}
<li>SimCenter</li>
{% endif %}


<li class="c-app-card__repo">{{app.license_type}}</li>
</ul>
</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h2>{{instance.app_category}}</h2>
<section style="display:flex; gap:30px; flex-wrap: wrap">
{% for app in listing %}
{% include "designsafe/apps/workspace/app_card.html" with app=app %}
{% endfor %}
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<section style="background-color: #f4f4f4; padding: 25px;">
<h3>Select a Version</h3>
{% for variant in listing %}
<article>
<div style="display:flex; align-items: center; justify-content: space-between;">
<h4>{{variant.label}}</h4>
<a href="{{variant.href}}"><button class="btn btn-success">Get Started</button></a>
</div>
<p>{{variant.description}}</p>
{% if listing.count > forloop.counter %}
<hr />
{% endif %}
</article>
{% endfor %}
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h2>Related Applications</h2>
<section style="display:flex; gap:30px; flex-wrap: wrap">
{% for app in listing %}
{% include "designsafe/apps/workspace/app_card.html" with app=app %}
{% endfor %}
</section>
3 changes: 3 additions & 0 deletions designsafe/settings/common_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@
'FormPlugin',
'MeetingFormPlugin',
'ResponsiveEmbedPlugin',
'AppCategoryListing',
'RelatedApps',
'AppVariants'
)
}
CMSPLUGIN_CASCADE_PLUGINS = [
Expand Down
Loading

0 comments on commit e2abb16

Please sign in to comment.