Beispiel #1
0
def load_meta(img_file: Path) -> ImageMetadata:
    """
    Load the metadata tuple for a given image file.

    If no metadata file is present, or it is currently inaccessible, return a blank metadata tuple.

    :arg img_file: a path pointing to a managed image for which we want to load metadata
    :return: the associated metadata as a tuple, or a blank metadata tuple
    """

    meta_file = _construct_metadata_path(img_file)

    if meta_file.exists():
        try:
            with meta_file.open() as mf:
                metadata = parse_xml(mf.read())

                # Check if 'file' is a valid URI, otherwise make it so (for retro-compatibility with older schema)
                if metadata.file.scheme is None:
                    metadata = _old_to_new_schema(img_file, metadata)
        except (OSError, ParseError):
            metadata = ImageMetadata(uuid3(NAMESPACE_URL, str(URI(img_file))),
                                     URI(img_file), None, None, None, None)
    else:
        metadata = ImageMetadata(uuid3(NAMESPACE_URL, str(URI(img_file))),
                                 URI(img_file), None, None, None, None)

    return metadata
Beispiel #2
0
    def test_collective_disjunctive_filter(self):
        # Verify the effectiveness of multi-valued matchers, when evaluated in disjunction
        chars1 = ["al", "john", "jack"]
        chars2 = ["jm", "jr"]
        chars3 = ["jr"]

        el1 = ImageMetadata(uuid4(), URI("a.png"), "ghi", None, chars1, None)
        el2 = ImageMetadata(uuid4(), URI("b.png"), "nsh", None, chars2, None)
        el3 = ImageMetadata(uuid4(), URI("c.png"), "ShT", None, chars3, None)

        filter_builder = FilterBuilder()

        # Test disjunctive filtering with inclusion
        f = filter_builder.character_constraint("jm").character_constraint("al").characters_as_disjunctive(True) \
            .get_character_filter()
        self.assertTrue(f(el1))
        self.assertTrue(f(el2))
        self.assertFalse(f(el3))

        # Test disjunctive filtering with exclusion
        f = filter_builder.character_constraint("jack", True).get_character_filter()
        self.assertTrue(f(el1))
        self.assertTrue(f(el2))
        self.assertTrue(f(el3))

        filter_builder = FilterBuilder()
        f = filter_builder.characters_as_disjunctive(True).character_constraint("john", True) \
            .character_constraint("jack", True).get_character_filter()
        self.assertFalse(f(el1))
        self.assertTrue(f(el2))
        self.assertTrue(f(el3))
Beispiel #3
0
    def test_single_element_filter(self):
        # Verify the effectiveness of single-valued matchers
        id1, filename1, author1, universe1 = uuid4(), URI("01.png"), "bdhnd", "fotwf"
        id2, filename2, author2, universe2 = uuid4(), URI("02.png"), "shndl", None
        id3, filename3, author3, universe3 = uuid4(), URI("03.png"), "okn", "ph"

        el1 = ImageMetadata(id1, filename1, author1, universe1, None, None)
        el2 = ImageMetadata(id2, filename2, author2, universe2, None, None)
        el3 = ImageMetadata(id3, filename3, author3, universe3, None, None)

        filter_builder = FilterBuilder()

        # Test constraints satisfied
        filter_builder.filename_constraint(filename1.path.name) \
            .filename_constraint(filename2.path.name) \
            .filename_constraint(filename3.path.name)
        filename_filter = filter_builder.get_filename_filter()
        self.assertTrue(filename_filter(el1))
        self.assertTrue(filename_filter(el2))
        self.assertTrue(filename_filter(el3))

        # Test implicit exclusion
        filter_builder.author_constraint(author1)
        author_filter = filter_builder.get_author_filter()
        self.assertTrue(author_filter(el1))
        self.assertFalse(author_filter(el2))
        self.assertFalse(author_filter(el3))

        # Test explicit exclusion
        filter_builder.id_constraint(str(id2), True)
        id_filter = filter_builder.get_id_filter()
        self.assertTrue(id_filter(el1))
        self.assertFalse(id_filter(el2))
        self.assertTrue(id_filter(el3))
