moat-db-box 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,14 @@
1
+ The code in this repository, and all MoaT submodules it refers to,
2
+ is part of the MoaT project.
3
+
4
+ Unless a submodule's LICENSE.txt states otherwise, all included files are
5
+ licensed under the LGPL V3, as published by the FSF at
6
+ https://www.gnu.org/licenses/lgpl-3.0.html .
7
+
8
+ In addition to the LGPL's terms, the author(s) respectfully ask all users of
9
+ this code to contribute any bug fixes or enhancements. Also, please link back to
10
+ https://M-o-a-T.org.
11
+
12
+ Thank you.
13
+
14
+ Copyright © 2021 ff.: the MoaT contributor(s), as per the git changelog(s).
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: moat-db-box
3
+ Version: 0.0.1
4
+ Summary: MoaT support for managing containers
5
+ Author-email: Matthias Urlichs <matthias@urlichs.de>
6
+ Project-URL: homepage, https://m-o-a-t.org
7
+ Project-URL: repository, https://github.com/M-o-a-T/moat
8
+ Keywords: MoaT
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Framework :: AsyncIO
12
+ Classifier: Framework :: Trio
13
+ Classifier: Framework :: AnyIO
14
+ Classifier: Development Status :: 4 - Beta
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: moat-util~=0.60.9
19
+ Requires-Dist: moat-db~=0.2.3
20
+ Requires-Dist: moat-label~=0.2.2
21
+ Provides-Extra: event
22
+ Requires-Dist: attr; extra == "event"
23
+ Requires-Dist: outcome; extra == "event"
24
+ Provides-Extra: main
25
+ Requires-Dist: asyncclick; extra == "main"
26
+ Requires-Dist: getpass; extra == "main"
27
+ Provides-Extra: msg
28
+ Requires-Dist: msgpack; extra == "msg"
29
+ Provides-Extra: path
30
+ Requires-Dist: simpleeval; extra == "path"
31
+ Provides-Extra: queue
32
+ Requires-Dist: outcome; extra == "queue"
33
+ Provides-Extra: systemd
34
+ Requires-Dist: systemd.daemon; extra == "systemd"
35
+ Provides-Extra: yaml
36
+ Requires-Dist: ruyaml; extra == "yaml"
37
+ Provides-Extra: time
38
+ Requires-Dist: calendar; extra == "time"
39
+ Provides-Extra: dev
40
+ Requires-Dist: flake8; extra == "dev"
41
+ Requires-Dist: isort; extra == "dev"
42
+ Dynamic: license-file
@@ -0,0 +1,18 @@
1
+ # MoaT-Box
2
+
3
+ % start synopsis
4
+
5
+ This module manages boxes, i.e. things you can put other things into.
6
+
7
+ "Box" is meant to be inclusive. A room is a box. So is a cupboard, or a row
8
+ of books. Some boxes can be nested, with locations. Some boxes can be moved
9
+ (e.g. a bookcase or a drawer), some can't (the compartments within a drawer)
10
+
11
+ Boxes are typed. You can put a cupboard into a room but not vice versa.
12
+
13
+ Boxes are labelled. A box can have an arbitrary number of labels, though
14
+ most of the time it'll have one, or at most two: one with a barcode and a
15
+ box number, and one with a text that describes the content – or at least
16
+ reminds the owner of it.
17
+
18
+ % end synopsis
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ build-backend = "setuptools.build_meta"
3
+ requires = [ "setuptools", "wheel",]
4
+
5
+ [project]
6
+ classifiers = [
7
+ "Intended Audience :: Developers",
8
+ "Programming Language :: Python :: 3",
9
+ "Framework :: AsyncIO",
10
+ "Framework :: Trio",
11
+ "Framework :: AnyIO",
12
+ "Development Status :: 4 - Beta",
13
+ ]
14
+ dependencies = [
15
+ "moat-util ~= 0.60.9",
16
+ "moat-db ~= 0.2.3",
17
+ "moat-label ~= 0.2.2",
18
+ ]
19
+ version = "0.0.1"
20
+ keywords = [ "MoaT",]
21
+ requires-python = ">=3.11"
22
+ name = "moat-db-box"
23
+ description = "MoaT support for managing containers"
24
+ readme = "README.rst"
25
+ [[project.authors]]
26
+ name = "Matthias Urlichs"
27
+ email = "matthias@urlichs.de"
28
+
29
+
30
+ [project.urls]
31
+ homepage = "https://m-o-a-t.org"
32
+ repository = "https://github.com/M-o-a-T/moat"
33
+
34
+ [project.optional-dependencies]
35
+ event = [ "attr", "outcome",]
36
+ main = [ "asyncclick", "getpass",]
37
+ msg = [ "msgpack",]
38
+ path = [ "simpleeval",]
39
+ queue = [ "outcome",]
40
+ systemd = [ "systemd.daemon",]
41
+ yaml = [ "ruyaml",]
42
+ time = [ "calendar",]
43
+ dev = [ "flake8", "isort",]
44
+
45
+ [tool.setuptools]
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.setuptools.package-data]
50
+ "*" = ["*.yaml"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """
2
+ MoaT inventory module to store boxes.
3
+ """
@@ -0,0 +1,228 @@
1
+ """
2
+ Support for boxes for storing things.
3
+ """
4
+
5
+ # The main code must not load any sqlalchemy code.
6
+ # sqlalchemy might not be present.
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+
12
+ import asyncclick as click
13
+ from sqlalchemy import select
14
+
15
+ from moat.util import load_subgroup, option_ng, yprint
16
+ from moat.db import database
17
+
18
+ from .model import Box, BoxTyp
19
+
20
+
21
+ @load_subgroup(prefix="moat.box", invoke_without_command=False)
22
+ @click.pass_context
23
+ def cli(ctx):
24
+ """Boxes for storing things."""
25
+ obj = ctx.obj
26
+
27
+ sess = ctx.with_resource(database(obj.cfg.db))
28
+ ctx.with_resource(sess.begin())
29
+
30
+ obj.session = sess
31
+
32
+
33
+ @cli.group
34
+ @click.option("--name", "-n", type=str, help="Text on the label")
35
+ @click.pass_obj
36
+ def one(obj, name):
37
+ """
38
+ Manage boxes.
39
+ """
40
+ obj.name = name
41
+ pass
42
+
43
+
44
+ @one.command(name="show")
45
+ @click.pass_obj
46
+ def show_(obj):
47
+ """
48
+ Show box details / list all box types.
49
+
50
+ If a name is set, show details for this box.
51
+ Otherwise list all boxes without parents.
52
+ """
53
+ sess = obj.session
54
+
55
+ if obj.name is None:
56
+ seen = False
57
+ with sess.execute(select(Box).where(Box.container == None)) as boxes: # noqa:E711
58
+ for (box,) in boxes:
59
+ seen = True
60
+ print(box.name)
61
+ if not seen:
62
+ print("No box types defined yet. Use '--help'?", file=sys.stderr)
63
+ else:
64
+ box = sess.one(Box, name=obj.name)
65
+ yprint(box.dump())
66
+
67
+
68
+ def opts(c):
69
+ c = option_ng("--name", "-n", type=str, help="Rename this box")(c)
70
+ c = option_ng("--x", "-x", "pos_x", type=int, help="X position in parent")(c)
71
+ c = option_ng("--y", "-y", "pos_y", type=int, help="Y position in parent")(c)
72
+ c = option_ng("--z", "-z", "pos_z", type=int, help="Z position in parent")(c)
73
+ c = option_ng("--in", "-i", "container", type=str, help="Where is it?")(c)
74
+ c = option_ng("--typ", "-t", "boxtyp", type=str, help="Type of the box")(c)
75
+ return c
76
+
77
+
78
+ @one.command()
79
+ @opts
80
+ @click.pass_obj
81
+ def add(obj, **kw):
82
+ """
83
+ Add a box.
84
+ """
85
+ if obj.name is None:
86
+ raise click.UsageError("The box needs a name!")
87
+
88
+ try:
89
+ box = obj.session.one(Box, name=obj.name)
90
+ except KeyError:
91
+ pass
92
+ else:
93
+ print("This box already exists", file=sys.stderr)
94
+ sys.exit(1)
95
+ box = Box(name=obj.name)
96
+ obj.session.add(box)
97
+ box.apply(**kw)
98
+
99
+
100
+ @one.command(epilog="Use '-in -' to drop the parent box, -x/-y/-z 0 to clear a position.")
101
+ @opts
102
+ @click.pass_obj
103
+ def set(obj, **kw): # noqa:A001
104
+ """
105
+ Modify a box.
106
+ """
107
+ if obj.name is None:
108
+ raise click.UsageError("Which box? Use a name")
109
+
110
+ try:
111
+ box = obj.session.one(Box, name=obj.name)
112
+ except KeyError:
113
+ print("This box doesn't exist", file=sys.stderr)
114
+ sys.exit(1)
115
+ box.apply(**kw)
116
+
117
+
118
+ @one.command()
119
+ @click.pass_obj
120
+ def delete(obj):
121
+ """
122
+ Remove a box.
123
+ """
124
+ if obj.name is None:
125
+ raise click.UsageError("Which box? Use a name")
126
+ try:
127
+ box = obj.session.one(Box, name=obj.name)
128
+ except KeyError:
129
+ print("This box doesn't exist", file=sys.stderr)
130
+ sys.exit(1)
131
+ obj.session.delete(box)
132
+
133
+
134
+ @cli.group(name="typ")
135
+ @click.option("--name", "-n", type=str, help="Name of the box type")
136
+ @click.pass_context
137
+ def typ_(ctx, name):
138
+ """\
139
+ Manage box types.
140
+ """
141
+ obj = ctx.obj
142
+ obj.name = name
143
+
144
+
145
+ @typ_.command(name="show")
146
+ @click.pass_obj
147
+ def typ_show(obj):
148
+ """
149
+ Show box details / list all box types.
150
+
151
+ If a name is set, show details for this type.
152
+ Otherwise list all known box types.
153
+ """
154
+ sess = obj.session
155
+
156
+ if obj.name is None:
157
+ seen = False
158
+ with sess.execute(select(BoxTyp)) as boxes:
159
+ for (box,) in boxes:
160
+ seen = True
161
+ print(box.name)
162
+ if not seen:
163
+ print("No box types defined yet. Use '--help'?", file=sys.stderr)
164
+ else:
165
+ box = sess.one(BoxTyp, name=obj.name)
166
+ yprint(box.dump())
167
+
168
+
169
+ def typopts(c):
170
+ c = option_ng("--name", "-n", type=str, help="Rename this type")(c)
171
+ c = option_ng("--x", "-x", "pos_x", type=int, help="Number of X positions for content")(c)
172
+ c = option_ng("--y", "-y", "pos_y", type=int, help="Number of Y positions for content")(c)
173
+ c = option_ng("--z", "-z", "pos_z", type=int, help="Number of Z positions for content")(c)
174
+ c = option_ng("--comment", "-c", "comment", type=str, help="Description of this type")(c)
175
+ c = click.option(
176
+ "--in",
177
+ "-i",
178
+ "parent",
179
+ type=str,
180
+ multiple=True,
181
+ help="Where can you put this thing into?",
182
+ )(c)
183
+ c = click.option("--usable", "-u", is_flag=True, help="Things can be put into this box")(c)
184
+ c = click.option("--unusable", "-U", is_flag=True, help="The box holds fixed subdividers")(c)
185
+ return c
186
+
187
+
188
+ @typ_.command(name="add")
189
+ @typopts
190
+ @click.pass_obj
191
+ def typ_add(obj, parent, **kw):
192
+ """
193
+ Add a box type.
194
+ """
195
+ parent # noqa:B018
196
+
197
+ if obj.name is None:
198
+ raise click.UsageError("The box type needs a name!")
199
+
200
+ bt = BoxTyp(name=obj.name)
201
+ obj.session.add(bt)
202
+ bt.apply(**kw)
203
+
204
+
205
+ @typ_.command(name="set", epilog="Use '-i -NAME' to remove a containing box type.")
206
+ @typopts
207
+ @click.pass_obj
208
+ def typ_set(obj, **kw):
209
+ """
210
+ Modify a box type.
211
+ """
212
+ if obj.name is None:
213
+ raise click.UsageError("Which box type? Use a name")
214
+
215
+ bt = obj.session.one(BoxTyp, name=obj.name)
216
+ bt.apply(**kw)
217
+
218
+
219
+ @typ_.command(name="delete")
220
+ @click.pass_obj
221
+ def typ_delete(obj):
222
+ """
223
+ Remove a box type.
224
+ """
225
+ if obj.name is None:
226
+ raise click.UsageError("Which box type? Use a name")
227
+ bt = obj.session.one(BoxTyp, name=obj.name)
228
+ obj.session.delete(bt)
@@ -0,0 +1,152 @@
1
+ """
2
+ Database schema for collecting things
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from sqlalchemy import Column, ForeignKey, Integer, String, Table, event
8
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
9
+
10
+ from moat.db.schema import Base
11
+
12
+ from typing import TYPE_CHECKING
13
+
14
+ if TYPE_CHECKING:
15
+ from typing import Any
16
+
17
+ boxtyp_tree = Table(
18
+ "boxtyp_tree",
19
+ Base.metadata,
20
+ Column(
21
+ "parent_id",
22
+ Integer,
23
+ ForeignKey("boxtyp.id", name="fk_boxtyp_parent"),
24
+ primary_key=True,
25
+ ),
26
+ Column("child_id", Integer, ForeignKey("boxtyp.id", name="fk_boxtyp_child"), primary_key=True),
27
+ )
28
+
29
+
30
+ class BoxTyp(Base):
31
+ "One kind of box."
32
+
33
+ name: Mapped[str] = mapped_column(unique=True, type_=String(40))
34
+ comment: Mapped[str] = mapped_column(type_=String(200), nullable=True)
35
+
36
+ parents: Mapped[set[BoxTyp]] = relationship(
37
+ "BoxTyp",
38
+ secondary=boxtyp_tree,
39
+ primaryjoin="BoxTyp.id == boxtyp_tree.c.child_id",
40
+ secondaryjoin="BoxTyp.id == boxtyp_tree.c.parent_id",
41
+ back_populates="children",
42
+ )
43
+ children: Mapped[set[BoxTyp]] = relationship(
44
+ "BoxTyp",
45
+ secondary=boxtyp_tree,
46
+ primaryjoin="BoxTyp.id == boxtyp_tree.c.parent_id",
47
+ secondaryjoin="BoxTyp.id == boxtyp_tree.c.child_id",
48
+ back_populates="parents",
49
+ )
50
+ usable: Mapped[bool] = mapped_column(
51
+ nullable=False,
52
+ default=True,
53
+ server_default="1",
54
+ comment="Can you put things directly into this?",
55
+ )
56
+
57
+ # Possible locations in there
58
+ pos_x: Mapped[int] = mapped_column(nullable=True, comment="Max # of X positions")
59
+ pos_y: Mapped[int] = mapped_column(nullable=True, comment="Max # of Y positions")
60
+ pos_z: Mapped[int] = mapped_column(nullable=True, comment="Max # of Z positions")
61
+
62
+ labeltyp_id: Mapped[int] = mapped_column(
63
+ ForeignKey("labeltyp.id", name="fk_boxtyp_labeltyp"),
64
+ nullable=True,
65
+ comment="Default label",
66
+ )
67
+
68
+ boxes: Mapped[set[Box]] = relationship(back_populates="boxtyp")
69
+
70
+ def dump(self) -> dict[str, Any]:
71
+ "Standard info dump"
72
+ res = super().dump()
73
+ res.pop("pos_x", None)
74
+ res.pop("pos_y", None)
75
+ res.pop("pos_z", None)
76
+ if self.pos_x or self.pos_y or self.pos_z:
77
+ res["size"] = [self.pos_x, self.pos_y, self.pos_z]
78
+ if self.parents:
79
+ res["parent"] = [p.name for p in self.parents]
80
+ if self.children:
81
+ res["child"] = [p.name for p in self.children]
82
+ return res
83
+
84
+
85
+ class Box(Base):
86
+ "One particular box, possibly with other boxes inside."
87
+
88
+ id: Mapped[int] = mapped_column(primary_key=True)
89
+
90
+ typ_id: Mapped[int] = mapped_column(ForeignKey("boxtyp.id", name="fk_box_typ"))
91
+ name: Mapped[str] = mapped_column(unique=True, type_=String(40))
92
+ container_id: Mapped[int] = mapped_column(
93
+ ForeignKey("box.id", name="fk_box_container"),
94
+ nullable=True,
95
+ )
96
+
97
+ boxtyp: Mapped[BoxTyp] = relationship(back_populates="boxes")
98
+ container: Mapped[Box | None] = relationship(back_populates="boxes", remote_side=[id])
99
+ boxes: Mapped[set[Box]] = relationship(back_populates="container")
100
+
101
+ # location within its parent
102
+ pos_x: Mapped[int] = mapped_column(nullable=True, comment="X position in parent")
103
+ pos_y: Mapped[int] = mapped_column(nullable=True, comment="Y position in parent")
104
+ pos_z: Mapped[int] = mapped_column(nullable=True, comment="Z position in parent")
105
+
106
+ def dump(self) -> dict[str, Any]:
107
+ "Standard info dump"
108
+ res = super().dump()
109
+ res.pop("pos_x", None)
110
+ res.pop("pos_y", None)
111
+ res.pop("pos_z", None)
112
+ if self.pos_x or self.pos_y or self.pos_z:
113
+ res["pos"] = [self.pos_x, self.pos_y, self.pos_z]
114
+ if self.container is not None:
115
+ res["in"] = self.container.name
116
+ if self.labels:
117
+ res["labels"] = [f"{lab.id}:{lab.text}" for lab in self.labels]
118
+ if self.boxes:
119
+ res["content"] = sorted([box.name for box in self.boxes])
120
+ if self.boxtyp:
121
+ res["typ"] = self.boxtyp.name
122
+ return res
123
+
124
+
125
+ @event.listens_for(Box, "before_insert")
126
+ @event.listens_for(Box, "before_update")
127
+ def validate_box_coords(mapper, connection, model):
128
+ "Validator for box position"
129
+ mapper, connection # noqa:B018
130
+ par = model.container
131
+ if par is not None:
132
+ par = par.boxtyp
133
+
134
+ def chk(p):
135
+ pp = f"pos_{p}"
136
+ ppos = getattr(par, pp, None)
137
+
138
+ if (pos := getattr(model, pp)) is None:
139
+ if ppos is not None and ppos > 1:
140
+ raise ValueError(f"Box {model.name} needs a value for {p}")
141
+ elif pos <= 0:
142
+ raise ValueError(f"Box {model.name} can't set {p} <= 0")
143
+ elif par is None:
144
+ raise ValueError(f"Box {model.name} is not in a sized container")
145
+ elif ppos is None:
146
+ raise ValueError(f"Box {model.name} can't have a value for {p}")
147
+ elif ppos < pos:
148
+ raise ValueError(f"Box {model.name}: {p} is {pos}, max is {ppos}")
149
+
150
+ chk("x")
151
+ chk("y")
152
+ chk("z")
@@ -0,0 +1,67 @@
1
+ """
2
+ Database schema for collecting things
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from sqlalchemy.orm import relationship
8
+
9
+ from moat.util import NotGiven
10
+ from moat.db.schema import Base
11
+ from moat.db.util import session
12
+ from moat.label.model import Label, LabelTyp
13
+ from moat.thing.model import Thing
14
+
15
+ from .model import Box, BoxTyp
16
+
17
+ BoxTyp.labeltyp = relationship(LabelTyp, back_populates="boxtypes")
18
+ Box.labels = relationship(Label, back_populates="box", collection_class=set)
19
+ Box.things = relationship(Thing, back_populates="container", collection_class=set)
20
+
21
+
22
+ def box_apply(self, container=NotGiven, boxtyp=NotGiven, **kw):
23
+ "?"
24
+ sess = session.get()
25
+ with sess.no_autoflush:
26
+ Base.apply(self, **kw)
27
+
28
+ if container is NotGiven:
29
+ pass
30
+ elif container is None:
31
+ self.container = None
32
+ else:
33
+ self.container = sess.one(Box, name=container)
34
+
35
+ if boxtyp is NotGiven:
36
+ if self.boxtyp is None:
37
+ raise ValueError("New boxes need a type")
38
+ elif boxtyp is None:
39
+ raise ValueError("Boxes need a type")
40
+ else:
41
+ if self.boxtyp is None:
42
+ self.boxtyp = sess.one(BoxTyp, name=boxtyp)
43
+ elif self.boxtyp.name != boxtyp:
44
+ raise ValueError("Box types cannot be changed")
45
+
46
+
47
+ Box.apply = box_apply
48
+
49
+
50
+ def boxtyp_apply(self, usable, unusable, parent=(), **kw):
51
+ "?"
52
+ Base.apply(self, **kw)
53
+ if usable:
54
+ if unusable:
55
+ raise ValueError("Can't both use and not use this type")
56
+ self.usable = True
57
+ elif unusable:
58
+ self.usable = False
59
+
60
+ for p in parent:
61
+ if p[0] != "-":
62
+ self.parents.add(session.get().one(BoxTyp, name=p))
63
+ else:
64
+ self.parents.remove(session.get().one(BoxTyp, name=p[1:]))
65
+
66
+
67
+ BoxTyp.apply = boxtyp_apply
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: moat-db-box
3
+ Version: 0.0.1
4
+ Summary: MoaT support for managing containers
5
+ Author-email: Matthias Urlichs <matthias@urlichs.de>
6
+ Project-URL: homepage, https://m-o-a-t.org
7
+ Project-URL: repository, https://github.com/M-o-a-T/moat
8
+ Keywords: MoaT
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Framework :: AsyncIO
12
+ Classifier: Framework :: Trio
13
+ Classifier: Framework :: AnyIO
14
+ Classifier: Development Status :: 4 - Beta
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: moat-util~=0.60.9
19
+ Requires-Dist: moat-db~=0.2.3
20
+ Requires-Dist: moat-label~=0.2.2
21
+ Provides-Extra: event
22
+ Requires-Dist: attr; extra == "event"
23
+ Requires-Dist: outcome; extra == "event"
24
+ Provides-Extra: main
25
+ Requires-Dist: asyncclick; extra == "main"
26
+ Requires-Dist: getpass; extra == "main"
27
+ Provides-Extra: msg
28
+ Requires-Dist: msgpack; extra == "msg"
29
+ Provides-Extra: path
30
+ Requires-Dist: simpleeval; extra == "path"
31
+ Provides-Extra: queue
32
+ Requires-Dist: outcome; extra == "queue"
33
+ Provides-Extra: systemd
34
+ Requires-Dist: systemd.daemon; extra == "systemd"
35
+ Provides-Extra: yaml
36
+ Requires-Dist: ruyaml; extra == "yaml"
37
+ Provides-Extra: time
38
+ Requires-Dist: calendar; extra == "time"
39
+ Provides-Extra: dev
40
+ Requires-Dist: flake8; extra == "dev"
41
+ Requires-Dist: isort; extra == "dev"
42
+ Dynamic: license-file
@@ -0,0 +1,12 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ src/moat/db/box/__init__.py
5
+ src/moat/db/box/_main.py
6
+ src/moat/db/box/model.py
7
+ src/moat/db/box/model_.py
8
+ src/moat_db_box.egg-info/PKG-INFO
9
+ src/moat_db_box.egg-info/SOURCES.txt
10
+ src/moat_db_box.egg-info/dependency_links.txt
11
+ src/moat_db_box.egg-info/requires.txt
12
+ src/moat_db_box.egg-info/top_level.txt
@@ -0,0 +1,33 @@
1
+ moat-db~=0.2.3
2
+ moat-label~=0.2.2
3
+ moat-util~=0.60.9
4
+
5
+ [dev]
6
+ flake8
7
+ isort
8
+
9
+ [event]
10
+ attr
11
+ outcome
12
+
13
+ [main]
14
+ asyncclick
15
+ getpass
16
+
17
+ [msg]
18
+ msgpack
19
+
20
+ [path]
21
+ simpleeval
22
+
23
+ [queue]
24
+ outcome
25
+
26
+ [systemd]
27
+ systemd.daemon
28
+
29
+ [time]
30
+ calendar
31
+
32
+ [yaml]
33
+ ruyaml