14731 change to table

This commit is contained in:
Arthur Hanson 2024-07-15 15:48:50 +07:00
parent 6136154588
commit e9dd15443c
6 changed files with 96 additions and 151 deletions

View File

@ -4,4 +4,3 @@ from .data import *
from .jobs import * from .jobs import *
from .tasks import * from .tasks import *
from .plugins import * from .plugins import *
from .plugin import *

View File

@ -1,40 +0,0 @@
from datetime import datetime
from django.contrib.humanize.templatetags.humanize import naturalday
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from netbox.tables import BaseTable
__all__ = (
'CertifiedPluginTable',
)
class CertifiedPluginTable(BaseTable):
version = tables.Column(
verbose_name=_('Version')
)
last_updated = tables.Column(
accessor=tables.A('date'),
verbose_name=_('Last Updated')
)
min_version = tables.Column(
accessor=tables.A('netbox_min_version'),
verbose_name=_('Minimum NetBox Version')
)
max_version = tables.Column(
accessor=tables.A('netbox_max_version'),
verbose_name=_('Maximum NetBox Version')
)
class Meta(BaseTable.Meta):
empty_text = _('No plugin data found')
fields = (
'version', 'last_updated', 'min_version', 'max_version',
)
default_columns = (
'version', 'last_updated', 'min_version', 'max_version',
)
orderable = False
def render_last_updated(self, value, record):
return naturalday(datetime.fromisoformat(value))

View File

