OCA/server-tools

View on GitHub
base_multi_image/models/image.py

Summary

Maintainability
B
6 hrs
Test Coverage
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
#        Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import base64
import urllib
import os
import logging
from openerp import models, fields, api, exceptions, _
from openerp import tools

_logger = logging.getLogger(__name__)


class Image(models.Model):
    _name = "base_multi_image.image"
    _order = "sequence, owner_model, owner_id, id"
    _sql_constraints = [
        ('uniq_name_owner', 'UNIQUE(owner_id, owner_model, name)',
         _('A document can have only one image with the same name.')),
    ]

    owner_id = fields.Integer(
        "Owner",
        required=True)
    owner_model = fields.Char(
        required=True)
    storage = fields.Selection(
        [('url', 'URL'), ('file', 'OS file'), ('db', 'Database')],
        required=True)
    name = fields.Char(
        'Image title',
        translate=True)
    filename = fields.Char()
    extension = fields.Char(
        'File extension',
        readonly=True)
    file_db_store = fields.Binary(
        'Image stored in database',
        filters='*.png,*.jpg,*.gif')
    path = fields.Char(
        "Image path",
        help="Image path")
    url = fields.Char(
        'Image remote URL')
    image_main = fields.Binary(
        "Full-sized image",
        # pylint: disable=method-compute
        compute="_get_image")
    image_medium = fields.Binary(
        "Medium-sized image",
        # pylint: disable=method-compute
        compute="_get_image_sizes",
        help="Medium-sized image. It is automatically resized as a "
             "128 x 128 px image, with aspect ratio preserved, only when the "
             "image exceeds one of those sizes. Use this field in form views "
             "or kanban views.")
    image_small = fields.Binary(
        "Small-sized image",
        # pylint: disable=method-compute
        compute="_get_image_sizes",
        help="Small-sized image. It is automatically resized as a 64 x 64 px "
             "image, with aspect ratio preserved. Use this field anywhere a "
             "small image is required.")
    comments = fields.Text(
        'Comments',
        translate=True)
    sequence = fields.Integer(
        default=10)
    show_technical = fields.Boolean(
        # pylint: disable=method-compute
        compute="_show_technical")

    @api.multi
    @api.depends('storage', 'path', 'file_db_store', 'url')
    def _get_image(self):
        """Get image data from the right storage type."""
        for s in self:
            s.image_main = getattr(s, "_get_image_from_%s" % s.storage)()

    @api.multi
    @api.depends("owner_id", "owner_model")
    def _show_technical(self):
        """Know if you need to show the technical fields."""
        self.show_technical = all(
            "default_owner_%s" % f not in self.env.context
            for f in ("id", "model"))

    @api.multi
    def _get_image_from_db(self):
        return self.file_db_store

    @api.multi
    def _get_image_from_file(self):
        if self.path and os.path.exists(self.path):
            try:
                with open(self.path, 'rb') as f:
                    return base64.b64encode(f.read())
            except Exception as e:
                _logger.error("Can not open the image %s, error : %s",
                              self.path, e, exc_info=True)
        else:
            _logger.error("The image %s doesn't exist ", self.path)

        return False

    @api.multi
    def _get_image_from_url(self):
        return self._get_image_from_url_cached(self.url)

    @api.model
    @tools.ormcache(skiparg=1)
    def _get_image_from_url_cached(self, url):
        """Allow to download an image and cache it by its URL."""
        if url:
            try:
                (filename, header) = urllib.urlretrieve(url)
                with open(filename, 'rb') as f:
                    return base64.b64encode(f.read())
            except:
                _logger.error("URL %s cannot be fetched", url,
                              exc_info=True)

        return False

    @api.multi
    @api.depends('image_main')
    def _get_image_sizes(self):
        for s in self:
            try:
                vals = tools.image_get_resized_images(
                    s.with_context(bin_size=False).image_main)
            except:
                vals = {"image_medium": False,
                        "image_small": False}
            s.update(vals)

    @api.model
    def _make_name_pretty(self, name):
        return name.replace('_', ' ').capitalize()

    @api.onchange('url')
    def _onchange_url(self):
        if self.url:
            filename = self.url.split('/')[-1]
            self.name, self.extension = os.path.splitext(filename)
            self.name = self._make_name_pretty(self.name)

    @api.onchange('path')
    def _onchange_path(self):
        if self.path:
            self.name, self.extension = os.path.splitext(os.path.basename(
                self.path))
            self.name = self._make_name_pretty(self.name)

    @api.onchange('filename')
    def _onchange_filename(self):
        if self.filename:
            self.name, self.extension = os.path.splitext(self.filename)
            self.name = self._make_name_pretty(self.name)

    @api.constrains('storage', 'url')
    def _check_url(self):
        if self.storage == 'url' and not self.url:
            raise exceptions.ValidationError(_(
                'You must provide an URL for the image.'))

    @api.constrains('storage', 'path')
    def _check_path(self):
        if self.storage == 'file' and not self.path:
            raise exceptions.ValidationError(_(
                'You must provide a file path for the image.'))

    @api.constrains('storage', 'file_db_store')
    def _check_store(self):
        if self.storage == 'db' and not self.file_db_store:
            raise exceptions.ValidationError(_(
                'You must provide an attached file for the image.'))