Skip to content

Commit

Permalink
Generate ZIP file for download with QR code button
Browse files Browse the repository at this point in the history
  • Loading branch information
johannaengland committed May 7, 2024
1 parent f121110 commit 17bc18f
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 3 deletions.
1 change: 1 addition & 0 deletions changelog.d/+qr-code-bulk.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add button to SeedDB that generates a ZIP file to download with QR Codes linking to the selected netboxes/rooms
10 changes: 9 additions & 1 deletion python/nav/web/seeddb/page/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,19 @@ def not_implemented(*_args, **_kwargs):
raise NotImplementedError()


def view_switcher(request, list_view=None, move_view=None, delete_view=None):
def view_switcher(
request,
list_view=None,
move_view=None,
delete_view=None,
generate_qr_codes_view=None,
):
"""Selects appropriate view depending on POST data."""
if request.method == 'POST':
if 'move' in request.POST:
return move_view(request)
elif 'delete' in request.POST:
return delete_view(request)
elif 'qr_code' in request.POST:
return generate_qr_codes_view(request)
return list_view(request)
65 changes: 64 additions & 1 deletion python/nav/web/seeddb/page/netbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
import datetime
from django.db import transaction
from django.contrib.postgres.aggregates import ArrayAgg
from django.http import HttpResponseRedirect
from django.urls import reverse

from nav.models.manage import Netbox
from nav.bulkparse import NetboxBulkParser
from nav.bulkimport import NetboxImporter

from nav.web.message import new_message, Messages
from nav.web.seeddb import SeeddbInfo, reverse_lazy
from nav.web.seeddb.constants import SEEDDB_EDITABLE_MODELS
from nav.web.seeddb.page import view_switcher
Expand All @@ -31,6 +34,10 @@
from nav.web.seeddb.utils.move import move
from nav.web.seeddb.utils.bulk import render_bulkimport
from nav.web.seeddb.page.netbox.forms import NetboxFilterForm, NetboxMoveForm
from nav.web.utils import (
generate_qr_codes_as_byte_strings,
generate_qr_codes_as_zip_file,
)


class NetboxInfo(SeeddbInfo):
Expand All @@ -54,7 +61,11 @@ class NetboxInfo(SeeddbInfo):
def netbox(request):
"""Controller for landing page for netboxes"""
return view_switcher(
request, list_view=netbox_list, move_view=netbox_move, delete_view=netbox_delete
request,
list_view=netbox_list,
move_view=netbox_move,
delete_view=netbox_delete,
generate_qr_codes_view=netbox_generate_qr_codes,
)


Expand Down Expand Up @@ -112,6 +123,58 @@ def netbox_pre_deletion_mark(queryset):
queryset.update(deleted_at=datetime.datetime.now(), up_to_date=False)


def netbox_generate_qr_codes(request):
"""Controller for generating qr codes for netboxes"""
if not request.POST.getlist('object'):
new_message(
request,
"You need to select at least one object to generate qr codes for",
Messages.ERROR,
)
return HttpResponseRedirect(reverse('seeddb-room'))

url_dict = dict()
netboxes = Netbox.objects.filter(id__in=request.POST.getlist('object'))

for netbox in netboxes:
url = request.build_absolute_uri(
reverse('ipdevinfo-details-by-id', kwargs={'netbox_id': netbox.id})
)
url_dict[str(netbox)] = url

qr_codes_zip_file = generate_qr_codes_as_zip_file(url_dict=url_dict)

info = NetboxInfo()
query = (
Netbox.objects.select_related("room", "category", "type", "organization")
.prefetch_related("profiles")
.annotate(profile=ArrayAgg("profiles__name"))
)
filter_form = NetboxFilterForm(request.GET)
value_list = (
'sysname',
'room',
'ip',
'category',
'organization',
'profile',
'type__name',
)
return render_list(
request,
query,
value_list,
'seeddb-netbox-edit',
edit_url_attr='pk',
filter_form=filter_form,
template='seeddb/list_netbox.html',
extra_context={
**info.template_context,
**{"qr_codes_zip_file": qr_codes_zip_file},
},
)


def netbox_move(request):
"""Controller for handling a move request"""
info = NetboxInfo()
Expand Down
49 changes: 48 additions & 1 deletion python/nav/web/seeddb/page/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
#
"""Forms and view functions for SeedDB's Room view"""

from django.http import HttpResponseRedirect
from django.urls import reverse

from nav.models.manage import Room
from nav.bulkparse import RoomBulkParser
from nav.bulkimport import RoomImporter