Beispiel #4
0
    def test_empty_filter_single(self):
        # Check the effects of the absence of constraints on single-valued matchers
        el1 = ImageMetadata(uuid4(), URI('xxx'), None, None, None, None)
        el2 = ImageMetadata(uuid4(), URI('kkk'), None, None, None, None)

        empty_filter = FilterBuilder().get_id_filter()
        self.assertTrue(empty_filter(el1))
        self.assertTrue(empty_filter(el2))
Beispiel #5
0
    def test_none_match_collective(self):
        # Check the effects of None constraints on multi-valued matchers
        el1 = ImageMetadata(uuid4(), URI('aaa'), None, None, None, None)
        el2 = ImageMetadata(uuid4(), URI('yyy'), None, None, None, ["fta"])

        none_filter = FilterBuilder().tag_constraint(None).get_tag_filter()
        self.assertTrue(none_filter(el1))
        self.assertFalse(none_filter(el2))
Beispiel #6
0
    def test_empty_filter_collective(self):
        # Check the effects of the absence of constraints on multi-valued matchers
        el1 = ImageMetadata(uuid4(), URI('xxx'), None, None, None, ["nl", "ll"])
        el2 = ImageMetadata(uuid4(), URI('kkk'), None, None, None, None)

        empty_filter = FilterBuilder().get_tag_filter()
        self.assertTrue(empty_filter(el1))
        self.assertTrue(empty_filter(el2))
Beispiel #7
0
    def test_none_match_single(self):
        # Check the effects of None constraints on single-valued matchers
        el1 = ImageMetadata(uuid4(), URI('fff'), None, 'u', None, None)
        el2 = ImageMetadata(uuid4(), URI('zzz'), None, None, None, None)

        none_filter = FilterBuilder().universe_constraint(None).get_universe_filter()
        self.assertFalse(none_filter(el1))
        self.assertTrue(none_filter(el2))
Beispiel #8
0
    def test_without_optional_elements(self):
        img_id = uuid.UUID('03012ba3-086c-4604-bd6a-aa3e1a78f389')
        file = uri.URI("test.png")

        self.assertEqual(
            ImageMetadata(img_id, file, None, None, None, None),
            xm.parse_xml(
                xm.generate_xml(
                    ImageMetadata(img_id, file, None, None, None, None))))
Beispiel #9
0
    def test_with_optional_elements(self):
        img_id = uuid.UUID('03012ba3-086c-4604-bd6a-aa3e1a78f389')
        file = uri.URI("test.png")
        author = "John Doe"
        universe = "Example"
        characters = ["M", "Q"]
        tags = ["a", "bee"]

        self.assertEqual(
            ImageMetadata(img_id, file, author, universe, characters, tags),
            xm.parse_xml(
                xm.generate_xml(
                    ImageMetadata(img_id, file, author, universe, characters,
                                  tags))))
Beispiel #10
0
def _old_to_new_schema(img_path: Path, old_meta: ImageMetadata):
    return ImageMetadata(img_id=old_meta.img_id,
                         file=URI(img_path),
                         author=old_meta.author,
                         universe=old_meta.universe,
                         characters=old_meta.characters,
                         tags=old_meta.tags)
Beispiel #11
0
def parse_xml(data: str) -> ImageMetadata:
    """Parse an XML containing image metadata.

    :param data: a string containing valid image metadata
    :return: an image metadata object"""

    image_elem = ElTree.fromstring(data)
    img_id = image_elem.get('id')
    file = image_elem.get('file')

    # If we were presented with a legacy XML not containing 'file', use the legacy name 'filename'
    if file is None:
        file = image_elem.get('filename')

    author = image_elem.find("./author")
    universe = image_elem.find("./universe")
    characters = [
        char.text for char in image_elem.findall("./characters/character")
    ]
    tags = [tag.text for tag in image_elem.findall("./tags/tag")]

    return ImageMetadata(
        img_id=UUID(img_id),
        file=URI(file),
        author=author.text if author is not None else None,
        universe=universe.text if universe is not None else None,
        characters=characters if len(characters) != 0 else None,
        tags=tags if len(tags) != 0 else None)
