From c38535e4c062afdd8e1f2e9a93a937e5956e836d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 9 Aug 2016 19:50:24 -0500 Subject: [PATCH] Add hopefully generic `FileFieldRenderer` Eventually should refactor other (batch) things to use this. --- tailbone/forms/renderers/__init__.py | 2 + tailbone/forms/renderers/batch.py | 1 + tailbone/forms/renderers/files.py | 98 ++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 tailbone/forms/renderers/files.py diff --git a/tailbone/forms/renderers/__init__.py b/tailbone/forms/renderers/__init__.py index a52ff702..4ba8e0d3 100644 --- a/tailbone/forms/renderers/__init__.py +++ b/tailbone/forms/renderers/__init__.py @@ -33,6 +33,8 @@ from .common import (StrippedTextFieldRenderer, CodeTextAreaFieldRenderer, Autoc DateTimeFieldRenderer, DateTimePrettyFieldRenderer, TimeFieldRenderer, EnumFieldRenderer, YesNoFieldRenderer) +from .files import FileFieldRenderer + from .people import (PersonFieldRenderer, PersonFieldLinkRenderer, CustomerFieldRenderer, CustomerFieldLinkRenderer) diff --git a/tailbone/forms/renderers/batch.py b/tailbone/forms/renderers/batch.py index 65f84cd3..a7da7b88 100644 --- a/tailbone/forms/renderers/batch.py +++ b/tailbone/forms/renderers/batch.py @@ -34,6 +34,7 @@ from formalchemy.ext import fsblob from formalchemy.fields import FileFieldRenderer as Base +# TODO: make this inherit from `tailbone.forms.renderers.files.FileFieldRenderer` class FileFieldRenderer(fsblob.FileFieldRenderer): """ Custom file field renderer for batches based on a single source data file. diff --git a/tailbone/forms/renderers/files.py b/tailbone/forms/renderers/files.py new file mode 100644 index 00000000..940275af --- /dev/null +++ b/tailbone/forms/renderers/files.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Rattail -- Retail Software Framework +# Copyright © 2010-2016 Lance Edgar +# +# This file is part of Rattail. +# +# Rattail is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Rattail. If not, see . +# +################################################################################ +""" +Batch Field Renderers +""" + +from __future__ import unicode_literals, absolute_import + +import os +import stat +import random + +from formalchemy.ext import fsblob +from formalchemy.fields import FileFieldRenderer as Base + + +class FileFieldRenderer(fsblob.FileFieldRenderer): + """ + Custom file field renderer. In readonly mode, shows a filename and its + size; in edit mode, supports a single file upload. + """ + + @classmethod + def new(cls, view, **kwargs): + name = b'Configured{}_{}'.format(cls.__name__, str(random.random())[2:]) + return type(name, (cls,), dict(view=view, **kwargs)) + + @property + def request(self): + return self.view.request + + @property + def storage_path(self): + return self.view.upload_dir + + def get_file_path(self): + """ + Returns the absolute path to the data file. + """ + if hasattr(self, 'file_path'): + return self.file_path + return self.field.value + + def get_size(self): + """ + Returns the size of the data file, in bytes. + """ + path = self.get_file_path() + if path and os.path.isfile(path): + return os.stat(path)[stat.ST_SIZE] + return 0 + + def get_url(self, filename): + url = self.get_download_url() + if url: + if callable(url): + return url(filename) + return url + + def get_download_url(self): + if hasattr(self, 'download_url'): + return self.download_url + + def render(self, **kwargs): + return Base.render(self, **kwargs) + + def render_readonly(self, **kwargs): + """ + Render the filename and the binary size in a human readable with a link + to the file itself. + """ + value = self.get_file_path() + if value: + content = '{} ({})'.format(fsblob.normalized_basename(value), + self.readable_size()) + return fsblob.h.content_tag('a', content, + href=self.get_url(value), **kwargs) + return ''