diff --git a/app/scodoc/sco_logos.py b/app/scodoc/sco_logos.py index 0737e7f031..dba5dc61ea 100644 --- a/app/scodoc/sco_logos.py +++ b/app/scodoc/sco_logos.py @@ -137,8 +137,6 @@ class Logo: self.logoname = secure_filename(logoname) self.scodoc_dept_id = dept_id self.prefix = prefix or "" - self.suffix = None - self.dimensions = None if self.scodoc_dept_id: self.dirpath = os.path.sep.join( [ @@ -151,8 +149,16 @@ class Logo: self.basepath = os.path.sep.join( [self.dirpath, self.prefix + secure_filename(self.logoname)] ) - self.filepath = None - self.filename = None + # next attributes are computer by the select function + self.suffix = "Not inited: call the select or create function before access" + self.filepath = "Not inited: call the select or create function before access" + self.filename = "Not inited: call the select or create function before access" + self.size = "Not inited: call the select or create function before access" + self.aspect_ratio = ( + "Not inited: call the select or create function before access" + ) + self.density = "Not inited: call the select or create function before access" + self.cm = "Not inited: call the select or create function before access" def _set_format(self, fmt): self.suffix = fmt @@ -182,6 +188,31 @@ class Logo: except IOError: pass + def _read_info(self, img): + """computes some properties from the real image + aspect_ratio assumes that x_density and y_density are equals + """ + x_size, y_size = img.size + self.density = img.info.get("dpi", None) + unit = 1 + if self.density is None: # no dpi found try jfif infos + self.density = img.info.get("jfif_density", None) + unit = img.info.get("jfif_unit", 0) # 0 = no unit ; 1 = inch ; 2 = cm + if self.density is not None: + x_density, y_density = self.density + if unit != 0: + unit2cm = [0, 1 / 2.54, 1][unit] + x_cm = round(x_size * unit2cm / x_density, 2) + y_cm = round(y_size * unit2cm / y_density, 2) + self.cm = (x_cm, y_cm) + else: + self.cm = None + else: + self.cm = None + + self.size = (x_size, y_size) + self.aspect_ratio = float(x_size) / y_size + def select(self): """ Récupération des données pour un logo existant @@ -195,7 +226,7 @@ class Logo: self._set_format(suffix) with open(self.filepath, "rb") as f: img = PILImage.open(f) - self.dimensions = img.size + self._read_info(img) return self return None diff --git a/tests/ressources/test_logos/logo_A.jpg b/tests/ressources/test_logos/logo_A.jpg new file mode 100644 index 0000000000..7f107a05b6 Binary files /dev/null and b/tests/ressources/test_logos/logo_A.jpg differ diff --git a/tests/ressources/test_logos/logo_C.jpg b/tests/ressources/test_logos/logo_C.jpg new file mode 100644 index 0000000000..bdca2b7360 Binary files /dev/null and b/tests/ressources/test_logos/logo_C.jpg differ diff --git a/tests/ressources/test_logos/logo_D.png b/tests/ressources/test_logos/logo_D.png new file mode 100644 index 0000000000..7ac56e1681 Binary files /dev/null and b/tests/ressources/test_logos/logo_D.png differ diff --git a/tests/ressources/test_logos/logo_E.jpg b/tests/ressources/test_logos/logo_E.jpg new file mode 100644 index 0000000000..e97b78d07f Binary files /dev/null and b/tests/ressources/test_logos/logo_E.jpg differ diff --git a/tests/ressources/test_logos/logo_F.jpeg b/tests/ressources/test_logos/logo_F.jpeg new file mode 100644 index 0000000000..c3750e29f2 Binary files /dev/null and b/tests/ressources/test_logos/logo_F.jpeg differ diff --git a/tests/ressources/test_logos/logos_1/logo_A.jpg b/tests/ressources/test_logos/logos_1/logo_A.jpg new file mode 100644 index 0000000000..96cfabd5e5 Binary files /dev/null and b/tests/ressources/test_logos/logos_1/logo_A.jpg differ diff --git a/tests/ressources/test_logos/logos_1/logo_B.jpg b/tests/ressources/test_logos/logos_1/logo_B.jpg new file mode 100644 index 0000000000..2ebb82823e Binary files /dev/null and b/tests/ressources/test_logos/logos_1/logo_B.jpg differ diff --git a/tests/ressources/test_logos/logos_2/logo_A.jpg b/tests/ressources/test_logos/logos_2/logo_A.jpg new file mode 100644 index 0000000000..8c1c4efcb5 Binary files /dev/null and b/tests/ressources/test_logos/logos_2/logo_A.jpg differ diff --git a/tests/unit/test_logos.py b/tests/unit/test_logos.py new file mode 100644 index 0000000000..8cf2e275be --- /dev/null +++ b/tests/unit/test_logos.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- + +"""Test Logos + + +Utiliser comme: + pytest tests/unit/test_logos.py + +""" +from io import BytesIO +from pathlib import Path +from shutil import copytree, copy, rmtree + +import pytest as pytest +from _pytest.python_api import approx + +import app +from app import db +from app.models import Departement +import app.scodoc.sco_utils as scu +from app.scodoc.sco_logos import find_logo, Logo, list_logos + +RESOURCES_DIR = "/opt/scodoc/tests/ressources/test_logos" + + +@pytest.fixture +def create_dept(test_client): + """Crée 2 départements: + return departements object + """ + dept1 = Departement(acronym="RT") + dept2 = Departement(acronym="INFO") + db.session.add(dept1) + db.session.add(dept2) + db.session.commit() + yield dept1, dept2 + db.session.delete(dept1) + db.session.delete(dept2) + db.session.commit() + + +@pytest.fixture +def create_logos(create_dept): + """Crée les logos: + ...logos --+-- logo_A.jpg + +-- logo_C.jpg + +-- logo_D.png + +-- logo_E.jpg + +-- logo_F.jpeg + +-- logos_{d1} --+-- logo_A.jpg + | +-- logo_B.jpg + +-- logos_{d2} --+-- logo_A.jpg + + """ + dept1, dept2 = create_dept + d1 = dept1.id + d2 = dept2.id + FILE_LIST = ["logo_A.jpg", "logo_C.jpg", "logo_D.png", "logo_E.jpg", "logo_F.jpeg"] + for fn in FILE_LIST: + from_path = Path(RESOURCES_DIR).joinpath(fn) + to_path = Path(scu.SCODOC_LOGOS_DIR).joinpath(fn) + copy(from_path.absolute(), to_path.absolute()) + copytree( + f"{RESOURCES_DIR}/logos_1", + f"{scu.SCODOC_LOGOS_DIR}/logos_{d1}", + ) + copytree( + f"{RESOURCES_DIR}/logos_2", + f"{scu.SCODOC_LOGOS_DIR}/logos_{d2}", + ) + yield None + rmtree(f"{scu.SCODOC_LOGOS_DIR}/logos_{d1}") + rmtree(f"{scu.SCODOC_LOGOS_DIR}/logos_{d2}") + # rm files + for fn in FILE_LIST: + to_path = Path(scu.SCODOC_LOGOS_DIR).joinpath(fn) + to_path.unlink() + + +def test_select_global_only(create_logos): + C_logo = app.scodoc.sco_logos.find_logo(logoname="C") + assert C_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logo_C.jpg" + + +def test_select_local_only(create_dept, create_logos): + dept1, dept2 = create_dept + B_logo = app.scodoc.sco_logos.find_logo(logoname="B", dept_id=dept1.id) + assert B_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_B.jpg" + + +def test_select_local_override_global(create_dept, create_logos): + dept1, dept2 = create_dept + A1_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id) + assert A1_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg" + + +def test_select_global_with_strict(create_dept, create_logos): + dept1, dept2 = create_dept + A_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id, strict=True) + assert A_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg" + + +def test_looks_for_non_existant_should_give_none(create_dept, create_logos): + # search for a local non-existant logo returns None + dept1, dept2 = create_dept + no_logo = app.scodoc.sco_logos.find_logo(logoname="Z", dept_id=dept1.id) + assert no_logo is None + + +def test_looks_localy_for_a_global_should_give_none(create_dept, create_logos): + # search for a local non-existant logo returns None + dept1, dept2 = create_dept + no_logo = app.scodoc.sco_logos.find_logo( + logoname="C", dept_id=dept1.id, strict=True + ) + assert no_logo is None + + +def test_get_jpg_data(create_dept, create_logos): + logo = find_logo("A", dept_id=None) + assert logo is not None + logo.select() + assert logo.logoname == "A" + assert logo.suffix == "jpg" + assert logo.filename == "A.jpg" + assert logo.size == (1200, 600) + assert logo.cm == approx((4.0, 3.0), 0.01) + + +def test_get_png_without_data(create_dept, create_logos): + logo = find_logo("D", dept_id=None) + assert logo is not None + logo.select() + assert logo.logoname == "D" + assert logo.suffix == "png" + assert logo.filename == "D.png" + assert logo.size == (121, 121) + assert logo.density is None + assert logo.cm is None + + +def test_create_globale_jpg_logo(create_dept, create_logos): + path = Path(f"{RESOURCES_DIR}/logo_C.jpg") + logo = Logo("X") # create global logo + stream = path.open("rb") + + +def test_create_jpg_instead_of_png_logo(create_dept, create_logos): + # action + logo = Logo("D") # create global logo (replace logo_D.png) + path = Path(f"{RESOURCES_DIR}/logo_C.jpg") + stream = path.open("rb") + logo.create(stream) + # test + created = Path(f"{scu.SCODOC_LOGOS_DIR}/logo_D.jpg") + removed = Path(f"{scu.SCODOC_LOGOS_DIR}/logo_D.png") + # file system check + assert created.exists() + assert not removed.exists() + # logo check + logo = find_logo("D") + assert logo is not None + assert logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logo_D.jpg" # created.absolute() + # restore initial state + original = Path(f"{RESOURCES_DIR}/logo_D.png") + copy(original, removed) + created.unlink(missing_ok=True) + + +def test_list_logo(create_dept, create_logos): + # test only existence of copied logos. We assumes that they are OK + dept1, dept2 = create_dept + logos = list_logos() + assert logos.keys() == {"_GLOBAL", "RT", "INFO"} + assert {"A", "C", "D", "E", "F", "header", "footer"}.issubset( + set(logos["_GLOBAL"].keys()) + ) + rt = logos.get("RT", None) + assert rt is not None + assert {"A", "B"}.issubset(set(rt.keys())) + info = logos.get("INFO", None) + assert info is not None + assert {"A"}.issubset(set(rt.keys()))