Beispiel #12
0
    def test_generate_minimal(self):
        img_id = uuid.UUID('03012ba3-086c-4604-bd6a-aa3e1a78f389')
        file = uri.URI("test.png")
        minimal_xml = xm.generate_xml(
            ImageMetadata(img_id, file, None, None, None, None))
        expected = '<image id="{i}" file="{f}" />'.format(i=img_id, f=file)

        self.assertEqual(expected, minimal_xml)
Beispiel #13
0
    def meta_extractor(v: View) -> ImageMetadata:
        characters = v.get_characters()
        characters = characters.split(', ') if characters is not None else None

        tags = v.get_tags()
        tags = tags.split(', ') if tags is not None else None

        return ImageMetadata(v.image_id, URI(v._image_path), v.get_author(),
                             v.get_universe(), characters, tags)
Beispiel #14
0
    def write(self) -> None:
        """
        Persist the updated metadata.

        :raise OSError: when the metadata file couldn't be opened
        """

        meta_obj = ImageMetadata(self._id, URI(self._image_path), self.author,
                                 self.universe, self.characters, self.tags)
        write_meta(meta_obj, self._image_path)
Beispiel #15
0
    def test_legacy_load(self):
        image_uri = URI(self.test_path / "test.png")
        with (self.test_path / "test.xml").open('w') as f:
            f.write(
                "<image id=\"97ed6183-73a0-46ea-b51d-0721b0fbd357\" filename=\"test.png\"></image>"
            )

        loaded = load_meta(Path(image_uri.path))
        self.assertEqual(
            ImageMetadata(UUID('97ed6183-73a0-46ea-b51d-0721b0fbd357'),
                          image_uri, None, None, None, None), loaded)
Beispiel #16
0
    def test_load_actual(self):
        image_uri = URI(self.test_path / "test.png")
        with (self.test_path / "test.xml").open('w') as f:
            f.write(
                "<image id=\"97ed6183-73a0-46ea-b51d-0721b0fbd357\" file=\"" +
                str(image_uri) + "\">" +
                "<author>a</author><universe>u</universe>" +
                "<characters><character>x</character><character>y</character></characters>"
                + "<tags><tag>f</tag><tag>a</tag></tags></image>")

        self.assertEqual(
            ImageMetadata(UUID('97ed6183-73a0-46ea-b51d-0721b0fbd357'),
                          image_uri, "a", "u", ["x", "y"], ["f", "a"]),
            load_meta(Path(self.test_dir.name) / "test.png"))
Beispiel #17
0
    def test_collective_conjunctive_filter(self):
        # Verify the effectiveness of multi-valued matchers, when evaluated in conjunction
        tags1 = ["y", "an", "hry"]
        tags2 = ["an", "mb", "sty", "rp"]
        tags3 = ["ll", "vnl"]

        el1 = ImageMetadata(uuid4(), URI("a.png"), "ghi", None, None, tags1)
        el2 = ImageMetadata(uuid4(), URI("b.png"), "nsh", None, None, tags2)
        el3 = ImageMetadata(uuid4(), URI("c.png"), "ShT", None, None, tags3)

        filter_builder = FilterBuilder()

        # Test conjunctive filtering with inclusion
        f = filter_builder.tag_constraint("an").get_tag_filter()
        self.assertTrue(f(el1))
        self.assertTrue(f(el2))
        self.assertFalse(f(el3))

        # Test conjunctive filtering with exclusion
        f = filter_builder.tag_constraint("y", True).get_tag_filter()
        self.assertFalse(f(el1))
        self.assertTrue(f(el2))
        self.assertFalse((f(el3)))
