flinventory 0.3.1__py3-none-any.whl → 0.4.2__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.
- flinventory/inventory_io.py +68 -4
- flinventory/location.py +1 -0
- flinventory/sign.py +7 -4
- flinventory/thing.py +4 -3
- {flinventory-0.3.1.dist-info → flinventory-0.4.2.dist-info}/METADATA +2 -2
- {flinventory-0.3.1.dist-info → flinventory-0.4.2.dist-info}/RECORD +9 -9
- {flinventory-0.3.1.dist-info → flinventory-0.4.2.dist-info}/WHEEL +1 -1
- {flinventory-0.3.1.dist-info → flinventory-0.4.2.dist-info}/entry_points.txt +0 -0
- {flinventory-0.3.1.dist-info → flinventory-0.4.2.dist-info}/licenses/LICENSE +0 -0
flinventory/inventory_io.py
CHANGED
@@ -8,7 +8,7 @@ import random
|
|
8
8
|
import argparse
|
9
9
|
import logging
|
10
10
|
import os
|
11
|
-
from typing import Callable, Sequence, Iterable, Optional, Self,
|
11
|
+
from typing import Callable, Sequence, Iterable, Optional, Self, Iterator, cast
|
12
12
|
|
13
13
|
from . import constant
|
14
14
|
from .box import BoxedThing
|
@@ -120,7 +120,6 @@ class Inventory(list[BoxedThing]):
|
|
120
120
|
Saving schema would be possible with schema.json attribute.
|
121
121
|
todo: maybe save options
|
122
122
|
"""
|
123
|
-
logger = logging.getLogger("save_things")
|
124
123
|
for thing in self:
|
125
124
|
thing.save()
|
126
125
|
|
@@ -148,7 +147,7 @@ class Inventory(list[BoxedThing]):
|
|
148
147
|
return thing
|
149
148
|
raise KeyError(thing_id)
|
150
149
|
|
151
|
-
def get_by_key(self, key, value) ->
|
150
|
+
def get_by_key(self, key, value) -> Iterable[BoxedThing]:
|
152
151
|
"""Return all boxed things that have this value for this key.
|
153
152
|
|
154
153
|
For example useful for looking up thing by name: get_by_key(('name', 0), 'Nice thing')
|
@@ -174,6 +173,71 @@ class Inventory(list[BoxedThing]):
|
|
174
173
|
"""
|
175
174
|
return (box for box in self if box.thing.best(key, backup=None) == value)
|
176
175
|
|
176
|
+
def direct_ancestors(self, thing: BoxedThing, key: str) -> set[BoxedThing]:
|
177
|
+
"""Convert key (assuming list of ids) into set of things.
|
178
|
+
|
179
|
+
Intended for key = subclass_of and part_of.
|
180
|
+
"""
|
181
|
+
generalisations = set()
|
182
|
+
for thing_id in cast(tuple, thing.get(key, tuple())):
|
183
|
+
try:
|
184
|
+
generalisations.add(self.get_by_id(thing_id))
|
185
|
+
except KeyError:
|
186
|
+
logging.getLogger("inventory.direct_generalisations").warning(
|
187
|
+
f"id {thing_id} in {self.get_id(thing)} ({thing.best('name', backup="?")}) "
|
188
|
+
"subclass_of is not a valid id. Ignore it."
|
189
|
+
)
|
190
|
+
return generalisations
|
191
|
+
|
192
|
+
def ancestors(self, thing: BoxedThing, key: str) -> set[BoxedThing]:
|
193
|
+
"""Get all direct and indirect key of elements as things.
|
194
|
+
|
195
|
+
Intended for key = subclass_of and part_of.
|
196
|
+
"""
|
197
|
+
generalisations = set()
|
198
|
+
unchecked = {thing}
|
199
|
+
while unchecked:
|
200
|
+
current = unchecked.pop()
|
201
|
+
generalisations.add(current)
|
202
|
+
unchecked.update(self.direct_ancestors(current, key))
|
203
|
+
# to avoid checking something again, avoiding infinite loops:
|
204
|
+
unchecked.difference_update(generalisations)
|
205
|
+
return generalisations.difference({thing})
|
206
|
+
|
207
|
+
def super_things(self, thing: BoxedThing) -> set[BoxedThing]:
|
208
|
+
"""Get all things this thing is a part of. Directly or indirectly.
|
209
|
+
|
210
|
+
That is: a part_of b & b part_of c ⇒ a part_of c
|
211
|
+
and a subclass_of b not⇒ a part_of b but
|
212
|
+
a subclass_of b & b part_of c ⇒ a part_of c
|
213
|
+
|
214
|
+
Infinite loops must be avoided, so simple recursive call might be problematic.
|
215
|
+
|
216
|
+
Not implemented but maybe necessary: caching result in the thing.
|
217
|
+
Cache must be invalidated whenever part_of or subclass_of of any
|
218
|
+
included thing changes. Difficult. Better would be cache on the
|
219
|
+
direct level.
|
220
|
+
"""
|
221
|
+
supers = set()
|
222
|
+
unchecked = {thing}
|
223
|
+
generalisations = {thing} # only saved to avoid recursion loop
|
224
|
+
unchecked_generalisations = set()
|
225
|
+
while unchecked or unchecked_generalisations:
|
226
|
+
if unchecked:
|
227
|
+
current = unchecked.pop()
|
228
|
+
supers.add(current)
|
229
|
+
else:
|
230
|
+
current = unchecked_generalisations.pop()
|
231
|
+
generalisations.add(current)
|
232
|
+
unchecked.update(self.direct_ancestors(current, "part_of"))
|
233
|
+
unchecked_generalisations.update(
|
234
|
+
self.direct_ancestors(current, "subclass_of")
|
235
|
+
)
|
236
|
+
unchecked.difference_update(supers)
|
237
|
+
unchecked_generalisations.difference_update(generalisations)
|
238
|
+
|
239
|
+
return supers.difference({thing})
|
240
|
+
|
177
241
|
|
178
242
|
def check_filename_types(filetypes: Sequence[str]) -> Callable[[str], str]:
|
179
243
|
"""Create function that checks for being a simple file.
|
@@ -267,7 +331,7 @@ def add_file_args(parser: argparse.ArgumentParser) -> None:
|
|
267
331
|
"--logfile",
|
268
332
|
"-lf",
|
269
333
|
default="things.log",
|
270
|
-
help=
|
334
|
+
help="Where to write the log file. Relative to the out directory.",
|
271
335
|
type=check_filename_types([".log"]),
|
272
336
|
)
|
273
337
|
|
flinventory/location.py
CHANGED
@@ -384,6 +384,7 @@ class Location(dict[str, Value]):
|
|
384
384
|
"""Save location data to yaml file."""
|
385
385
|
path = pathlib.Path(os.path.join(self._directory, constant.LOCATION_FILE))
|
386
386
|
if len(self) > 0: # not empty?
|
387
|
+
os.makedirs(self._directory, exist_ok=True)
|
387
388
|
with open(path, mode="w", encoding="utf-8") as location_file:
|
388
389
|
yaml.dump(
|
389
390
|
data=self.to_jsonable_data(),
|
flinventory/sign.py
CHANGED
@@ -38,10 +38,12 @@ class Sign(defaulted_data.DefaultedDict):
|
|
38
38
|
If None, it not saved. (Can be set later to save.)
|
39
39
|
"""
|
40
40
|
self._directory = None # to avoid saving during initialisation
|
41
|
-
for source, target in [
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
for source, target in [
|
42
|
+
("fontsize_de", ("fontsize", "de")),
|
43
|
+
("fontsize_en", ("fontsize", "en")),
|
44
|
+
("fontsize_main", ("fontsize", "de")),
|
45
|
+
("fontsize_secondary", ("fontsize", "en")),
|
46
|
+
]:
|
45
47
|
try:
|
46
48
|
data[target] = data[source]
|
47
49
|
del data[source]
|
@@ -117,6 +119,7 @@ class Sign(defaulted_data.DefaultedDict):
|
|
117
119
|
self._logger.info(f"Delete {path}")
|
118
120
|
path.unlink(missing_ok=True)
|
119
121
|
else:
|
122
|
+
os.makedirs(self._directory, exist_ok=True)
|
120
123
|
with open(path, mode="w", encoding="utf-8") as sign_file:
|
121
124
|
yaml.dump(
|
122
125
|
self.to_jsonable_data(), sign_file, **constant.YAML_DUMP_OPTIONS
|
flinventory/thing.py
CHANGED
@@ -39,7 +39,7 @@ class Thing(defaulted_data.DefaultedDict):
|
|
39
39
|
default=default,
|
40
40
|
non_defaulted=tuple(),
|
41
41
|
translated=("name", "name_alt", "description"),
|
42
|
-
lists=("name_alt", "part_of"),
|
42
|
+
lists=("name_alt", "part_of", "subclass_of"),
|
43
43
|
default_order=("name", "name_alt", "part_of", "category", "url"),
|
44
44
|
options=options,
|
45
45
|
)
|
@@ -93,10 +93,10 @@ class Thing(defaulted_data.DefaultedDict):
|
|
93
93
|
Is relative path if my directory is relative.
|
94
94
|
None if no image was found.
|
95
95
|
"""
|
96
|
-
path_base = self.get(
|
96
|
+
path_base = self.get("image", constant.IMAGE_FILE_BASE)
|
97
97
|
if not os.path.isabs(path_base):
|
98
98
|
path_base = os.path.join(self.directory, path_base)
|
99
|
-
for extension in (
|
99
|
+
for extension in ("", *(f".{ext}" for ext in constant.IMAGE_FILE_TYPES)):
|
100
100
|
if os.path.isfile(path := path_base + extension):
|
101
101
|
return path
|
102
102
|
return None
|
@@ -138,6 +138,7 @@ class Thing(defaulted_data.DefaultedDict):
|
|
138
138
|
self._logger.info(f"Delete {path}")
|
139
139
|
path.unlink(missing_ok=True)
|
140
140
|
else:
|
141
|
+
os.makedirs(self._directory, exist_ok=True)
|
141
142
|
with open(path, mode="w", encoding="utf-8") as thing_file:
|
142
143
|
yaml.dump(jsonable_data, thing_file, **constant.YAML_DUMP_OPTIONS)
|
143
144
|
self._logger.info(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: flinventory
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.2
|
4
4
|
Summary: An inventory system for self-help repair shops
|
5
5
|
Author-Email: flukx <flinventory-flukx@612211124.xyz>
|
6
6
|
License: GPL3
|
@@ -43,7 +43,7 @@ There this inventory should help.
|
|
43
43
|
3. Each thing can be found by one of its names alphabetically, by several categories, by English or German name, by looking at pictures in the storage. Not a single categorization to be limited to.
|
44
44
|
|
45
45
|
## Data
|
46
|
-
For example data see the directory `sample-data` or [the data repository](https://flugit.hilsky.de/flukx/things-data)
|
46
|
+
For example data see the directory `sample-data` or [branch Rad i.O. in the data repository](https://codeberg.org/flukx/flings/src/branch/rad-i-o) or the [private data repository](https://flugit.hilsky.de/flukx/things-data) if you have access.
|
47
47
|
The data format is a specific directory structure with specific file names:
|
48
48
|
- `schema.yaml`: describes how locations in this workshop are defined. See `sample_data` for details.
|
49
49
|
- `preferences.yaml`: general options for this data. See `sample_data` for details.
|
@@ -1,7 +1,7 @@
|
|
1
|
-
flinventory-0.
|
2
|
-
flinventory-0.
|
3
|
-
flinventory-0.
|
4
|
-
flinventory-0.
|
1
|
+
flinventory-0.4.2.dist-info/METADATA,sha256=ZQjK-O4pZu_8nTuBLmDKJ0iSepizKaVkxaGIRP-cTnc,4542
|
2
|
+
flinventory-0.4.2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
|
3
|
+
flinventory-0.4.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
|
+
flinventory-0.4.2.dist-info/licenses/LICENSE,sha256=9c-AWlAEeFMmn1aODEfabsw2he-SpQeykurYw-KqSdw,34478
|
5
5
|
flinventory/__init__.py,sha256=0HTGxw9J-2KnbxQbzX6HH-Pztkp0BEjiHANhndfbtYM,270
|
6
6
|
flinventory/__main__.py,sha256=YI3dGiBr1i56JpGommQTWYjgfCmhr2JT1pQUdf0lf1w,1228
|
7
7
|
flinventory/box.py,sha256=rk7yrAjVdxcrpvmTMt3jkMVOAoQNJfqoSig9U2S3yJ0,10480
|
@@ -9,15 +9,15 @@ flinventory/constant.py,sha256=pPNCwScLon_MOKL9_jjD_sxqB7Q2WW6rZqtrBl7SyWk,10283
|
|
9
9
|
flinventory/datacleanup.py,sha256=_CImmWJhq5uKz21jJQUqKYoQfbHEYA0000RyPRt5BI0,11363
|
10
10
|
flinventory/defaulted_data.py,sha256=KAy6V2KLcifLg7zHBFnx_eFLp31sCWih1Btp6cE0bQc,21585
|
11
11
|
flinventory/generate_labels.py,sha256=wB9ibokHOcab599qZhi5uTzfy3DTo3wT4DJP9qLMzXs,6642
|
12
|
-
flinventory/inventory_io.py,sha256=
|
13
|
-
flinventory/location.py,sha256=
|
14
|
-
flinventory/sign.py,sha256=
|
12
|
+
flinventory/inventory_io.py,sha256=OWThcf9nzeZNqvNpzQiBhESQAnVXC7hUA8DEVzq-w-Y,12561
|
13
|
+
flinventory/location.py,sha256=eaZ8tF8HdzH17IBDl3_p_MsnSw5-H1GfbvkTe3dPFGk,16593
|
14
|
+
flinventory/sign.py,sha256=8G7fsne3Nsf8zZKKTUl40uWMNsWIv7FYV2KcqBR2MQI,5515
|
15
15
|
flinventory/signprinter_latex.py,sha256=Ytf1T_ADedj_Wkqe8bWyN23-TwFaKE7zFpeoYxn5NzM,19105
|
16
|
-
flinventory/thing.py,sha256=
|
16
|
+
flinventory/thing.py,sha256=86fXj3RwTbkCdVVWrbyO-VEW_e5lKJlV8PPt1Xf8ejY,5079
|
17
17
|
flinventory/thingtemplate_latex/.gitignore,sha256=DTF250Ttj8O2lxrQBwMNrQ73rWPvdwCytipfZSLFtlU,51
|
18
18
|
flinventory/thingtemplate_latex/dummyImage.jpg,sha256=bIBH5Lrxlr5qJ42nP1cpJXPRNGLOwyEuzwXFSvNwTSk,147857
|
19
19
|
flinventory/thingtemplate_latex/sign.tex,sha256=cVlOjImuS97Gf19MGW27HiP6Uw8JNnobXym6NlY-qSI,787
|
20
20
|
flinventory/thingtemplate_latex/signlist-footer.tex,sha256=LcZw5__h8SqgMmYxs5oLbXLaFTQlx_X5rtYnpxwUh9Y,14
|
21
21
|
flinventory/thingtemplate_latex/signlist-header.tex,sha256=F1J4aSPq6QO_Ved4Zn7-rytP1vDkdWTqjjtGJpe_FDg,297
|
22
22
|
flinventory/thingtemplate_latex/signs-example.tex,sha256=J6eCcPhP0Xklgn8xnSenDOvjzmc_NGmakWU4RGbeUtM,3090
|
23
|
-
flinventory-0.
|
23
|
+
flinventory-0.4.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|