from nav.web.message import new_message, Messages
from nav.web.seeddb import SeeddbInfo, reverse_lazy
from nav.web.seeddb.constants import SEEDDB_EDITABLE_MODELS
from nav.web.seeddb.page import view_switcher
Expand All @@ -31,6 +33,10 @@
from nav.web.seeddb.utils.delete import render_delete
from nav.web.seeddb.utils.move import move
from nav.web.seeddb.utils.bulk import render_bulkimport
from nav.web.utils import (
generate_qr_codes_as_byte_strings,
generate_qr_codes_as_zip_file,
)

from ..forms import RoomForm, RoomFilterForm, RoomMoveForm

Expand All @@ -56,7 +62,11 @@ class RoomInfo(SeeddbInfo):
def room(request):
"""Controller for listing, moving and deleting rooms"""
return view_switcher(
request, list_view=room_list, move_view=room_move, delete_view=room_delete
request,
list_view=room_list,
move_view=room_move,
delete_view=room_delete,
generate_qr_codes_view=room_generate_qr_codes,
)


Expand Down Expand Up @@ -84,6 +94,43 @@ def room_move(request):
)


def room_generate_qr_codes(request):
"""Controller for generating qr codes for rooms"""
if not request.POST.getlist('object'):
new_message(
request,
"You need to select at least one object to generate qr codes for",
Messages.ERROR,
)
return HttpResponseRedirect(reverse('seeddb-room'))

url_dict = dict()
ids = request.POST.getlist('object')

for id in ids:
url = request.build_absolute_uri(reverse('room-info', kwargs={'roomid': id}))
url_dict[id] = url

qr_codes_zip_file = generate_qr_codes_as_zip_file(url_dict=url_dict)

info = RoomInfo()
value_list = ('id', 'location', 'description', 'position', 'data')
query = Room.objects.select_related("location").all()
filter_form = RoomFilterForm(request.GET)
# When we drop Python 3.7 we can use extra_context = info.template_context | {"qr_codes": qr_codes}
return render_list(
request,
query,
value_list,
'seeddb-room-edit',
filter_form=filter_form,
extra_context={
**info.template_context,
**{"qr_codes_zip_file": qr_codes_zip_file},
},
)


def room_delete(request, object_id=None):
"""Controller for deleting rooms. Used in room()"""
info = RoomInfo()
Expand Down
4 changes: 4 additions & 0 deletions python/nav/web/templates/seeddb/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
</div>
{% endif %}

{% if qr_codes_zip_file %}
<a src="{{ qr_codes_zip_file }}" download="nav_qr_codes">Download generated QR Codes</a>
{% endif %}

<div id="tablewrapper" class="notvisible" data-forpage="{{ request.path }}" data-page="{{ active_page }}">
<table id="seeddb-content" class="listtable" width="100%">
<caption>
Expand Down
68 changes: 68 additions & 0 deletions tests/integration/seeddb_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,71 @@ def test_log_netbox_change_should_not_crash(admin_account, netbox):
new.category_id = "OTHER"

assert log_netbox_change(admin_account, old, new) is None


def test_generating_qr_codes_for_netboxes_should_succeed(client, netbox):
url = reverse('seeddb-netbox')

response = client.post(
url,
follow=True,
data={
"qr_code": "Generate+QR+codes+for+selected",
"object": [netbox.id],
},
)

assert response.status_code == 200
assert 'Download generated QR Codes' in smart_str(response.content)


def test_generating_qr_codes_for_no_selected_netboxes_should_show_error(client, netbox):
url = reverse('seeddb-netbox')

response = client.post(
url,
follow=True,
data={
"qr_code": "Generate+QR+codes+for+selected",
},
)

assert response.status_code == 200
assert (
'You need to select at least one object to generate qr codes for'
in smart_str(response.content)
)


def test_generating_qr_codes_for_rooms_should_succeed(client):
url = reverse('seeddb-room')

response = client.post(
url,
follow=True,
data={
"qr_code": "Generate+QR+codes+for+selected",
"object": ["myroom"],
},
)

assert response.status_code == 200
assert 'Download generated QR Codes' in smart_str(response.content)


def test_generating_qr_codes_for_no_selected_rooms_should_show_error(client, netbox):
url = reverse('seeddb-room')

response = client.post(
url,
follow=True,
data={
"qr_code": "Generate+QR+codes+for+selected",
},
)

assert response.status_code == 200
assert (
'You need to select at least one object to generate qr codes for'
in smart_str(response.content)
)

0 comments on commit 17bc18f

Please sign in to comment.