Beispiel #18
0
    def test_filter(self):
        filenames_meta = [("included.png", "included"),
                          ("excluded.jpg", "excluded")]
        for name, meta in filenames_meta:
            img_path = (Path(self.test_dir.name) / name)
            img_path.touch()
            # Use the author field as the filtering target
            write_meta(ImageMetadata(uuid4(), name, meta, None, None, None),
                       img_path)

        specimen = Carousel(Path(self.test_dir.name),
                            [lambda meta: meta.author == "included"])

        self.assertEqual([Path(self.test_dir.name) / "included.png"],
                         specimen._image_files)
Beispiel #19
0
    def test_metadata_read(self):
        # Write some metadata for one of the images
        meta1 = ImageMetadata(
            img_id=UUID('f32ed6ad-1162-4ea6-b243-1e6c91fb7eda'),
            file=URI(self.test_path / '01.png'),
            author="a",
            universe="p",
            characters=["x", "y"],
            tags=["t", "f"])
        write_meta(meta1, (self.test_path / '01.png'))

        specimen = GtkView(self.test_path)

        # Collect metadata from the specimen
        results = dict()
        for _ in range(0, 2):
            specimen.load_next()
            results[specimen.filename] = TestView.meta_extractor(specimen)

        self.assertEqual(meta1, results["01.png"])
        self.assertEqual(
            ImageMetadata(results["02.png"].img_id,
                          URI(self.test_path / "02.png"), None, None, None,
                          None), results["02.png"])
Beispiel #20
0
    def test_store(self):
        image_uri = URI(self.test_path / "test.png")
        write_meta(
            ImageMetadata(UUID('97ed6183-73a0-46ea-b51d-0721b0fbd357'),
                          image_uri, "a", "u", ["x", "y"], ["f", "a"]),
            Path(self.test_dir.name) / "test.png")

        with (self.test_path / "test.xml") as f:
            result = f.read_text()

        self.assertEqual(
            "<image id=\"97ed6183-73a0-46ea-b51d-0721b0fbd357\" file=\"" +
            str(image_uri) + "\">" +
            "<author>a</author><universe>u</universe>" +
            "<characters><character>x</character><character>y</character></characters>"
            + "<tags><tag>f</tag><tag>a</tag></tags></image>", result)
Beispiel #21
0
    def test_generate_single_element_and_subtree(self):
        img_id = uuid.UUID('03012ba3-086c-4604-bd6a-aa3e1a78f389')
        file = uri.URI("test.png")
        universe = "unknown"
        tags = ["a", "b"]
        new_xml = xm.generate_xml(
            ImageMetadata(img_id, file, None, universe, None, tags))
        expected = ('<image id="{i}" file="{f}">' +
                    '<universe>{u}</universe>' +
                    '<tags><tag>{t1}</tag><tag>{t2}</tag></tags>' +
                    '</image>').format(i=img_id,
                                       f=file,
                                       u=universe,
                                       t1=tags[0],
                                       t2=tags[1])

        self.assertEqual(expected, new_xml)
Beispiel #22
0
    def test_metadata_update(self):
        target_filename = '02.png'
        specimen = GtkView(self.test_path)

        # Scan until we find our target
        specimen.load_next()
        while specimen.filename != target_filename:
            specimen.load_next()

        # Verify that no metadata is present
        self.assertIsNone(specimen.get_tags())

        # Set metadata and check coherence
        specimen.set_author(":DD")
        specimen.set_universe("\tu")
        specimen.set_characters("3, f,p\n")
        specimen.set_tags("fa,s \vjo,\u200dl")
        specimen.write()

        self.assertEqual(
            ImageMetadata(specimen.image_id,
                          URI(self.test_path / target_filename), ":DD", "u",
                          ["3", "f", "p"], ["fa", "s jo", "l"]),
            load_meta(self.test_path / target_filename))
Beispiel #23
0
 def test_parse_legacy(self):
     xml_string_legacy = "<image id=\"03012ba3-086c-4604-bd6a-aa3e1a78f389\" filename=\"tt.png\"></image>"
     metadata = ImageMetadata(
         uuid.UUID('03012ba3-086c-4604-bd6a-aa3e1a78f389'),
         uri.URI("tt.png"), None, None, None, None)
     self.assertEqual(metadata, xm.parse_xml(xml_string_legacy))