Coverage for extras/tables/tables.py: 95%
291 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1import json
3import django_tables2 as tables
4from django.template.defaultfilters import filesizeformat
5from django.utils.html import format_html
6from django.utils.translation import gettext_lazy as _
8from core.models import Job
9from core.tables import JobTable
10from extras.models import *
11from netbox.constants import EMPTY_TABLE_TEXT
12from netbox.events import get_event_text
13from netbox.tables import BaseTable, NetBoxTable, PrimaryModelTable, columns
15from .columns import CustomFieldStatusColumn, NotificationActionsColumn
17__all__ = (
18 'BookmarkTable',
19 'ConfigContextProfileTable',
20 'ConfigContextTable',
21 'ConfigTemplateTable',
22 'CustomFieldChoiceSetTable',
23 'CustomFieldTable',
24 'CustomLinkTable',
25 'EventRuleTable',
26 'ExportTemplateTable',
27 'ImageAttachmentTable',
28 'JournalEntryTable',
29 'NotificationGroupTable',
30 'NotificationTable',
31 'ReportResultsTable',
32 'SavedFilterTable',
33 'ScriptJobTable',
34 'ScriptResultsTable',
35 'SubscriptionTable',
36 'TableConfigTable',
37 'TagTable',
38 'TaggedItemTable',
39 'WebhookTable',
40)
42IMAGEATTACHMENT_IMAGE = """
43{% load thumbnail %}
44{% if record.image %}
45 {% thumbnail record.image "400x400" as tn %}
46 <a href="{{ record.get_absolute_url }}"
47 class="image-preview"
48 data-preview-url="{{ tn.url }}"
49 data-bs-placement="left"
50 title="{{ record.filename }}"
51 rel="noopener noreferrer"
52 target="_blank"
53 aria-label="{{ record.filename }}">
54 <i class="mdi mdi-image"></i>
55 </a>
56 {% endthumbnail %}
57{% endif %}
58<a href="{{ record.get_absolute_url }}">{{ record.filename|truncate_middle:16 }}</a>
59"""
61NOTIFICATION_ICON = """
62<span class="text-{{ value.color }} fs-3"><i class="{{ value.icon }}"></i></span>
63"""
65NOTIFICATION_LINK = """
66{% if not record.event.destructive %}
67 <a href="{% url 'extras:notification_read' pk=record.pk %}">{{ record.object_repr }}</a>
68{% else %}
69 {{ record.object_repr }}
70{% endif %}
71"""
74class CustomFieldTable(NetBoxTable):
75 name = tables.Column(
76 verbose_name=_('Name'),
77 linkify=True
78 )
79 object_types = columns.ContentTypesColumn(
80 verbose_name=_('Object Types')
81 )
82 required = columns.BooleanColumn(
83 verbose_name=_('Required'),
84 false_mark=None
85 )
86 unique = columns.BooleanColumn(
87 verbose_name=_('Validate Uniqueness'),
88 false_mark=None
89 )
90 status = CustomFieldStatusColumn(
91 verbose_name=_('Status')
92 )
93 ui_visible = columns.ChoiceFieldColumn(
94 verbose_name=_('Visible')
95 )
96 ui_editable = columns.ChoiceFieldColumn(
97 verbose_name=_('Editable')
98 )
99 description = columns.MarkdownColumn(
100 verbose_name=_('Description')
101 )
102 related_object_type = columns.ContentTypeColumn(
103 verbose_name=_('Related Object Type')
104 )
105 choice_set = tables.Column(
106 linkify=True,
107 verbose_name=_('Choice Set')
108 )
109 choices = columns.ChoicesColumn(
110 max_items=10,
111 orderable=False,
112 verbose_name=_('Choices')
113 )
114 is_cloneable = columns.BooleanColumn(
115 verbose_name=_('Is Cloneable'),
116 false_mark=None
117 )
118 nulls_first = columns.BooleanColumn(
119 verbose_name=_('Nulls First'),
120 false_mark=None
121 )
122 validation_minimum = tables.Column(
123 verbose_name=_('Minimum Value'),
124 )
125 validation_maximum = tables.Column(
126 verbose_name=_('Maximum Value'),
127 )
128 validation_regex = tables.Column(
129 verbose_name=_('Validation Regex'),
130 )
131 validation_schema = columns.BooleanColumn(
132 verbose_name=_('Validation Schema'),
133 false_mark=None,
134 )
135 owner = tables.Column(
136 linkify=True,
137 verbose_name=_('Owner')
138 )
140 class Meta(NetBoxTable.Meta):
141 model = CustomField
142 fields = (
143 'pk', 'id', 'name', 'object_types', 'label', 'type', 'related_object_type', 'group_name', 'required',
144 'unique', 'default', 'description', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable',
145 'is_cloneable', 'nulls_first', 'weight', 'choice_set', 'choices', 'validation_minimum',
146 'validation_maximum', 'validation_regex', 'validation_schema', 'status', 'comments', 'created',
147 'last_updated',
148 )
149 default_columns = (
150 'pk', 'name', 'status', 'object_types', 'label', 'group_name', 'type', 'required', 'unique',
151 'description',
152 )
155class CustomFieldChoiceSetTable(NetBoxTable):
156 name = tables.Column(
157 verbose_name=_('Name'),
158 linkify=True
159 )
160 base_choices = columns.ChoiceFieldColumn()
161 extra_choices = tables.TemplateColumn(
162 template_code="""{% for k, v in value.items %}{{ v }}{% if not forloop.last %}, {% endif %}{% endfor %}"""
163 )
164 choices = columns.ChoicesColumn(
165 max_items=10,
166 orderable=False
167 )
168 choice_count = tables.TemplateColumn(
169 accessor=tables.A('extra_choices'),
170 template_code='{{ value|length }}',
171 orderable=False,
172 verbose_name=_('Count')
173 )
174 order_alphabetically = columns.BooleanColumn(
175 verbose_name=_('Order Alphabetically'),
176 false_mark=None
177 )
178 owner = tables.Column(
179 linkify=True,
180 verbose_name=_('Owner')
181 )
183 class Meta(NetBoxTable.Meta):
184 model = CustomFieldChoiceSet
185 fields = (
186 'pk', 'id', 'name', 'description', 'base_choices', 'extra_choices', 'choice_count', 'choices',
187 'order_alphabetically', 'created', 'last_updated',
188 )
189 default_columns = ('pk', 'name', 'base_choices', 'choice_count', 'description')
192class CustomLinkTable(NetBoxTable):
193 name = tables.Column(
194 verbose_name=_('Name'),
195 linkify=True
196 )
197 object_types = columns.ContentTypesColumn(
198 verbose_name=_('Object Types'),
199 )
200 enabled = columns.BooleanColumn(
201 verbose_name=_('Enabled'),
202 )
203 new_window = columns.BooleanColumn(
204 verbose_name=_('New Window'),
205 false_mark=None
206 )
207 owner = tables.Column(
208 linkify=True,
209 verbose_name=_('Owner')
210 )
212 class Meta(NetBoxTable.Meta):
213 model = CustomLink
214 fields = (
215 'pk', 'id', 'name', 'object_types', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
216 'button_class', 'new_window', 'created', 'last_updated',
217 )
218 default_columns = ('pk', 'name', 'object_types', 'enabled', 'group_name', 'button_class', 'new_window')
221class ExportTemplateTable(NetBoxTable):
222 name = tables.Column(
223 verbose_name=_('Name'),
224 linkify=True
225 )
226 object_types = columns.ContentTypesColumn(
227 verbose_name=_('Object Types'),
228 )
229 mime_type = tables.Column(
230 verbose_name=_('MIME Type')
231 )
232 file_name = tables.Column(
233 verbose_name=_('File Name'),
234 )
235 file_extension = tables.Column(
236 verbose_name=_('File Extension'),
237 )
238 as_attachment = columns.BooleanColumn(
239 verbose_name=_('As Attachment'),
240 false_mark=None
241 )
242 data_source = tables.Column(
243 verbose_name=_('Data Source'),
244 linkify=True
245 )
246 data_file = tables.Column(
247 verbose_name=_('Data File'),
248 linkify=True
249 )
250 is_synced = columns.BooleanColumn(
251 orderable=False,
252 verbose_name=_('Synced')
253 )
254 owner = tables.Column(
255 linkify=True,
256 verbose_name=_('Owner')
257 )
259 class Meta(NetBoxTable.Meta):
260 model = ExportTemplate
261 fields = (
262 'pk', 'id', 'name', 'object_types', 'description', 'mime_type', 'file_name', 'file_extension',
263 'as_attachment', 'data_source', 'data_file', 'data_synced', 'created', 'last_updated',
264 )
265 default_columns = (
266 'pk', 'name', 'object_types', 'description', 'mime_type', 'file_name', 'file_extension',
267 'as_attachment', 'is_synced',
268 )
271class ImageAttachmentTable(NetBoxTable):
272 id = tables.Column(
273 verbose_name=_('ID'),
274 linkify=False
275 )
276 image = columns.TemplateColumn(
277 verbose_name=_('Image'),
278 template_code=IMAGEATTACHMENT_IMAGE,
279 attrs={'td': {'class': 'text-nowrap'}}
280 )
281 name = tables.Column(
282 verbose_name=_('Name'),
283 linkify=True,
284 )
285 filename = tables.Column(
286 verbose_name=_('Filename'),
287 linkify=lambda record: record.image.url,
288 orderable=False,
289 )
290 dimensions = columns.TemplateColumn(
291 verbose_name=_('Dimensions'),
292 orderable=False,
293 template_code="{{ record.image_width }}×{{ record.image_height }}",
294 )
295 object_type = columns.ContentTypeColumn(
296 verbose_name=_('Object Type'),
297 )
298 parent = tables.Column(
299 verbose_name=_('Object'),
300 linkify=True,
301 orderable=False,
302 )
303 size = tables.Column(
304 orderable=False,
305 verbose_name=_('Size')
306 )
308 class Meta(NetBoxTable.Meta):
309 model = ImageAttachment
310 fields = (
311 'pk', 'object_type', 'parent', 'image', 'name', 'filename', 'description', 'image_height', 'image_width',
312 'size', 'created', 'last_updated',
313 )
314 default_columns = ('image', 'parent', 'description', 'dimensions', 'size')
316 def render_size(self, value):
317 return filesizeformat(value)
319 def value_size(self, value):
320 return value
323class SavedFilterTable(NetBoxTable):
324 name = tables.Column(
325 verbose_name=_('Name'),
326 linkify=True
327 )
328 object_types = columns.ContentTypesColumn(
329 verbose_name=_('Object Types'),
330 )
331 enabled = columns.BooleanColumn(
332 verbose_name=_('Enabled'),
333 )
334 shared = columns.BooleanColumn(
335 verbose_name=_('Shared'),
336 false_mark=None
337 )
338 owner = tables.Column(
339 linkify=True,
340 verbose_name=_('Owner')
341 )
343 def value_parameters(self, value):
344 return json.dumps(value)
346 class Meta(NetBoxTable.Meta):
347 model = SavedFilter
348 fields = (
349 'pk', 'id', 'name', 'slug', 'object_types', 'description', 'user', 'weight', 'enabled', 'shared',
350 'created', 'last_updated', 'parameters'
351 )
352 default_columns = (
353 'pk', 'name', 'object_types', 'user', 'description', 'enabled', 'shared',
354 )
357class TableConfigTable(NetBoxTable):
358 name = tables.Column(
359 verbose_name=_('Name'),
360 linkify=True
361 )
362 object_type = columns.ContentTypeColumn(
363 verbose_name=_('Object Type'),
364 )
365 table = tables.Column(
366 verbose_name=_('Table Name')
367 )
368 enabled = columns.BooleanColumn(
369 verbose_name=_('Enabled'),
370 )
371 shared = columns.BooleanColumn(
372 verbose_name=_('Shared'),
373 false_mark=None
374 )
376 class Meta(NetBoxTable.Meta):
377 model = TableConfig
378 fields = (
379 'pk', 'id', 'name', 'object_type', 'table', 'description', 'user', 'weight', 'enabled', 'shared', 'created',
380 'last_updated',
381 )
382 default_columns = (
383 'pk', 'name', 'object_type', 'table', 'user', 'description', 'enabled', 'shared',
384 )
387class BookmarkTable(NetBoxTable):
388 object_type = columns.ContentTypeColumn(
389 verbose_name=_('Object Type'),
390 )
391 object = tables.Column(
392 verbose_name=_('Object'),
393 linkify=True,
394 orderable=False
395 )
396 actions = columns.ActionsColumn(
397 actions=('delete',)
398 )
400 class Meta(NetBoxTable.Meta):
401 model = Bookmark
402 fields = ('pk', 'object', 'object_type', 'created')
403 default_columns = ('object', 'object_type', 'created')
406class SubscriptionTable(NetBoxTable):
407 object_type = columns.ContentTypeColumn(
408 verbose_name=_('Object Type'),
409 )
410 object = tables.Column(
411 verbose_name=_('Object'),
412 linkify=True,
413 orderable=False
414 )
415 user = tables.Column(
416 verbose_name=_('User'),
417 linkify=True
418 )
419 actions = columns.ActionsColumn(
420 actions=('delete',)
421 )
423 class Meta(NetBoxTable.Meta):
424 model = Subscription
425 fields = ('pk', 'object', 'object_type', 'created', 'user')
426 default_columns = ('object', 'object_type', 'created')
429class NotificationTable(NetBoxTable):
430 icon = columns.TemplateColumn(
431 template_code=NOTIFICATION_ICON,
432 accessor=tables.A('event'),
433 orderable=False,
434 attrs={
435 'td': {'class': 'w-1'},
436 'th': {'class': 'w-1', 'aria-label': _('Type')},
437 },
438 verbose_name=''
439 )
440 object_type = columns.ContentTypeColumn(
441 verbose_name=_('Object Type'),
442 )
443 object = columns.TemplateColumn(
444 verbose_name=_('Object'),
445 template_code=NOTIFICATION_LINK,
446 orderable=False
447 )
448 created = columns.DateTimeColumn(
449 timespec='minutes',
450 verbose_name=_('Created'),
451 )
452 read = columns.DateTimeColumn(
453 timespec='minutes',
454 verbose_name=_('Read'),
455 )
456 user = tables.Column(
457 verbose_name=_('User'),
458 linkify=True
459 )
460 actions = NotificationActionsColumn(
461 actions=('dismiss',)
462 )
464 class Meta(NetBoxTable.Meta):
465 model = Notification
466 fields = ('pk', 'icon', 'object', 'object_type', 'event_type', 'created', 'read', 'user')
467 default_columns = ('icon', 'object', 'object_type', 'event_type', 'created')
468 row_attrs = {
469 'data-read': lambda record: bool(record.read),
470 }
473class NotificationGroupTable(NetBoxTable):
474 name = tables.Column(
475 linkify=True,
476 verbose_name=_('Name')
477 )
478 users = columns.ManyToManyColumn(
479 linkify_item=True
480 )
481 groups = columns.ManyToManyColumn(
482 linkify_item=True
483 )
485 class Meta(NetBoxTable.Meta):
486 model = NotificationGroup
487 fields = ('pk', 'name', 'description', 'groups', 'users')
488 default_columns = ('name', 'description', 'groups', 'users')
491class WebhookTable(NetBoxTable):
492 name = tables.Column(
493 verbose_name=_('Name'),
494 linkify=True
495 )
496 ssl_verification = columns.BooleanColumn(
497 verbose_name=_('SSL Verification'),
498 )
499 timeout = tables.Column(
500 verbose_name=_('Timeout (sec)'),
501 )
502 owner = tables.Column(
503 linkify=True,
504 verbose_name=_('Owner')
505 )
506 tags = columns.TagColumn(
507 url_name='extras:webhook_list'
508 )
510 class Meta(NetBoxTable.Meta):
511 model = Webhook
512 fields = (
513 'pk', 'id', 'name', 'http_method', 'payload_url', 'http_content_type', 'secret', 'ssl_verification',
514 'ca_file_path', 'timeout', 'description', 'tags', 'created', 'last_updated',
515 )
516 default_columns = (
517 'pk', 'name', 'http_method', 'payload_url', 'description',
518 )
521class EventRuleTable(NetBoxTable):
522 name = tables.Column(
523 verbose_name=_('Name'),
524 linkify=True
525 )
526 action_type = tables.Column(
527 verbose_name=_('Type'),
528 )
529 action_object = tables.Column(
530 verbose_name=_('Object'),
531 orderable=False,
532 linkify=True,
533 )
534 object_types = columns.ContentTypesColumn(
535 verbose_name=_('Object Types'),
536 )
537 enabled = columns.BooleanColumn(
538 verbose_name=_('Enabled'),
539 )
540 event_types = columns.ArrayColumn(
541 verbose_name=_('Event Types'),
542 func=get_event_text,
543 orderable=False
544 )
545 owner = tables.Column(
546 linkify=True,
547 verbose_name=_('Owner')
548 )
549 tags = columns.TagColumn(
550 url_name='extras:webhook_list'
551 )
553 class Meta(NetBoxTable.Meta):
554 model = EventRule
555 fields = (
556 'pk', 'id', 'name', 'enabled', 'description', 'action_type', 'action_object', 'object_types',
557 'event_types', 'tags', 'created', 'last_updated',
558 )
559 default_columns = (
560 'pk', 'name', 'enabled', 'action_type', 'action_object', 'object_types', 'event_types',
561 )
563 def render_action_type(self, record):
564 # Render explicitly (rather than relying on django-tables2's built-in choices-driven
565 # get_FOO_display() auto-rendering) so an unavailable action type gets a red badge.
566 label = record.get_action_type_display()
567 if not record.action_is_available:
568 return format_html('<span class="badge text-bg-red">{}</span>', label)
569 return label
571 def value_action_type(self, record):
572 # Raw value for non-HTML output (e.g. CSV/table-config export), so the badge's HTML
573 # markup from render_action_type() above isn't leaked into it.
574 return record.get_action_type_display()
577class TagTable(NetBoxTable):
578 name = tables.Column(
579 verbose_name=_('Name'),
580 linkify=True
581 )
582 color = columns.ColorColumn(
583 verbose_name=_('Color'),
584 )
585 object_types = columns.ContentTypesColumn(
586 verbose_name=_('Object Types'),
587 )
588 owner = tables.Column(
589 linkify=True,
590 verbose_name=_('Owner')
591 )
593 class Meta(NetBoxTable.Meta):
594 model = Tag
595 fields = (
596 'pk', 'id', 'name', 'items', 'slug', 'color', 'weight', 'description', 'object_types',
597 'created', 'last_updated', 'actions',
598 )
599 default_columns = ('pk', 'name', 'items', 'slug', 'color', 'description')
602class TaggedItemTable(NetBoxTable):
603 id = tables.Column(
604 verbose_name=_('ID'),
605 linkify=lambda record: record.content_object.get_absolute_url(),
606 accessor='content_object__id'
607 )
608 content_type = columns.ContentTypeColumn(
609 verbose_name=_('Type')
610 )
611 content_object = tables.Column(
612 linkify=True,
613 orderable=False,
614 verbose_name=_('Object')
615 )
616 actions = columns.ActionsColumn(
617 actions=()
618 )
620 class Meta(NetBoxTable.Meta):
621 model = TaggedItem
622 fields = ('id', 'content_type', 'content_object')
625class ConfigContextProfileTable(PrimaryModelTable):
626 name = tables.Column(
627 verbose_name=_('Name'),
628 linkify=True
629 )
630 data_source = tables.Column(
631 verbose_name=_('Data Source'),
632 linkify=True
633 )
634 data_file = tables.Column(
635 verbose_name=_('Data File'),
636 linkify=True
637 )
638 is_synced = columns.BooleanColumn(
639 orderable=False,
640 verbose_name=_('Synced')
641 )
642 tags = columns.TagColumn(
643 url_name='extras:configcontextprofile_list'
644 )
646 class Meta(PrimaryModelTable.Meta):
647 model = ConfigContextProfile
648 fields = (
649 'pk', 'id', 'name', 'description', 'comments', 'data_source', 'data_file', 'is_synced', 'tags', 'created',
650 'last_updated',
651 )
652 default_columns = ('pk', 'name', 'is_synced', 'description')
655class ConfigContextTable(NetBoxTable):
656 profile = tables.Column(
657 linkify=True,
658 verbose_name=_('Profile'),
659 )
660 data_source = tables.Column(
661 verbose_name=_('Data Source'),
662 linkify=True
663 )
664 data_file = tables.Column(
665 verbose_name=_('Data File'),
666 linkify=True
667 )
668 name = tables.Column(
669 verbose_name=_('Name'),
670 linkify=True
671 )
672 is_active = columns.BooleanColumn(
673 verbose_name=_('Active')
674 )
675 is_synced = columns.BooleanColumn(
676 orderable=False,
677 verbose_name=_('Synced')
678 )
679 owner = tables.Column(
680 linkify=True,
681 verbose_name=_('Owner')
682 )
683 tags = columns.TagColumn(
684 url_name='extras:configcontext_list'
685 )
687 class Meta(NetBoxTable.Meta):
688 model = ConfigContext
689 fields = (
690 'pk', 'id', 'name', 'weight', 'profile', 'is_active', 'is_synced', 'description', 'regions', 'sites',
691 'locations', 'roles', 'platforms', 'cluster_types', 'cluster_groups', 'clusters', 'tenant_groups',
692 'tenants', 'data_source', 'data_file', 'data_synced', 'tags', 'created', 'last_updated',
693 )
694 default_columns = ('pk', 'name', 'weight', 'profile', 'is_active', 'is_synced', 'description')
697class ConfigTemplateTable(NetBoxTable):
698 name = tables.Column(
699 verbose_name=_('Name'),
700 linkify=True
701 )
702 data_source = tables.Column(
703 verbose_name=_('Data Source'),
704 linkify=True
705 )
706 data_file = tables.Column(
707 verbose_name=_('Data File'),
708 linkify=True
709 )
710 is_synced = columns.BooleanColumn(
711 orderable=False,
712 verbose_name=_('Synced')
713 )
714 auto_sync_enabled = columns.BooleanColumn(
715 verbose_name=_('Auto Sync Enabled'),
716 orderable=False,
717 )
718 mime_type = tables.Column(
719 verbose_name=_('MIME Type')
720 )
721 file_name = tables.Column(
722 verbose_name=_('File Name'),
723 )
724 file_extension = tables.Column(
725 verbose_name=_('File Extension'),
726 )
727 as_attachment = columns.BooleanColumn(
728 verbose_name=_('As Attachment'),
729 false_mark=None
730 )
731 debug = columns.BooleanColumn(
732 verbose_name=_('Debug'),
733 false_mark=None
734 )
735 owner = tables.Column(
736 linkify=True,
737 verbose_name=_('Owner')
738 )
739 tags = columns.TagColumn(
740 url_name='extras:configtemplate_list'
741 )
742 role_count = columns.LinkedCountColumn(
743 viewname='dcim:devicerole_list',
744 url_params={'config_template_id': 'pk'},
745 verbose_name=_('Device Roles')
746 )
747 platform_count = columns.LinkedCountColumn(
748 viewname='dcim:platform_list',
749 url_params={'config_template_id': 'pk'},
750 verbose_name=_('Platforms')
751 )
752 device_count = columns.LinkedCountColumn(
753 viewname='dcim:device_list',
754 url_params={'config_template_id': 'pk'},
755 verbose_name=_('Devices')
756 )
757 vm_count = columns.LinkedCountColumn(
758 viewname='virtualization:virtualmachine_list',
759 url_params={'config_template_id': 'pk'},
760 verbose_name=_('Virtual Machines')
761 )
763 class Meta(NetBoxTable.Meta):
764 model = ConfigTemplate
765 fields = (
766 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'as_attachment', 'debug',
767 'mime_type', 'file_name', 'file_extension', 'role_count', 'platform_count', 'device_count',
768 'vm_count', 'created', 'last_updated', 'tags',
769 )
770 default_columns = (
771 'pk', 'name', 'description', 'is_synced', 'device_count', 'vm_count',
772 )
775class JournalEntryTable(NetBoxTable):
776 created = columns.DateTimeColumn(
777 verbose_name=_('Created'),
778 timespec='minutes',
779 linkify=True
780 )
781 assigned_object_type = columns.ContentTypeColumn(
782 verbose_name=_('Object Type')
783 )
784 assigned_object = tables.Column(
785 linkify=True,
786 orderable=False,
787 verbose_name=_('Object')
788 )
789 kind = columns.ChoiceFieldColumn(
790 verbose_name=_('Kind'),
791 )
792 comments = columns.MarkdownColumn(
793 verbose_name=_('Comments'),
794 )
795 comments_short = tables.TemplateColumn(
796 accessor=tables.A('comments'),
797 template_code='{{ value|markdown|truncatewords_html:50 }}',
798 verbose_name=_('Comments (Short)')
799 )
800 tags = columns.TagColumn(
801 url_name='extras:journalentry_list'
802 )
804 class Meta(NetBoxTable.Meta):
805 model = JournalEntry
806 fields = (
807 'pk', 'id', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments',
808 'comments_short', 'tags', 'actions',
809 )
810 default_columns = (
811 'pk', 'created', 'created_by', 'assigned_object_type', 'assigned_object', 'kind', 'comments'
812 )
815class ScriptResultsTable(BaseTable):
816 index = tables.Column(
817 verbose_name=_('Line')
818 )
819 time = columns.DateTimeColumn(
820 verbose_name=_('Time'),
821 timespec='seconds'
822 )
823 status = tables.TemplateColumn(
824 template_code="""{% load log_levels %}{% log_level record.status %}""",
825 verbose_name=_('Level')
826 )
827 object = tables.Column(
828 verbose_name=_('Object')
829 )
830 message = columns.MarkdownColumn(
831 verbose_name=_('Message')
832 )
834 class Meta(BaseTable.Meta):
835 empty_text = _(EMPTY_TABLE_TEXT)
836 fields = (
837 'index', 'time', 'status', 'object', 'message',
838 )
839 default_columns = (
840 'index', 'time', 'status', 'object', 'message',
841 )
843 def render_object(self, value, record):
844 return format_html("<a href='{}'>{}</a>", record['url'], value)
846 def render_url(self, value):
847 return format_html("<a href='{}'>{}</a>", value, value)
850class ScriptJobTable(JobTable):
851 id = tables.TemplateColumn(
852 template_code="""<a href="{% url 'extras:script_result' job_pk=record.pk %}">{{ record.id }}</a>""",
853 verbose_name=_('ID'),
854 )
856 class Meta(NetBoxTable.Meta):
857 model = Job
858 fields = (
859 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'scheduled', 'interval', 'started',
860 'completed', 'user', 'error', 'job_id',
861 )
862 default_columns = (
863 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'started', 'completed', 'user',
864 )
867class ReportResultsTable(BaseTable):
868 index = tables.Column(
869 verbose_name=_('Line')
870 )
871 method = tables.Column(
872 verbose_name=_('Method')
873 )
874 time = tables.Column(
875 verbose_name=_('Time')
876 )
877 status = tables.TemplateColumn(
878 template_code="""{% load log_levels %}{% log_level record.status %}""",
879 verbose_name=_('Level')
880 )
881 object = tables.Column(
882 verbose_name=_('Object')
883 )
884 url = tables.Column(
885 verbose_name=_('URL')
886 )
887 message = columns.MarkdownColumn(
888 verbose_name=_('Message')
889 )
891 class Meta(BaseTable.Meta):
892 empty_text = _(EMPTY_TABLE_TEXT)
893 fields = (
894 'index', 'method', 'time', 'status', 'object', 'url', 'message',
895 )
897 def render_object(self, value, record):
898 return format_html("<a href='{}'>{}</a>", record['url'], value)
900 def render_url(self, value):
901 return format_html("<a href='{}'>{}</a>", value, value)