コード例 #1
0
    def __init__(self, data=None, file=None, id_media=None):
        self.data = data or {}
        self.thumbnail_sizes = current_app.config["MEDIAS"]["THUMBNAIL_SIZES"]
        # filtrer les données du dict qui
        # vont être insérées dans l'objet Model
        self.media_data = {
            k: self.data[k]
            for k in TMedias.__mapper__.c.keys() if k in self.data
        }
        self.file = file

        # Chargement du média
        if self.media_data.get("id_media"):
            self.media = self._load_from_id(self.media_data["id_media"])
        elif id_media is not None:
            self.media = self._load_from_id(id_media)
        else:
            self.new = True
            self.media = TMedias(**self.media_data)
コード例 #2
0
    def __init__(self, data=None, file=None, id_media=None):
        self.data = data or {}

        # filtrer les données du dict qui
        # vont être insérées dans l'objet TMedias
        self.media_data = {
            k: self.data[k] for k in TMedias.__mapper__.c.keys() if k in self.data
        }
        self.file = file

        # Chargement du média
        if 'id_media' in self.media_data:
            self.media = self._load_from_id(self.media_data['id_media'])
        elif id_media is not None:
            self.media = self._load_from_id(id_media)
        else:
            self.new = True
            self.media = TMedias(**self.media_data)
