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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ stele1-datatypes>=0.0.1