stele1-filesystem 0.0.1__tar.gz
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.
- stele1_filesystem-0.0.1/LICENSE +8 -0
- stele1_filesystem-0.0.1/PKG-INFO +62 -0
- stele1_filesystem-0.0.1/README.md +44 -0
- stele1_filesystem-0.0.1/pyproject.toml +29 -0
- stele1_filesystem-0.0.1/setup.cfg +4 -0
- stele1_filesystem-0.0.1/stele1/filesystem.py +248 -0
- stele1_filesystem-0.0.1/stele1/filesystemcli.py +177 -0
- stele1_filesystem-0.0.1/stele1_filesystem.egg-info/PKG-INFO +62 -0
- stele1_filesystem-0.0.1/stele1_filesystem.egg-info/SOURCES.txt +10 -0
- stele1_filesystem-0.0.1/stele1_filesystem.egg-info/dependency_links.txt +1 -0
- stele1_filesystem-0.0.1/stele1_filesystem.egg-info/requires.txt +1 -0
- stele1_filesystem-0.0.1/stele1_filesystem.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright (c) Stele Climbing contributors
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
8
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stele1-filesystem
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Filesystem interface for stele1 catalogs
|
|
5
|
+
Author-email: Daniel M <contact@steleclimbing.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://stele1.steleclimbing.org
|
|
8
|
+
Project-URL: Repository, https://codeberg.org/stele-climbing/stele1
|
|
9
|
+
Project-URL: Issues, https://codeberg.org/stele-climbing/stele1/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: stele1-datatypes>=0.0.1
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# stele1-filesystem
|
|
20
|
+
|
|
21
|
+
Filesystem interface for [stele1](https://stele1.steleclimbing.org) catalogs.
|
|
22
|
+
|
|
23
|
+
Reads and writes catalogs on disk. Each catalog is a directory; records are
|
|
24
|
+
individual JSON files organized by datatype.
|
|
25
|
+
|
|
26
|
+
Exposes primitives: get, set, list, remove.
|
|
27
|
+
Higher-level operations (queries, traversals, indexing) are left to consumers.
|
|
28
|
+
|
|
29
|
+
## Use
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from stele1.filesystem import Catalog
|
|
33
|
+
|
|
34
|
+
catalog = Catalog('/path/to/catalog.stele1.d')
|
|
35
|
+
|
|
36
|
+
if not catalog.exists():
|
|
37
|
+
catalog.create()
|
|
38
|
+
|
|
39
|
+
for uuid in catalog.climb_uuids():
|
|
40
|
+
climb = catalog.get_climb_by_uuid(uuid)
|
|
41
|
+
climb.get_name().to_data() # 'The Mandala'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each datatype (`Climb`, `Area`, `Photo`, `Parking`, `Approach`) has matching
|
|
45
|
+
`{type}_uuids()`, `get_{type}_by_uuid(uuid)`, `set_{type}({type})`, and
|
|
46
|
+
`remove_{type}_by_uuid(uuid)` methods.
|
|
47
|
+
|
|
48
|
+
Photos additionally support `get_photo_image_by_uuid` and
|
|
49
|
+
`set_photo_image_by_uuid` for the associated image file.
|
|
50
|
+
|
|
51
|
+
See [stele1-datatypes](https://pypi.org/project/stele1-datatypes/) for the
|
|
52
|
+
datatypes returned by the getters.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
pip install stele1-filesystem
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# stele1-filesystem
|
|
2
|
+
|
|
3
|
+
Filesystem interface for [stele1](https://stele1.steleclimbing.org) catalogs.
|
|
4
|
+
|
|
5
|
+
Reads and writes catalogs on disk. Each catalog is a directory; records are
|
|
6
|
+
individual JSON files organized by datatype.
|
|
7
|
+
|
|
8
|
+
Exposes primitives: get, set, list, remove.
|
|
9
|
+
Higher-level operations (queries, traversals, indexing) are left to consumers.
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from stele1.filesystem import Catalog
|
|
15
|
+
|
|
16
|
+
catalog = Catalog('/path/to/catalog.stele1.d')
|
|
17
|
+
|
|
18
|
+
if not catalog.exists():
|
|
19
|
+
catalog.create()
|
|
20
|
+
|
|
21
|
+
for uuid in catalog.climb_uuids():
|
|
22
|
+
climb = catalog.get_climb_by_uuid(uuid)
|
|
23
|
+
climb.get_name().to_data() # 'The Mandala'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each datatype (`Climb`, `Area`, `Photo`, `Parking`, `Approach`) has matching
|
|
27
|
+
`{type}_uuids()`, `get_{type}_by_uuid(uuid)`, `set_{type}({type})`, and
|
|
28
|
+
`remove_{type}_by_uuid(uuid)` methods.
|
|
29
|
+
|
|
30
|
+
Photos additionally support `get_photo_image_by_uuid` and
|
|
31
|
+
`set_photo_image_by_uuid` for the associated image file.
|
|
32
|
+
|
|
33
|
+
See [stele1-datatypes](https://pypi.org/project/stele1-datatypes/) for the
|
|
34
|
+
datatypes returned by the getters.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
pip install stele1-filesystem
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stele1-filesystem"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Filesystem interface for stele1 catalogs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [{ name = "Daniel M", email = "contact@steleclimbing.org" }]
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"stele1-datatypes>=0.0.1",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://stele1.steleclimbing.org"
|
|
24
|
+
Repository = "https://codeberg.org/stele-climbing/stele1"
|
|
25
|
+
Issues = "https://codeberg.org/stele-climbing/stele1/issues"
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.packages.find]
|
|
28
|
+
namespaces = true
|
|
29
|
+
where = ["."]
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import os
|
|
3
|
+
import os.path
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import stele1.climb as climb
|
|
7
|
+
import stele1.approach as approach
|
|
8
|
+
import stele1.photo as photo
|
|
9
|
+
import stele1.parking as parking
|
|
10
|
+
import stele1.area as area
|
|
11
|
+
import stele1.metadata as metadata
|
|
12
|
+
|
|
13
|
+
class Catalog:
|
|
14
|
+
|
|
15
|
+
def __init__(self, path):
|
|
16
|
+
self._path_original = path;
|
|
17
|
+
self._path = os.path.abspath(path);
|
|
18
|
+
def _subpath(self, *segments):
|
|
19
|
+
return os.path.join(self._path, *segments)
|
|
20
|
+
def _ensure_dir(self, path):
|
|
21
|
+
if not os.path.isdir(path):
|
|
22
|
+
os.mkdir(path)
|
|
23
|
+
return None
|
|
24
|
+
def _ensure_parent_dir(self, path):
|
|
25
|
+
return self._ensure_dir(os.path.dirname(path))
|
|
26
|
+
|
|
27
|
+
def exists(self):
|
|
28
|
+
isfile = os.path.isfile
|
|
29
|
+
metafile = self._subpath('stele1.json')
|
|
30
|
+
humanfile = self._subpath('stele1.txt')
|
|
31
|
+
return isfile(metafile) and isfile(humanfile)
|
|
32
|
+
|
|
33
|
+
def create(self):
|
|
34
|
+
parent = os.path.dirname(self._path)
|
|
35
|
+
if os.path.isdir(self._path):
|
|
36
|
+
raise Exception(f'directory exists {self._path}')
|
|
37
|
+
if not os.path.isdir(parent):
|
|
38
|
+
raise Exception(f'parent directory missing {parent} (for {self._path})')
|
|
39
|
+
os.mkdir(self._path)
|
|
40
|
+
os.mkdir(self._subpath('climbs'))
|
|
41
|
+
os.mkdir(self._subpath('approaches'))
|
|
42
|
+
os.mkdir(self._subpath('photos'))
|
|
43
|
+
os.mkdir(self._subpath('parkings'))
|
|
44
|
+
os.mkdir(self._subpath('areas'))
|
|
45
|
+
with open(os.path.join(self._path, 'stele1.txt'), 'w') as f:
|
|
46
|
+
f.write('stele1' + os.linesep)
|
|
47
|
+
self.set_metadata(metadata.Metadata())
|
|
48
|
+
|
|
49
|
+
def get_metadata(self):
|
|
50
|
+
metapath = self._subpath('stele1.json')
|
|
51
|
+
if os.path.isfile(metapath):
|
|
52
|
+
with open(metapath, 'r') as f:
|
|
53
|
+
try:
|
|
54
|
+
return metadata.Metadata.from_data(json.load(f))
|
|
55
|
+
except json.decoder.JSONDecodeError as e:
|
|
56
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
57
|
+
else:
|
|
58
|
+
return None
|
|
59
|
+
def set_metadata(self, metadata):
|
|
60
|
+
metapath = self._subpath('stele1.json')
|
|
61
|
+
if os.path.isdir(os.path.dirname(metapath)):
|
|
62
|
+
with open(metapath, 'w') as f:
|
|
63
|
+
json.dump(metadata.to_data(), f, indent=4, ensure_ascii=False)
|
|
64
|
+
f.write(os.linesep)
|
|
65
|
+
else:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def climb_uuids(self):
|
|
69
|
+
dir = self._subpath('climbs')
|
|
70
|
+
if os.path.isdir(dir):
|
|
71
|
+
for f in os.listdir(dir):
|
|
72
|
+
if f.endswith('.json'):
|
|
73
|
+
yield climb.Uuid.from_data(f.replace('.json', ''))
|
|
74
|
+
def get_climb_by_uuid(self, uuid):
|
|
75
|
+
pat = self._subpath('climbs', uuid.to_data() + '.json')
|
|
76
|
+
if os.path.isfile(pat):
|
|
77
|
+
with open(pat, 'r') as f:
|
|
78
|
+
try:
|
|
79
|
+
e = climb.Climb.from_data(json.load(f))
|
|
80
|
+
if uuid.to_data() != e.get_uuid().to_data():
|
|
81
|
+
raise Exception(f'path {pat} does not match the uuid {e.get_uuid().to_data()}')
|
|
82
|
+
return e
|
|
83
|
+
except json.decoder.JSONDecodeError as e:
|
|
84
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
85
|
+
def set_climb(self, climb):
|
|
86
|
+
uuid = climb.get_uuid()
|
|
87
|
+
if not uuid:
|
|
88
|
+
raise Exception('can not use climb without a uuid')
|
|
89
|
+
pat = self._subpath('climbs', climb.get_uuid().to_data() + '.json')
|
|
90
|
+
self._ensure_parent_dir(pat)
|
|
91
|
+
with open(pat, 'w') as f:
|
|
92
|
+
json.dump(climb.to_data(), f, indent=4, ensure_ascii=False)
|
|
93
|
+
f.write(os.linesep)
|
|
94
|
+
def remove_climb_by_uuid(self, uuid):
|
|
95
|
+
pat = self._subpath('climbs', uuid.to_data() + '.json')
|
|
96
|
+
if os.path.isfile(pat):
|
|
97
|
+
os.unlink(pat)
|
|
98
|
+
|
|
99
|
+
def approach_uuids(self):
|
|
100
|
+
dir = self._subpath('approaches')
|
|
101
|
+
if os.path.isdir(dir):
|
|
102
|
+
for f in os.listdir(dir):
|
|
103
|
+
if f.endswith('.json'):
|
|
104
|
+
yield approach.Uuid.from_data(f.replace('.json', ''))
|
|
105
|
+
def get_approach_by_uuid(self, uuid):
|
|
106
|
+
pat = self._subpath('approaches', uuid.to_data() + '.json')
|
|
107
|
+
if os.path.isfile(pat):
|
|
108
|
+
with open(pat, 'r') as f:
|
|
109
|
+
try:
|
|
110
|
+
e = approach.Approach.from_data(json.load(f))
|
|
111
|
+
if uuid.to_data() != e.get_uuid().to_data():
|
|
112
|
+
raise Exception(f'path {pat} does not match the uuid {e.get_uuid().to_data()}')
|
|
113
|
+
return e
|
|
114
|
+
except json.decoder.JSONDecodeError as e:
|
|
115
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
116
|
+
def set_approach(self, approach):
|
|
117
|
+
uuid = approach.get_uuid()
|
|
118
|
+
if not uuid:
|
|
119
|
+
raise Exception('can not use approach without a uuid')
|
|
120
|
+
pat = self._subpath('approaches', approach.get_uuid().to_data() + '.json')
|
|
121
|
+
self._ensure_parent_dir(pat)
|
|
122
|
+
with open(pat, 'w') as f:
|
|
123
|
+
json.dump(approach.to_data(), f, indent=4, ensure_ascii=False)
|
|
124
|
+
f.write(os.linesep)
|
|
125
|
+
def remove_approach_by_uuid(self, uuid):
|
|
126
|
+
pat = self._subpath('approaches', uuid.to_data() + '.json')
|
|
127
|
+
if os.path.isfile(pat):
|
|
128
|
+
os.unlink(pat)
|
|
129
|
+
|
|
130
|
+
def photo_uuids(self):
|
|
131
|
+
dir = self._subpath('photos')
|
|
132
|
+
if os.path.isdir(dir):
|
|
133
|
+
for f in os.listdir(dir):
|
|
134
|
+
if f.endswith('.json'):
|
|
135
|
+
yield photo.Uuid.from_data(f.replace('.json', ''))
|
|
136
|
+
def get_photo_by_uuid(self, uuid):
|
|
137
|
+
pat = self._subpath('photos', uuid.to_data() + '.json')
|
|
138
|
+
if os.path.isfile(pat):
|
|
139
|
+
with open(pat, 'r') as f:
|
|
140
|
+
try:
|
|
141
|
+
e = photo.Photo.from_data(json.load(f))
|
|
142
|
+
if uuid.to_data() != e.get_uuid().to_data():
|
|
143
|
+
raise Exception(f'path {pat} does not match the uuid {e.get_uuid().to_data()}')
|
|
144
|
+
return e
|
|
145
|
+
except json.decoder.JSONDecodeError as e:
|
|
146
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
147
|
+
def set_photo(self, photo):
|
|
148
|
+
uuid = photo.get_uuid()
|
|
149
|
+
if not uuid:
|
|
150
|
+
raise Exception('can not use photo without a uuid')
|
|
151
|
+
pat = self._subpath('photos', photo.get_uuid().to_data() + '.json')
|
|
152
|
+
self._ensure_parent_dir(pat)
|
|
153
|
+
with open(pat, 'w') as f:
|
|
154
|
+
json.dump(photo.to_data(), f, indent=4, ensure_ascii=False)
|
|
155
|
+
f.write(os.linesep)
|
|
156
|
+
def remove_photo_by_uuid(self, uuid):
|
|
157
|
+
pat = self._subpath('photos', uuid.to_data() + '.json')
|
|
158
|
+
imgpat = self._get_photo_image_path(uuid)
|
|
159
|
+
if os.path.isfile(pat):
|
|
160
|
+
os.unlink(pat)
|
|
161
|
+
if os.path.isfile(imgpat):
|
|
162
|
+
os.unlink(imgpat)
|
|
163
|
+
def _get_photo_image_path(self, uuid):
|
|
164
|
+
dir = self._subpath('photos')
|
|
165
|
+
prefix = uuid.to_data() + '_image'
|
|
166
|
+
if os.path.isdir(dir):
|
|
167
|
+
for f in os.listdir(dir):
|
|
168
|
+
if f.startswith(prefix):
|
|
169
|
+
return os.path.join(dir, f)
|
|
170
|
+
def set_photo_image_by_uuid(self, uuid, path):
|
|
171
|
+
if not os.path.isfile(path):
|
|
172
|
+
raise Exception('no file at path ' + path)
|
|
173
|
+
dir = self._subpath('photos')
|
|
174
|
+
existing = self._get_photo_image_path(uuid)
|
|
175
|
+
ext = os.path.basename(path).split('.')[-1]
|
|
176
|
+
suffix = '.' + ext if ext else ''
|
|
177
|
+
if existing:
|
|
178
|
+
# TODO: warn the user because this is a terrible idea
|
|
179
|
+
os.unlink(existing)
|
|
180
|
+
shutil.copyfile(path, os.path.join(dir, f'{uuid.to_data()}_image{suffix}'))
|
|
181
|
+
def get_photo_image_by_uuid(self, uuid):
|
|
182
|
+
return self._get_photo_image_path(uuid)
|
|
183
|
+
|
|
184
|
+
def parking_uuids(self):
|
|
185
|
+
dir = self._subpath('parkings')
|
|
186
|
+
if os.path.isdir(dir):
|
|
187
|
+
for f in os.listdir(dir):
|
|
188
|
+
if f.endswith('.json'):
|
|
189
|
+
yield parking.Uuid.from_data(f.replace('.json', ''))
|
|
190
|
+
def get_parking_by_uuid(self, uuid):
|
|
191
|
+
pat = self._subpath('parkings', uuid.to_data() + '.json')
|
|
192
|
+
if os.path.isfile(pat):
|
|
193
|
+
with open(pat, 'r') as f:
|
|
194
|
+
try:
|
|
195
|
+
e = parking.Parking.from_data(json.load(f))
|
|
196
|
+
if uuid.to_data() != e.get_uuid().to_data():
|
|
197
|
+
raise Exception(f'path {pat} does not match the uuid {e.get_uuid().to_data()}')
|
|
198
|
+
return e
|
|
199
|
+
except json.decoder.JSONDecodeError as e:
|
|
200
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
201
|
+
def set_parking(self, parking):
|
|
202
|
+
uuid = parking.get_uuid()
|
|
203
|
+
if not uuid:
|
|
204
|
+
raise Exception('can not use parking without a uuid')
|
|
205
|
+
pat = self._subpath('parkings', parking.get_uuid().to_data() + '.json')
|
|
206
|
+
self._ensure_parent_dir(pat)
|
|
207
|
+
with open(pat, 'w') as f:
|
|
208
|
+
json.dump(parking.to_data(), f, indent=4, ensure_ascii=False)
|
|
209
|
+
f.write(os.linesep)
|
|
210
|
+
def remove_parking_by_uuid(self, uuid):
|
|
211
|
+
pat = self._subpath('parkings', uuid.to_data() + '.json')
|
|
212
|
+
if os.path.isfile(pat):
|
|
213
|
+
os.unlink(pat)
|
|
214
|
+
|
|
215
|
+
def area_uuids(self):
|
|
216
|
+
dir = self._subpath('areas')
|
|
217
|
+
if os.path.isdir(dir):
|
|
218
|
+
for f in os.listdir(dir):
|
|
219
|
+
if f.endswith('.json'):
|
|
220
|
+
yield area.Uuid.from_data(f.replace('.json', ''))
|
|
221
|
+
def get_area_by_uuid(self, uuid):
|
|
222
|
+
pat = self._subpath('areas', uuid.to_data() + '.json')
|
|
223
|
+
if os.path.isfile(pat):
|
|
224
|
+
with open(pat, 'r') as f:
|
|
225
|
+
try:
|
|
226
|
+
e = area.Area.from_data(json.load(f))
|
|
227
|
+
if uuid.to_data() != e.get_uuid().to_data():
|
|
228
|
+
raise Exception(f'path {pat} does not match the uuid {e.get_uuid().to_data()}')
|
|
229
|
+
return e
|
|
230
|
+
except json.decoder.JSONDecodeError as e:
|
|
231
|
+
raise Exception(f'bad json at {pat}: {e.args[0]}')
|
|
232
|
+
def set_area(self, area):
|
|
233
|
+
uuid = area.get_uuid()
|
|
234
|
+
if not uuid:
|
|
235
|
+
raise Exception('can not use area without a uuid')
|
|
236
|
+
pat = self._subpath('areas', area.get_uuid().to_data() + '.json')
|
|
237
|
+
self._ensure_parent_dir(pat)
|
|
238
|
+
with open(pat, 'w') as f:
|
|
239
|
+
json.dump(area.to_data(), f, indent=4, ensure_ascii=False)
|
|
240
|
+
f.write(os.linesep)
|
|
241
|
+
def remove_area_by_uuid(self, uuid):
|
|
242
|
+
pat = self._subpath('areas', uuid.to_data() + '.json')
|
|
243
|
+
if os.path.isfile(pat):
|
|
244
|
+
os.unlink(pat)
|
|
245
|
+
|
|
246
|
+
if __name__ == '__main__':
|
|
247
|
+
import stele1.filesystemcli
|
|
248
|
+
stele1.filesystemcli.main()
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import os.path
|
|
3
|
+
import sys
|
|
4
|
+
import uuid
|
|
5
|
+
import stele1.filesystem
|
|
6
|
+
|
|
7
|
+
import stele1.climb as climb
|
|
8
|
+
import stele1.approach as approach
|
|
9
|
+
import stele1.photo as photo
|
|
10
|
+
import stele1.parking as parking
|
|
11
|
+
import stele1.area as area
|
|
12
|
+
import stele1.metadata as metadata
|
|
13
|
+
|
|
14
|
+
def list_climbs(args):
|
|
15
|
+
print('listing climbs')
|
|
16
|
+
for id in catalog.climb_uuids():
|
|
17
|
+
print(id.to_data())
|
|
18
|
+
def add_climb(args):
|
|
19
|
+
e = stele1.climb.Climb()
|
|
20
|
+
id = stele1.climb.Uuid.from_data(str(uuid.uuid4()))
|
|
21
|
+
e.setUuid(id)
|
|
22
|
+
catalog.set_climb(d)
|
|
23
|
+
print(id.to_data())
|
|
24
|
+
def get_climb_by_uuid(args):
|
|
25
|
+
print('getting climb')
|
|
26
|
+
def set_climb(args):
|
|
27
|
+
print('setting climb')
|
|
28
|
+
def remove_climb_by_uuid(args):
|
|
29
|
+
print('removing climb')
|
|
30
|
+
|
|
31
|
+
def list_approaches(args):
|
|
32
|
+
print('listing approaches')
|
|
33
|
+
for id in catalog.approach_uuids():
|
|
34
|
+
print(id.to_data())
|
|
35
|
+
def add_approach(args):
|
|
36
|
+
e = stele1.approach.Approach()
|
|
37
|
+
id = stele1.approach.Uuid.from_data(str(uuid.uuid4()))
|
|
38
|
+
e.setUuid(id)
|
|
39
|
+
catalog.set_approach(d)
|
|
40
|
+
print(id.to_data())
|
|
41
|
+
def get_approach_by_uuid(args):
|
|
42
|
+
print('getting approach')
|
|
43
|
+
def set_approach(args):
|
|
44
|
+
print('setting approach')
|
|
45
|
+
def remove_approach_by_uuid(args):
|
|
46
|
+
print('removing approach')
|
|
47
|
+
|
|
48
|
+
def list_photos(args):
|
|
49
|
+
print('listing photos')
|
|
50
|
+
for id in catalog.photo_uuids():
|
|
51
|
+
print(id.to_data())
|
|
52
|
+
def add_photo(args):
|
|
53
|
+
e = stele1.photo.Photo()
|
|
54
|
+
id = stele1.photo.Uuid.from_data(str(uuid.uuid4()))
|
|
55
|
+
e.setUuid(id)
|
|
56
|
+
catalog.set_photo(d)
|
|
57
|
+
print(id.to_data())
|
|
58
|
+
def get_photo_by_uuid(args):
|
|
59
|
+
print('getting photo')
|
|
60
|
+
def set_photo(args):
|
|
61
|
+
print('setting photo')
|
|
62
|
+
def remove_photo_by_uuid(args):
|
|
63
|
+
print('removing photo')
|
|
64
|
+
|
|
65
|
+
def list_parkings(args):
|
|
66
|
+
print('listing parkings')
|
|
67
|
+
for id in catalog.parking_uuids():
|
|
68
|
+
print(id.to_data())
|
|
69
|
+
def add_parking(args):
|
|
70
|
+
e = stele1.parking.Parking()
|
|
71
|
+
id = stele1.parking.Uuid.from_data(str(uuid.uuid4()))
|
|
72
|
+
e.setUuid(id)
|
|
73
|
+
catalog.set_parking(d)
|
|
74
|
+
print(id.to_data())
|
|
75
|
+
def get_parking_by_uuid(args):
|
|
76
|
+
print('getting parking')
|
|
77
|
+
def set_parking(args):
|
|
78
|
+
print('setting parking')
|
|
79
|
+
def remove_parking_by_uuid(args):
|
|
80
|
+
print('removing parking')
|
|
81
|
+
|
|
82
|
+
def list_areas(args):
|
|
83
|
+
print('listing areas')
|
|
84
|
+
for id in catalog.area_uuids():
|
|
85
|
+
print(id.to_data())
|
|
86
|
+
def add_area(args):
|
|
87
|
+
e = stele1.area.Area()
|
|
88
|
+
id = stele1.area.Uuid.from_data(str(uuid.uuid4()))
|
|
89
|
+
e.setUuid(id)
|
|
90
|
+
catalog.set_area(d)
|
|
91
|
+
print(id.to_data())
|
|
92
|
+
def get_area_by_uuid(args):
|
|
93
|
+
print('getting area')
|
|
94
|
+
def set_area(args):
|
|
95
|
+
print('setting area')
|
|
96
|
+
def remove_area_by_uuid(args):
|
|
97
|
+
print('removing area')
|
|
98
|
+
|
|
99
|
+
def main():
|
|
100
|
+
subcmd = sys.argv[1]
|
|
101
|
+
subcmd_args = sys.argv[2:]
|
|
102
|
+
catalog = stele1.filesystem.Catalog(os.environ['STELE1_CATALOG']) if 'STELE1_CATALOG' in os.environ else None
|
|
103
|
+
print(catalog)
|
|
104
|
+
if subcmd == 'help':
|
|
105
|
+
print(f'usage: {os.path.basename(sys.argv[0])} SUBCOMMAND ...')
|
|
106
|
+
print('subcommands:')
|
|
107
|
+
print(' create, exists, get-metadata, set-metadata')
|
|
108
|
+
print(' list-climbs add-climb ' +
|
|
109
|
+
'get-climb set-climb remove-climb')
|
|
110
|
+
print(' list-approaches add-approach ' +
|
|
111
|
+
'get-approach set-approach remove-approach')
|
|
112
|
+
print(' list-photos add-photo ' +
|
|
113
|
+
'get-photo set-photo remove-photo')
|
|
114
|
+
print(' list-parkings add-parking ' +
|
|
115
|
+
'get-parking set-parking remove-parking')
|
|
116
|
+
print(' list-areas add-area ' +
|
|
117
|
+
'get-area set-area remove-area')
|
|
118
|
+
print('environment vars:')
|
|
119
|
+
print(' STELE1_CATALOG')
|
|
120
|
+
elif subcmd == 'exists':
|
|
121
|
+
print('TODO: exists')
|
|
122
|
+
elif subcmd == 'create':
|
|
123
|
+
print('TODO: create')
|
|
124
|
+
elif subcmd == 'set-metadata':
|
|
125
|
+
print('TODO: set metadata')
|
|
126
|
+
elif subcmd == 'list-climbs':
|
|
127
|
+
list_climbs(subcmd_args)
|
|
128
|
+
elif subcmd == 'add-climb':
|
|
129
|
+
add_climb(subcmd_args)
|
|
130
|
+
elif subcmd == 'get-climb':
|
|
131
|
+
get_climb(subcmd_args)
|
|
132
|
+
elif subcmd == 'set-climb':
|
|
133
|
+
set_climb(subcmd_args)
|
|
134
|
+
elif subcmd == 'remove-climb':
|
|
135
|
+
remove_climb(subcmd_args)
|
|
136
|
+
elif subcmd == 'list-approaches':
|
|
137
|
+
list_approaches(subcmd_args)
|
|
138
|
+
elif subcmd == 'add-approach':
|
|
139
|
+
add_approach(subcmd_args)
|
|
140
|
+
elif subcmd == 'get-approach':
|
|
141
|
+
get_approach(subcmd_args)
|
|
142
|
+
elif subcmd == 'set-approach':
|
|
143
|
+
set_approach(subcmd_args)
|
|
144
|
+
elif subcmd == 'remove-approach':
|
|
145
|
+
remove_approach(subcmd_args)
|
|
146
|
+
elif subcmd == 'list-photos':
|
|
147
|
+
list_photos(subcmd_args)
|
|
148
|
+
elif subcmd == 'add-photo':
|
|
149
|
+
add_photo(subcmd_args)
|
|
150
|
+
elif subcmd == 'get-photo':
|
|
151
|
+
get_photo(subcmd_args)
|
|
152
|
+
elif subcmd == 'set-photo':
|
|
153
|
+
set_photo(subcmd_args)
|
|
154
|
+
elif subcmd == 'remove-photo':
|
|
155
|
+
remove_photo(subcmd_args)
|
|
156
|
+
elif subcmd == 'list-parkings':
|
|
157
|
+
list_parkings(subcmd_args)
|
|
158
|
+
elif subcmd == 'add-parking':
|
|
159
|
+
add_parking(subcmd_args)
|
|
160
|
+
elif subcmd == 'get-parking':
|
|
161
|
+
get_parking(subcmd_args)
|
|
162
|
+
elif subcmd == 'set-parking':
|
|
163
|
+
set_parking(subcmd_args)
|
|
164
|
+
elif subcmd == 'remove-parking':
|
|
165
|
+
remove_parking(subcmd_args)
|
|
166
|
+
elif subcmd == 'list-areas':
|
|
167
|
+
list_areas(subcmd_args)
|
|
168
|
+
elif subcmd == 'add-area':
|
|
169
|
+
add_area(subcmd_args)
|
|
170
|
+
elif subcmd == 'get-area':
|
|
171
|
+
get_area(subcmd_args)
|
|
172
|
+
elif subcmd == 'set-area':
|
|
173
|
+
set_area(subcmd_args)
|
|
174
|
+
elif subcmd == 'remove-area':
|
|
175
|
+
remove_area(subcmd_args)
|
|
176
|
+
else:
|
|
177
|
+
raise Exception('unknown subcommand ' + subcmd)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stele1-filesystem
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Filesystem interface for stele1 catalogs
|
|
5
|
+
Author-email: Daniel M <contact@steleclimbing.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://stele1.steleclimbing.org
|
|
8
|
+
Project-URL: Repository, https://codeberg.org/stele-climbing/stele1
|
|
9
|
+
Project-URL: Issues, https://codeberg.org/stele-climbing/stele1/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Requires-Dist: stele1-datatypes>=0.0.1
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# stele1-filesystem
|
|
20
|
+
|
|
21
|
+
Filesystem interface for [stele1](https://stele1.steleclimbing.org) catalogs.
|
|
22
|
+
|
|
23
|
+
Reads and writes catalogs on disk. Each catalog is a directory; records are
|
|
24
|
+
individual JSON files organized by datatype.
|
|
25
|
+
|
|
26
|
+
Exposes primitives: get, set, list, remove.
|
|
27
|
+
Higher-level operations (queries, traversals, indexing) are left to consumers.
|
|
28
|
+
|
|
29
|
+
## Use
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from stele1.filesystem import Catalog
|
|
33
|
+
|
|
34
|
+
catalog = Catalog('/path/to/catalog.stele1.d')
|
|
35
|
+
|
|
36
|
+
if not catalog.exists():
|
|
37
|
+
catalog.create()
|
|
38
|
+
|
|
39
|
+
for uuid in catalog.climb_uuids():
|
|
40
|
+
climb = catalog.get_climb_by_uuid(uuid)
|
|
41
|
+
climb.get_name().to_data() # 'The Mandala'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each datatype (`Climb`, `Area`, `Photo`, `Parking`, `Approach`) has matching
|
|
45
|
+
`{type}_uuids()`, `get_{type}_by_uuid(uuid)`, `set_{type}({type})`, and
|
|
46
|
+
`remove_{type}_by_uuid(uuid)` methods.
|
|
47
|
+
|
|
48
|
+
Photos additionally support `get_photo_image_by_uuid` and
|
|
49
|
+
`set_photo_image_by_uuid` for the associated image file.
|
|
50
|
+
|
|
51
|
+
See [stele1-datatypes](https://pypi.org/project/stele1-datatypes/) for the
|
|
52
|
+
datatypes returned by the getters.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
pip install stele1-filesystem
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
stele1/filesystem.py
|
|
5
|
+
stele1/filesystemcli.py
|
|
6
|
+
stele1_filesystem.egg-info/PKG-INFO
|
|
7
|
+
stele1_filesystem.egg-info/SOURCES.txt
|
|
8
|
+
stele1_filesystem.egg-info/dependency_links.txt
|
|
9
|
+
stele1_filesystem.egg-info/requires.txt
|
|
10
|
+
stele1_filesystem.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
stele1-datatypes>=0.0.1
|