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

1import json 

2 

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 _ 

7 

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 

14 

15from .columns import CustomFieldStatusColumn, NotificationActionsColumn 

16 

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) 

41 

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""" 

60 

61NOTIFICATION_ICON = """ 

62<span class="text-{{ value.color }} fs-3"><i class="{{ value.icon }}"></i></span> 

63""" 

64 

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""" 

72 

73 

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 ) 

139 

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 ) 

153 

154 

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 ) 

182 

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') 

190 

191 

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 ) 

211 

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') 

219 

220 

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 ) 

258 

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 ) 

269 

270 

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 ) 

307 

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') 

315 

316 def render_size(self, value): 

317 return filesizeformat(value) 

318 

319 def value_size(self, value): 

320 return value 

321 

322 

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 ) 

342 

343 def value_parameters(self, value): 

344 return json.dumps(value) 

345 

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 ) 

355 

356 

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 ) 

375 

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 ) 

385 

386 

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 ) 

399 

400 class Meta(NetBoxTable.Meta): 

401 model = Bookmark 

402 fields = ('pk', 'object', 'object_type', 'created') 

403 default_columns = ('object', 'object_type', 'created') 

404 

405 

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 ) 

422 

423 class Meta(NetBoxTable.Meta): 

424 model = Subscription 

425 fields = ('pk', 'object', 'object_type', 'created', 'user') 

426 default_columns = ('object', 'object_type', 'created') 

427 

428 

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 ) 

463 

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 } 

471 

472 

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 ) 

484 

485 class Meta(NetBoxTable.Meta): 

486 model = NotificationGroup 

487 fields = ('pk', 'name', 'description', 'groups', 'users') 

488 default_columns = ('name', 'description', 'groups', 'users') 

489 

490 

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 ) 

509 

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 ) 

519 

520 

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 ) 

552 

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 ) 

562 

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 

570 

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() 

575 

576 

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 ) 

592 

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') 

600 

601 

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 ) 

619 

620 class Meta(NetBoxTable.Meta): 

621 model = TaggedItem 

622 fields = ('id', 'content_type', 'content_object') 

623 

624 

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 ) 

645 

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') 

653 

654 

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 ) 

686 

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') 

695 

696 

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 ) 

762 

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 ) 

773 

774 

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 ) 

803 

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 ) 

813 

814 

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 ) 

833 

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 ) 

842 

843 def render_object(self, value, record): 

844 return format_html("<a href='{}'>{}</a>", record['url'], value) 

845 

846 def render_url(self, value): 

847 return format_html("<a href='{}'>{}</a>", value, value) 

848 

849 

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 ) 

855 

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 ) 

865 

866 

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 ) 

890 

891 class Meta(BaseTable.Meta): 

892 empty_text = _(EMPTY_TABLE_TEXT) 

893 fields = ( 

894 'index', 'method', 'time', 'status', 'object', 'url', 'message', 

895 ) 

896 

897 def render_object(self, value, record): 

898 return format_html("<a href='{}'>{}</a>", record['url'], value) 

899 

900 def render_url(self, value): 

901 return format_html("<a href='{}'>{}</a>", value, value)