コード例 #3
0
class TMediaRepository:
    """
        Reposity permettant de manipuler un objet média
        au niveau de la base de données et du système de fichier
        de façon synchrone
    """

    media_data = dict()
    data = dict()
    file = None
    media = None
    new = False

    def __init__(self, data=None, file=None, id_media=None):
        self.data = data or {}
        self.thumbnail_sizes = current_app.config["MEDIAS"]["THUMBNAIL_SIZES"]
        # filtrer les données du dict qui
        # vont être insérées dans l'objet Model
        self.media_data = {
            k: self.data[k]
            for k in TMedias.__mapper__.c.keys() if k in self.data
        }
        self.file = file

        # Chargement du média
        if self.media_data.get("id_media"):
            self.media = self._load_from_id(self.media_data["id_media"])
        elif id_media is not None:
            self.media = self._load_from_id(id_media)
        else:
            self.new = True
            self.media = TMedias(**self.media_data)

    def create_or_update_media(self):
        """
            Création ou modification d'un média :
             - Enregistrement en base de données
             - Stockage du fichier
        """
        if self.new:
            try:
                self._persist_media_db()
            except Exception as e:
                raise e

        # Si le média à un fichier associé
        if self.file:
            self.data["isFile"] = True
            self.media_data["media_path"] = self.upload_file()
            self.media_data["media_url"] = None
        elif self.data.get("media_path") not in ["", None]:
            self.data["isFile"] = True
            self.media_data["media_url"] = None
        else:
            self.data["isFile"] = False
            self.media_data["media_path"] = None
            self.media_data["media_url"] = self.data["media_url"]
            self.test_url()

        # Si le média avait un fichier associé
        # et qu'il a été remplacé par une url
        if ((not self.new) and (self.data["isFile"] is not True)
                and (self.media.media_path is not None)):
            remove_file(self.media.media_path)
            self.media.remove_thumbnails()

        # Si le média avait une url
        # et qu'elle a été modifiée
        if ((not self.new) and (self.data["isFile"] is not True)
                and (self.media.media_url is not self.data["media_url"])
                and self.is_img()):
            self.media.remove_thumbnails()

        for k in self.media_data:
            setattr(self.media, k, self.media_data[k])

        self._persist_media_db()

        if self.is_img():
            self.create_thumbnails()

        return self.media

    def _persist_media_db(self):
        """
            Enregistrement des données dans la base
        """
        # @TODO récupérer les exceptions
        try:
            DB.session.add(self.media)
            DB.session.commit()
            for k in self.media_data:
                self.media_data[k] = getattr(self.media, k)
        except IntegrityError as exp:
            # @TODO A revoir avec les nouvelles contraintes
            DB.session.rollback()
            if "check_entity_field_exist" in exp.args[0]:
                raise Exception("{} doesn't exists".format(
                    self.data["id_table_location"]))
            if "fk_t_medias_check_entity_value" in exp.args[0]:
                raise Exception("id {} of {} doesn't exists".format(
                    self.data["id_table_location"]))
            else:
                raise Exception("Errors {}".format(exp.args))

    def absolute_file_path(self, thumbnail_height=None):
        return os.path.join(current_app.config["BASE_DIR"],
                            self.file_path(thumbnail_height))

    def test_video_link(self):
        media_type = self.media_type()
        url = self.data["media_url"]
        if media_type == "Vidéo Youtube" and "youtube" not in url and "youtu.be" not in url:
            return False

        if media_type == "Vidéo Dailymotion" and "dailymotion" not in url:
            return False

        if media_type == "Vidéo Vimeo" and "vimeo" not in url:
            return False

        return True

    def test_header_content_type(self, content_type):
        media_type = self.media_type()
        if media_type == "Photo" and "image" not in content_type:
            return False

        if media_type == "Audio" and "audio" not in content_type:
            return False

        if media_type == "Vidéo (Fichier)" and "video" not in content_type:
            return False

        if media_type == "PDF" and "pdf" not in content_type:
            return False

        if media_type == "Page web" and "html" not in content_type:
            return False

        return True

    def test_url(self):

        try:
            res = requests.head(url=self.data["media_url"])

            if not ((res.status_code >= 200) and (res.status_code < 400)):
                raise GeoNatureError(
                    "la réponse est différente de 200 ({})".format(
                        res.status_code))

            if not self.test_header_content_type(res.headers["Content-type"]):
                raise GeoNatureError(
                    "le format du lien ({}) ne correspond pas au type de média choisi ({}). Si le media est de type image essayer de récupérer l'adresse de l'image (clique droit sur l'image : récupérer l'adresse de l'image)"
                    .format(res.headers["Content-type"], self.media_type()))

            if not self.test_video_link():
                raise GeoNatureError(
                    "l'URL n est pas valide pour le type de média choisi ({})".
                    format(self.media_type()))

        except GeoNatureError as e:
            raise GeoNatureError(
                "Il y a un problème avec l'URL renseignée : {}".format(str(e)))

    def file_path(self, thumbnail_height=None):
        file_path = None
        if self.media.media_path:
            file_path = self.media.media_path
        else:
            file_path = os.path.join(
                current_app.config["UPLOAD_FOLDER"],
                str(self.media.id_table_location),
                "{}.jpg".format(self.media.id_media),
            )

        if thumbnail_height:
            file_path = os.path.join(
                current_app.config["UPLOAD_FOLDER"],
                "thumbnails",
                str(self.media.id_table_location),
                "{}_thumbnail_{}.jpg".format(self.media.id_media,
                                             thumbnail_height),
            )

        return file_path

    def upload_file(self):
        """
            Upload des fichiers sur le serveur
        """

        # SI c'est une modification =>
        #       suppression de l'ancien fichier
        #       suppression des thumbnails
        if not self.new:
            self.media.remove_file()
            self.media.remove_thumbnails()

        # @TODO récupérer les exceptions
        filepath = upload_file(
            self.file,
            str(self.media.id_table_location),
            "{id_media}_{file_name}".format(id_media=self.media.id_media,
                                            file_name=self.file.filename),
        )

        return filepath

    def is_img(self):
        return self.media_type() == "Photo"

    def media_type(self):
        nomenclature = (DB.session.query(TNomenclatures).filter(
            TNomenclatures.id_nomenclature ==
            self.data["id_nomenclature_media_type"]).one())
        return nomenclature.label_fr

    def get_image(self):
        image = None

        if self.media.media_path:
            image = Image.open(self.absolute_file_path())

        if self.media.media_url:
            response = requests.get(self.media.media_url)
            image = Image.open(BytesIO(response.content))

        return image

    def get_image_with_exp(self):
        """
            Fonction qui tente de récupérer une image
            et qui lance des exceptions en cas d'erreur
        """

        try:
            return self.get_image()
        except Exception:
            if self.media.media_path:
                raise GeoNatureError(
                    "Le fichier fournit ne contient pas une image valide"
                ) from Exception
            else:
                raise GeoNatureError(
                    "L'URL renseignée ne contient pas une image valide"
                ) from Exception

    def has_thumbnails(self):
        """
            Test si la liste des thumbnails
            définis par défaut existe
        """
        for thumbnail_height in self.thumbnail_sizes:
            if not self.has_thumbnail(thumbnail_height):
                return False
        return True

    def has_thumbnail(self, size):
        """
            Test si le thumbnail de taille X existe
        """
        if not os.path.isfile(self.absolute_file_path(size)):
            return False
        return True

    def create_thumbnails(self):
        """
            Creation automatique des thumbnails
            dont les tailles sont spécifiés dans la config
        """
        # Test si les thumbnails existent déjà
        if self.has_thumbnails():
            return

        image = self.get_image_with_exp()

        for thumbnail_height in self.thumbnail_sizes:
            self.create_thumbnail(thumbnail_height, image)

    def create_thumbnail(self, size, image=None):
        if not image:
            image = self.get_image_with_exp()

        image_thumb = image.copy()
        width = size / image.size[1] * image.size[0]
        image_thumb.thumbnail((width, size))
        thumb_path = self.absolute_file_path(size)
        pathlib.Path("/".join(thumb_path.split("/")[:-1])).mkdir(parents=True,
                                                                 exist_ok=True)

        if image.mode in ("RGBA", "P"):
            image_thumb = image_thumb.convert("RGB")
        image_thumb.save(thumb_path, "JPEG")

        return thumb_path

    def get_thumbnail_url(self, size):
        """
            Fonction permettant de récupérer l'url d'un thumbnail
            Si le thumbnail n'existe pas il est créé à la volé
        """
        # Get Thumb path and create if not exists
        if not self.has_thumbnail(size):
            thumb_path = self.create_thumbnail(size)
        else:
            thumb_path = self.absolute_file_path(size)

        # Get relative path
        relative_path = os.path.relpath(
            thumb_path, os.path.join(current_app.config["BASE_DIR"], "static"))
        # Get URL
        thumb_url = url_for("static", filename=relative_path)
        return thumb_url

    def delete(self):
        # Note si SQLALCHEMY_TRACK_MODIFICATIONS  = true alors suppression du fichier gérée automatiquement

        # Suppression du média physiquement
        # En réalité renommage
        initial_path = self.media.media_path

        if self.media.media_path and not current_app.config[
                "SQLALCHEMY_TRACK_MODIFICATIONS"]:

            try:
                self.media.__before_commit_delete__()

            except FileNotFoundError:
                raise Exception("Unable to delete file")

        # Suppression du média dans la base
        try:
            DB.session.delete(self.media)
            DB.session.commit()
        except Exception:
            if initial_path:
                new_path = rename_file(self.media.media_path, initial_path)

    def _load_from_id(self, id_media):
        """
            Charge un média de la base à partir de son identifiant
        """
        media = DB.session.query(TMedias).get(id_media)
        return media