@ -1,13 +1,17 @@
from datetime import datetime
import django_tables2 as tables import django_tables2 as tables
from django.contrib.humanize.templatetags.humanize import naturalday
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from netbox.tables import BaseTable from netbox.tables import BaseTable
__all__ = ( __all__ = (
'PluginTable', 'CertifiedPluginTable',
'InstalledPluginTable',
'PluginVersionTable',
) )
class PluginTable(BaseTable): class InstalledPluginTable(BaseTable):
name = tables.Column( name = tables.Column(
accessor=tables.A('verbose_name'), accessor=tables.A('verbose_name'),
verbose_name=_('Name') verbose_name=_('Name')
@ -37,3 +41,71 @@ class PluginTable(BaseTable):
default_columns = ( default_columns = (
'name', 'version', 'package', 'description', 'name', 'version', 'package', 'description',
) )
class PluginVersionTable(BaseTable):
version = tables.Column(
verbose_name=_('Version')
)
last_updated = tables.Column(
accessor=tables.A('date'),
verbose_name=_('Last Updated')
)
min_version = tables.Column(
accessor=tables.A('netbox_min_version'),
verbose_name=_('Minimum NetBox Version')
)
max_version = tables.Column(
accessor=tables.A('netbox_max_version'),
verbose_name=_('Maximum NetBox Version')
)
class Meta(BaseTable.Meta):
empty_text = _('No plugin data found')
fields = (
'version', 'last_updated', 'min_version', 'max_version',
)
default_columns = (
'version', 'last_updated', 'min_version', 'max_version',
)
orderable = False
def render_last_updated(self, value, record):
return naturalday(datetime.fromisoformat(value))
class CertifiedPluginTable(BaseTable):
name = tables.Column(
linkify=('core:plugin', [tables.A('slug')]),
verbose_name=_('Name')
)
status = tables.Column(
verbose_name=_('Status')
)
author = tables.Column(
verbose_name=_('Author')
)
is_local = tables.BooleanColumn(
verbose_name=_('Local')
)
is_installed = tables.BooleanColumn(
verbose_name=_('Installed')
)
is_certified = tables.BooleanColumn(
verbose_name=_('Certified')
)
created = tables.Column(
verbose_name=_('Published')
)
updated = tables.Column(
verbose_name=_('Updated')
)
class Meta(BaseTable.Meta):
empty_text = _('No plugin data found')
fields = (
'name', 'status', 'author', 'is_local', 'is_installed', 'is_certified', 'created', 'updated',
)
default_columns = (
'name', 'status', 'author', 'is_local', 'is_installed', 'is_certified', 'created', 'updated',
)

View File

@ -41,7 +41,7 @@ from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModels
from . import filtersets, forms, tables from . import filtersets, forms, tables
from .choices import PluginSortChoices, PluginStatusChoices from .choices import PluginSortChoices, PluginStatusChoices
from .models import * from .models import *
from .tables import CertifiedPluginTable from .tables import CertifiedPluginTable, PluginVersionTable
# #
@ -648,7 +648,7 @@ class SystemView(UserPassesTestMixin, View):
response['Content-Disposition'] = 'attachment; filename="netbox.json"' response['Content-Disposition'] = 'attachment; filename="netbox.json"'
return response return response
plugins_table = tables.PluginTable(plugins, orderable=False) plugins_table = tables.InstalledPluginTable(plugins, orderable=False)
plugins_table.configure(request) plugins_table.configure(request)
return render(request, 'core/system.html', { return render(request, 'core/system.html', {
@ -675,8 +675,8 @@ def get_local_plugins(plugins):
'tag_line': plugin_config.description, 'tag_line': plugin_config.description,
'description_short': plugin_config.description, 'description_short': plugin_config.description,
'author': plugin_config.author or _('Unknown Author'), 'author': plugin_config.author or _('Unknown Author'),
'created': datetime.min.replace(tzinfo=timezone.utc), 'created': None,
'updated': datetime.min.replace(tzinfo=timezone.utc), 'updated': None,
'is_local': True, 'is_local': True,
'is_installed': True, 'is_installed': True,
'is_certified': False, 'is_certified': False,
@ -768,8 +768,8 @@ def get_catalog_plugins(plugins):
def get_plugins(): def get_plugins():
if plugins := cache.get('plugins-catalog-feed'): # if plugins := cache.get('plugins-catalog-feed'):
return plugins # return plugins
plugins = {} plugins = {}
plugins = get_local_plugins(plugins) plugins = get_local_plugins(plugins)
@ -785,30 +785,22 @@ class PluginListView(UserPassesTestMixin, View):
return self.request.user.is_staff return self.request.user.is_staff
def get(self, request): def get(self, request):
sort = request.GET.get('sort', PluginSortChoices.SORT_NAME_AZ)
status = request.GET.get('status', PluginStatusChoices.STATUS_ALL)
q = request.GET.get('q', None) q = request.GET.get('q', None)
plugins = get_plugins() plugins = get_plugins()
if status == PluginStatusChoices.STATUS_INSTALLED:
plugins = [v for k, v in plugins.items() if v['is_installed']]
else:
plugins = [v for k, v in plugins.items()] plugins = [v for k, v in plugins.items()]
if sort == PluginSortChoices.SORT_NAME_ZA:
plugins = sorted(plugins, key=lambda d: d['name'], reverse=True) table = CertifiedPluginTable(plugins, user=request.user)
elif sort == PluginSortChoices.SORT_UPDATED: table.configure(request)
plugins = sorted(plugins, key=lambda d: d['updated'])
elif sort == PluginSortChoices.SORT_PUBLISHED: # If this is an HTMX request, return only the rendered table HTML
plugins = sorted(plugins, key=lambda d: d['created']) if htmx_partial(request):
else: return render(request, 'htmx/table.html', {
plugins = sorted(plugins, key=lambda d: d['name']) 'table': table,
})
return render(request, 'core/plugin_list.html', { return render(request, 'core/plugin_list.html', {
'plugins': plugins, 'table': table,
'sort_choices': dict(PluginSortChoices),
'status_choices': dict(PluginStatusChoices),
'sort': sort,
'status': status,
}) })
@ -822,7 +814,7 @@ class PluginView(UserPassesTestMixin, View):
plugins = get_plugins() plugins = get_plugins()
plugin = plugins[name] plugin = plugins[name]
table = CertifiedPluginTable(plugin['versions'], user=request.user) table = PluginVersionTable(plugin['versions'], user=request.user)
table.configure(request) table.configure(request)
return render(request, 'core/plugin.html', { return render(request, 'core/plugin.html', {

View File

@ -2,41 +2,8 @@
{% load helpers %} {% load helpers %}
{% load i18n %} {% load i18n %}
{% for plugin in plugins %}
<div class="col-md-3">
<a href="{% url 'core:plugin' plugin.slug %}">
<div class="card"> <div class="card">
<div class="card-body"> <div class="table-responsive" id="object_list">
<h5 class="card-title">{{ plugin.name }}</h5> {% include 'htmx/table.html' %}
<p>By {{ plugin.author }}</p>
<div>
{% if plugin.is_local %}
<button type="button" class="btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-server"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M3 12m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M7 8l0 .01" /><path d="M7 16l0 .01" /></svg>
Local
</button>
{% endif %}
{% if plugin.is_certified %}
<button type="button" class="btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-award"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19.496 13.983l1.966 3.406a1.001 1.001 0 0 1 -.705 1.488l-.113 .011l-.112 -.001l-2.933 -.19l-1.303 2.636a1.001 1.001 0 0 1 -1.608 .26l-.082 -.094l-.072 -.11l-1.968 -3.407a8.994 8.994 0 0 0 6.93 -3.999z" /><path d="M11.43 17.982l-1.966 3.408a1.001 1.001 0 0 1 -1.622 .157l-.076 -.1l-.064 -.114l-1.304 -2.635l-2.931 .19a1.001 1.001 0 0 1 -1.022 -1.29l.04 -.107l.05 -.1l1.968 -3.409a8.994 8.994 0 0 0 6.927 4.001z" /><path d="M12 2l.24 .004a7 7 0 0 1 6.76 6.996l-.003 .193l-.007 .192l-.018 .245l-.026 .242l-.024 .178a6.985 6.985 0 0 1 -.317 1.268l-.116 .308l-.153 .348a7.001 7.001 0 0 1 -12.688 -.028l-.13 -.297l-.052 -.133l-.08 -.217l-.095 -.294a6.96 6.96 0 0 1 -.093 -.344l-.06 -.271l-.049 -.271l-.02 -.139l-.039 -.323l-.024 -.365l-.006 -.292a7 7 0 0 1 6.76 -6.996l.24 -.004z" /></svg>
Certified
</button>
{% endif %}
{% if plugin.is_community %}
<button type="button" class="btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-users-group"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M8 21v-1a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v1" /><path d="M15 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M17 10h2a2 2 0 0 1 2 2v1" /><path d="M5 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M3 13v-1a2 2 0 0 1 2 -2h2" /></svg>
Community
</button>
{% endif %}
{% if plugin.is_installed %}
<button type="button" class="btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-check"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10" /></svg>
Installed
</button>
{% endif %}
</div> </div>
</div> </div>
</div>
</a>
</div>
{% endfor %}

View File

@ -31,51 +31,6 @@
</div> </div>
</div> </div>
<div class="col-auto ms-auto d-print-none">
<div class="row">
<div class="col-auto">
<div class="px-2 d-print-none">
{% trans "Status" %}
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ status_choices|get_key:status }}
</button>
<div class="dropdown-menu">
{% for level, name in status_choices.items %}
<a class="dropdown-item d-flex justify-content-between" href="{% url 'core:plugin_list' %}?status={{ level }}">
{{ name }}
{% if level == status %}<span class="badge bg-green ms-auto"></span>{% endif %}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="col-auto">
<div class="px-2 d-print-none">
{% trans "Sort" %}
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ sort_choices|get_key:sort }}
</button>
<div class="dropdown-menu">
{% for level, name in sort_choices.items %}
<a class="dropdown-item d-flex justify-content-between" href="{% url 'core:plugin_list' %}?sort={{ level }}">
{{ name }}
{% if level == sort %}<span class="badge bg-green ms-auto"></span>{% endif %}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
{# System status #} {# System status #}