From 248591020256aa4444e22417079a7b2d0b5579ee Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@esss.se>
Date: Sat, 30 Dec 2017 21:35:47 +0100
Subject: [PATCH] Cache QR codes image generation

The generation of the QR code image is quite slow and makes the page
displaying them take a long time to load when it includes more a few.
The data never changes and can easily be cached (the repr() of the class
is used in the cache key, so if the name or id changes, the cache will
be recomputed - there is no need for a timeout).
---
 app/inventory/views.py |  2 +-
 app/models.py          | 15 +++++++++++++--
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/app/inventory/views.py b/app/inventory/views.py
index 20a9a36..70746c3 100644
--- a/app/inventory/views.py
+++ b/app/inventory/views.py
@@ -146,7 +146,7 @@ def qrcodes(kind='Action'):
         raise utils.CSEntryError(f"Unknown model '{kind}'", status_code=422)
     items = db.session.query(model).order_by(model.name)
     images = [{'name': item.name,
-               'data': utils.image_to_base64(item.image())}
+               'data': item.base64_image()}
               for item in items]
     return render_template('inventory/qrcodes.html', kind=kind, images=images)
 
diff --git a/app/models.py b/app/models.py
index 2d0d7b9..4788849 100644
--- a/app/models.py
+++ b/app/models.py
@@ -22,7 +22,7 @@ from citext import CIText
 from flask import current_app
 from flask_login import UserMixin
 from wtforms import ValidationError
-from .extensions import db, login_manager, ldap_manager
+from .extensions import db, login_manager, ldap_manager, cache
 from .plugins import FlaskUserPlugin
 from .validators import ICS_ID_RE, HOST_NAME_RE, VLAN_NAME_RE
 from . import utils
@@ -209,14 +209,25 @@ class QRCodeMixin:
         data = ':'.join(['CSE', self.__tablename__, self.name])
         return qrcode.make(data, version=1, box_size=5)
 
+    @cache.memoize(timeout=0)
+    def base64_image(self):
+        """Return the QRCode image as base64 string"""
+        return utils.image_to_base64(self.image())
+
     def __str__(self):
         return self.name
 
+    def __repr__(self):
+        # The cache.memoize decorator performs a repr() on the passed in arguments
+        # __repr__ is used as part of the cache key and shall be a uniquely identifying string
+        # See https://flask-caching.readthedocs.io/en/latest/#memoization
+        return f'{self.__class__.__name__}(id={self.id}, name={self.name})'
+
     def to_dict(self):
         return {
             'id': self.id,
             'name': self.name,
-            'qrcode': utils.image_to_base64(self.image()),
+            'qrcode': self.base64_image(),
         }
 
 
-- 
GitLab