ftlr 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ftlr/__init__.py ADDED
File without changes
ftlr/config.py ADDED
@@ -0,0 +1,48 @@
1
+ from dataclasses import dataclass
2
+ from functools import cache
3
+ from typing import List
4
+
5
+ from ftlr.modification import Modification
6
+ from ftlr.xmp_types import XmpType, Factory
7
+
8
+ __types = Factory.instance()
9
+
10
+ __descriptions = [
11
+ ("crs:Exposure2012", "exposure", __types.real(), -5, 5),
12
+ ("crs:Contrast2012", "contrast", __types.integer(), -100, 100)
13
+ ]
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Config:
18
+ key: str
19
+ name: str
20
+ type: XmpType
21
+ min: float
22
+ max: float
23
+
24
+ @property
25
+ @cache
26
+ def args(self) -> List[str]:
27
+ short_name = f"-{self.name[0]}"
28
+ long_name = f"--{self.name}"
29
+
30
+ return [
31
+ short_name,
32
+ long_name,
33
+ ]
34
+
35
+ @property
36
+ @cache
37
+ def kwargs(self):
38
+ return {
39
+ "type": self.build_modification
40
+ }
41
+
42
+ def build_modification(self, value: str) -> Modification:
43
+ assert value.startswith("+") or value.startswith("-")
44
+
45
+ return Modification(self.key, self.type, self.type.python_type(value))
46
+
47
+
48
+ CONFIG = [Config(*desc) for desc in __descriptions]
ftlr/config_bak.py ADDED
@@ -0,0 +1,71 @@
1
+ from dataclasses import dataclass
2
+ from functools import cache
3
+ from pathlib import Path
4
+ from typing import Dict, Iterable, List
5
+
6
+ from ftlr.modifier import Modification
7
+ from ftlr.xmp_types import XmpType, Factory
8
+
9
+ __types = Factory.instance()
10
+
11
+ __dict_mapping = {
12
+ "x:xmpmeta": {
13
+ "rdf:RDF": {
14
+ "rdf:Description": {
15
+ "crs:Exposure2012": ("exposure", __types.real()),
16
+ "crs:Contrast2012": ("contrast", __types.integer())
17
+ },
18
+ },
19
+ },
20
+ }
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class Config:
25
+ path: Path
26
+ name: str
27
+ type: XmpType
28
+
29
+ @property
30
+ @cache
31
+ def args(self) -> List[str]:
32
+ short_name = f"-{self.name[0]}"
33
+ long_name = f"--{self.name}"
34
+
35
+ return [
36
+ short_name,
37
+ long_name,
38
+ ]
39
+
40
+ @property
41
+ @cache
42
+ def kwargs(self):
43
+ return {
44
+ "type": self.build_modification
45
+ }
46
+
47
+ def build_modification(self, value: str) -> Modification:
48
+ assert value.startswith("+") or value.startswith("-")
49
+
50
+ return Modification(self.path, self.type, self.type.python_type(value))
51
+
52
+
53
+ def __flatten_dict(d: Dict, path: Path) -> Iterable[Config]:
54
+ result = []
55
+
56
+ for key, value in d.items():
57
+ key_path = path / key
58
+
59
+ if isinstance(value, tuple):
60
+ name, type_ = value
61
+
62
+ result.append(Config(key_path, name, type_))
63
+ elif isinstance(value, dict):
64
+ result += __flatten_dict(value, key_path)
65
+ else:
66
+ assert False
67
+
68
+ return result
69
+
70
+
71
+ CONFIG = __flatten_dict(__dict_mapping, Path())
ftlr/modification.py ADDED
@@ -0,0 +1,21 @@
1
+ from dataclasses import dataclass
2
+
3
+ from ftlr.xmp_types import XmpType, ValueType
4
+ from ftlr.xmp import Xmp
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Modification:
9
+ key: str
10
+ xmp_type: XmpType
11
+ value: ValueType
12
+
13
+ def apply(self, xmp: Xmp):
14
+ original_str = xmp[self.key]
15
+ original_value = self.xmp_type.from_string(original_str)
16
+
17
+ modified_value = original_value + self.value
18
+ modified_str = self.xmp_type.to_string(modified_value)
19
+
20
+ xmp[self.key] = modified_str
21
+
ftlr/runner.py ADDED
@@ -0,0 +1,39 @@
1
+ from argparse import ArgumentParser
2
+ from glob import iglob
3
+ from pathlib import Path
4
+
5
+ from ftlr import config
6
+ from ftlr.modification import Modification
7
+ from ftlr.xmp import Xmp
8
+
9
+
10
+ def run():
11
+ print("running")
12
+
13
+ parser = ArgumentParser()
14
+
15
+ for cfg in config.CONFIG:
16
+ parser.add_argument(*cfg.args, **cfg.kwargs)
17
+
18
+ # parser.add_argument("pattern", type=Path, nargs="?", default=Path.cwd() / "*")
19
+ parser.add_argument("pattern", nargs="?", default="*")
20
+
21
+ namespace = parser.parse_args("-e -1.5 ../../25.*/*.xmp".split())
22
+
23
+ modifications = [mod for mod in vars(namespace).values() if isinstance(mod, Modification)]
24
+
25
+ for str_path in iglob(namespace.pattern):
26
+ path = Path(str_path)
27
+
28
+ assert path.suffix == Xmp.SUFFIX
29
+
30
+ with Xmp.read(path) as xmp:
31
+ for modification in modifications:
32
+ modification.apply(xmp)
33
+
34
+ # if error:
35
+
36
+
37
+
38
+ if __name__ == '__main__':
39
+ run()
ftlr/xmp.py ADDED
@@ -0,0 +1,61 @@
1
+ from contextlib import contextmanager
2
+ from pathlib import Path
3
+ from typing import Any, Generator, Self, List
4
+
5
+
6
+ # todo: add context management
7
+ class Xmp:
8
+ SEPARATOR = "/"
9
+ SUFFIX = ".xmp"
10
+
11
+ def __init__(self, lines: List[str]) -> None:
12
+ super().__init__()
13
+
14
+ self.__lines = lines
15
+
16
+ @staticmethod
17
+ def __value(s: str) -> str:
18
+ s = s.strip()
19
+
20
+ key, value = s.split("=")
21
+
22
+ value = value.strip("\"")
23
+
24
+ return value
25
+
26
+ def __getitem__(self, key: str) -> str:
27
+ good_lines = [line for line in self.__lines if key in line]
28
+
29
+ assert len(good_lines) == 1
30
+
31
+ line = good_lines[0]
32
+
33
+ value = Xmp.__value(line)
34
+
35
+ return value
36
+
37
+ def __setitem__(self, key: str, new_value: str):
38
+ good_lines = [(index, line) for index, line in enumerate(self.__lines) if key in line]
39
+
40
+ assert len(good_lines) == 1
41
+
42
+ index, line = good_lines[0]
43
+
44
+ old_value = Xmp.__value(line)
45
+
46
+ line = line.replace(old_value, new_value)
47
+
48
+ self.__lines[index] = line
49
+
50
+ @classmethod
51
+ @contextmanager
52
+ def read(cls, path: Path) -> Generator[Self, Any, None]:
53
+ with path.open() as xmp_file:
54
+ lines = xmp_file.readlines()
55
+
56
+ xmp = cls(lines)
57
+
58
+ yield xmp
59
+
60
+ with path.open("w") as xmp_file:
61
+ xmp_file.writelines(xmp.__lines)
ftlr/xmp_types.py ADDED
@@ -0,0 +1,66 @@
1
+ from abc import abstractmethod
2
+ from functools import cache
3
+ from typing import Type
4
+
5
+ ValueType = float | int
6
+
7
+
8
+ class XmpType:
9
+ @property
10
+ @abstractmethod
11
+ def python_type(self) -> Type[ValueType]:
12
+ pass
13
+
14
+ def from_string(self, value: str) -> ValueType:
15
+ return self.python_type(value)
16
+
17
+ @abstractmethod
18
+ def prepare_value(self, value: ValueType) -> ValueType:
19
+ pass
20
+
21
+ def to_string(self, value: ValueType) -> str:
22
+ if value > 0:
23
+ sign = "+"
24
+ elif value < 0:
25
+ sign = "-"
26
+ else:
27
+ sign = ""
28
+
29
+ value = abs(self.prepare_value(value))
30
+
31
+ return sign + str(value)
32
+
33
+
34
+ class XmpReal(XmpType):
35
+ @property
36
+ def python_type(self) -> Type[ValueType]:
37
+ return float
38
+
39
+ def prepare_value(self, value: ValueType) -> ValueType:
40
+ return round(value, ndigits=2)
41
+
42
+
43
+ class XmpInteger(XmpType):
44
+ @property
45
+ def python_type(self) -> Type[ValueType]:
46
+ return int
47
+
48
+ def prepare_value(self, value: ValueType) -> ValueType:
49
+ return int(round(value))
50
+
51
+
52
+ class Factory:
53
+ __instance = None
54
+
55
+ @classmethod
56
+ @cache
57
+ def instance(cls) -> "Factory":
58
+ return Factory()
59
+
60
+ @cache
61
+ def real(self) -> XmpType:
62
+ return XmpReal()
63
+
64
+ @cache
65
+ def integer(self) -> XmpType:
66
+ return XmpInteger()
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: ftlr
3
+ Version: 0.0.1
4
+ Home-page:
5
+ Author: Igor Djachenko
6
+ Author-email: i.s.djachenko@gmail.com
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/djachenko/ftlr
9
+ Project-URL: Repository, https://github.com/djachenko/ftlr
10
+ Project-URL: Issues, https://github.com/djachenko/ftlr/issues
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: xmltodict
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest; extra == "test"
16
+ Requires-Dist: ruff; extra == "test"
17
+ Requires-Dist: mypy; extra == "test"
18
+ Requires-Dist: types-xmltodict; extra == "test"
19
+ Provides-Extra: release
20
+ Requires-Dist: build; extra == "release"
21
+ Requires-Dist: python-semantic-release; extra == "release"
22
+ Dynamic: author-email
23
+
24
+ # ftlr
25
+
26
+ Usage: `~$ ftlr [--exposure EXPOSURE]`
@@ -0,0 +1,12 @@
1
+ ftlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ftlr/config.py,sha256=vSBFG4pq5Ds72bTBQgzcN9yKcynmZhQhmh19sDmm8os,1058
3
+ ftlr/config_bak.py,sha256=84F2PxSi5LppXepkdo1rTnqecjjLp511GAaTp7TUm8c,1597
4
+ ftlr/modification.py,sha256=ai2vkJcmBVBlieC-yEsKuw3ifgH1D_qk6YHH7zWQHMM,498
5
+ ftlr/runner.py,sha256=rqPYgtupNG7b1vrZ_LcjXqsv9b-t0fuFiAK_wtyys5M,950
6
+ ftlr/xmp.py,sha256=AM64z-opcxLLPa0f_n5uVxIXmOy86efDGdVpuZH1mfI,1362
7
+ ftlr/xmp_types.py,sha256=VlWQLI99T-BBWtywZ0bxNTEkyAa_t-h3OJTrcZtnbkc,1345
8
+ ftlr-0.0.1.dist-info/METADATA,sha256=ZHnz1fY8LuCUbMZaQy6gmui_uaVRmHEhTJW_K0Va8W0,788
9
+ ftlr-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ ftlr-0.0.1.dist-info/entry_points.txt,sha256=UknK7g-IzxMg-VvoK4JwikVw0ox2bQRvPvv_feoe3iE,41
11
+ ftlr-0.0.1.dist-info/top_level.txt,sha256=wVpZu7b0tE3PxJRoBbOodJxSD9IOqaIOhy74ID5G280,5
12
+ ftlr-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ftlr = ftlr.runner:run
@@ -0,0 +1 @@
1
+ ftlr