sera-2 1.1.0__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.
- sera/__init__.py +0 -0
- sera/libs/__init__.py +0 -0
- sera/libs/api_helper.py +66 -0
- sera/libs/base_orm.py +109 -0
- sera/libs/base_service.py +78 -0
- sera/make/__init__.py +0 -0
- sera/make/__main__.py +38 -0
- sera/make/make_app.py +142 -0
- sera/make/make_python_api.py +242 -0
- sera/make/make_python_model.py +282 -0
- sera/make/make_python_services.py +64 -0
- sera/make/make_typescript_model.py +1 -0
- sera/misc/__init__.py +16 -0
- sera/misc/_rdf.py +60 -0
- sera/misc/_utils.py +46 -0
- sera/models/__init__.py +24 -0
- sera/models/_class.py +56 -0
- sera/models/_collection.py +34 -0
- sera/models/_datatype.py +54 -0
- sera/models/_module.py +140 -0
- sera/models/_multi_lingual_string.py +38 -0
- sera/models/_parse.py +153 -0
- sera/models/_property.py +124 -0
- sera/models/_schema.py +32 -0
- sera/namespace.py +5 -0
- sera/typing.py +11 -0
- sera_2-1.1.0.dist-info/METADATA +20 -0
- sera_2-1.1.0.dist-info/RECORD +29 -0
- sera_2-1.1.0.dist-info/WHEEL +4 -0
sera/models/_module.py
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import datetime
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from enum import Enum
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Sequence
|
8
|
+
|
9
|
+
import black
|
10
|
+
import black.mode
|
11
|
+
from codegen.models import Program
|
12
|
+
from loguru import logger
|
13
|
+
|
14
|
+
|
15
|
+
class Language(str, Enum):
|
16
|
+
Python = "python"
|
17
|
+
Typescript = "typescript"
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class Package:
|
22
|
+
"""Represent an target generated module in the application"""
|
23
|
+
|
24
|
+
app: App
|
25
|
+
path: str
|
26
|
+
dir: Path
|
27
|
+
language: Language
|
28
|
+
|
29
|
+
def ensure_exists(self):
|
30
|
+
"""Ensure the module exists"""
|
31
|
+
self.dir.mkdir(parents=True, exist_ok=True)
|
32
|
+
if self.language == Language.Python:
|
33
|
+
if not (self.dir / "__init__.py").exists():
|
34
|
+
(self.dir / "__init__.py").touch()
|
35
|
+
|
36
|
+
def pkg(self, name: str) -> Package:
|
37
|
+
"""Create a package in this package"""
|
38
|
+
return Package(self.app, f"{self.path}.{name}", self.dir / name, self.language)
|
39
|
+
|
40
|
+
def module(self, name: str) -> Module:
|
41
|
+
"""Create a module in this package"""
|
42
|
+
return Module(self, name, self.language)
|
43
|
+
|
44
|
+
|
45
|
+
@dataclass
|
46
|
+
class Module:
|
47
|
+
"""Represent a module in a package"""
|
48
|
+
|
49
|
+
package: Package
|
50
|
+
name: str
|
51
|
+
language: Language
|
52
|
+
|
53
|
+
@property
|
54
|
+
def path(self) -> str:
|
55
|
+
return f"{self.package.path}.{self.name}"
|
56
|
+
|
57
|
+
def write(self, program: Program):
|
58
|
+
"""Write the module to disk"""
|
59
|
+
self.package.ensure_exists()
|
60
|
+
if self.language == Language.Python:
|
61
|
+
try:
|
62
|
+
code = black.format_str(
|
63
|
+
program.root.to_python(),
|
64
|
+
mode=black.Mode(
|
65
|
+
target_versions={black.mode.TargetVersion.PY312},
|
66
|
+
line_length=120,
|
67
|
+
),
|
68
|
+
)
|
69
|
+
except:
|
70
|
+
logger.error("Error writing module {}", self.path)
|
71
|
+
print(">>> Program")
|
72
|
+
print(program.root.to_python())
|
73
|
+
print("<<<")
|
74
|
+
raise
|
75
|
+
else:
|
76
|
+
assert self.language == Language.Typescript
|
77
|
+
raise NotImplementedError()
|
78
|
+
|
79
|
+
copyright_statement = f"# Generated automatically at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}. All rights reserved.\n\n"
|
80
|
+
(self.package.dir / f"{self.name}.py").write_text(copyright_statement + code)
|
81
|
+
|
82
|
+
def exists(self) -> bool:
|
83
|
+
"""Check if the module exists"""
|
84
|
+
return (self.package.dir / f"{self.name}.py").exists()
|
85
|
+
|
86
|
+
|
87
|
+
@dataclass
|
88
|
+
class AppModels(Package):
|
89
|
+
data: Package
|
90
|
+
db: Package
|
91
|
+
|
92
|
+
@staticmethod
|
93
|
+
def from_pkg(pkg: Package) -> AppModels:
|
94
|
+
"""Create an AppModels from a package"""
|
95
|
+
return AppModels(
|
96
|
+
pkg.app,
|
97
|
+
path=pkg.path,
|
98
|
+
dir=pkg.dir,
|
99
|
+
language=pkg.language,
|
100
|
+
data=pkg.pkg("data"),
|
101
|
+
db=pkg.pkg("db"),
|
102
|
+
)
|
103
|
+
|
104
|
+
|
105
|
+
@dataclass
|
106
|
+
class App:
|
107
|
+
"""Represent the generated application"""
|
108
|
+
|
109
|
+
# top-level package of the application
|
110
|
+
root: Package
|
111
|
+
|
112
|
+
# application configuration
|
113
|
+
config: Module
|
114
|
+
|
115
|
+
# models of the application
|
116
|
+
models: AppModels
|
117
|
+
|
118
|
+
# services of the application, which encode the business logic
|
119
|
+
services: Package
|
120
|
+
|
121
|
+
# API of the application
|
122
|
+
api: Package
|
123
|
+
|
124
|
+
schema_files: Sequence[Path]
|
125
|
+
|
126
|
+
language: Language
|
127
|
+
|
128
|
+
def __init__(
|
129
|
+
self, name: str, dir: Path, schema_files: Sequence[Path], language: Language
|
130
|
+
):
|
131
|
+
self.root = Package(self, name, dir, language)
|
132
|
+
|
133
|
+
self.config = self.root.module("config")
|
134
|
+
self.models = AppModels.from_pkg(self.root.pkg("models"))
|
135
|
+
self.services = self.root.pkg("services")
|
136
|
+
self.api = self.root.pkg("api")
|
137
|
+
|
138
|
+
self.schema_files = schema_files
|
139
|
+
|
140
|
+
self.language = language
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
|
4
|
+
class MultiLingualString(str):
|
5
|
+
lang: str
|
6
|
+
lang2value: dict[str, str]
|
7
|
+
|
8
|
+
def __new__(cls, lang2value: dict[str, str], lang):
|
9
|
+
object = str.__new__(cls, lang2value[lang])
|
10
|
+
object.lang = lang
|
11
|
+
object.lang2value = lang2value
|
12
|
+
return object
|
13
|
+
|
14
|
+
def as_lang(self, lang: str) -> str:
|
15
|
+
return self.lang2value[lang]
|
16
|
+
|
17
|
+
def as_lang_default(self, lang: str, default: str) -> str:
|
18
|
+
return self.lang2value.get(lang, default)
|
19
|
+
|
20
|
+
def has_lang(self, lang: str) -> bool:
|
21
|
+
return lang in self.lang2value
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def en(label: str):
|
25
|
+
return MultiLingualString(lang2value={"en": label}, lang="en")
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def from_dict(obj: dict):
|
29
|
+
return MultiLingualString(obj["lang2value"], obj["lang"])
|
30
|
+
|
31
|
+
def to_dict(self):
|
32
|
+
return {"lang2value": self.lang2value, "lang": self.lang}
|
33
|
+
|
34
|
+
def to_tuple(self):
|
35
|
+
return self.lang2value, self.lang
|
36
|
+
|
37
|
+
def __getnewargs__(self) -> tuple[dict[str, str], str]: # type: ignore
|
38
|
+
return self.lang2value, self.lang
|
sera/models/_parse.py
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Sequence
|
5
|
+
|
6
|
+
import serde.yaml
|
7
|
+
from sera.models._class import Class, ClassDBMapInfo
|
8
|
+
from sera.models._datatype import DataType
|
9
|
+
from sera.models._multi_lingual_string import MultiLingualString
|
10
|
+
from sera.models._property import (
|
11
|
+
Cardinality,
|
12
|
+
DataPropDBInfo,
|
13
|
+
DataProperty,
|
14
|
+
ForeignKeyOnDelete,
|
15
|
+
ForeignKeyOnUpdate,
|
16
|
+
ObjectPropDBInfo,
|
17
|
+
ObjectProperty,
|
18
|
+
)
|
19
|
+
from sera.models._schema import Schema
|
20
|
+
|
21
|
+
|
22
|
+
def parse_schema(files: Sequence[Path | str]) -> Schema:
|
23
|
+
schema = Schema(classes={})
|
24
|
+
|
25
|
+
# parse all classes
|
26
|
+
raw_defs = {}
|
27
|
+
for file in files:
|
28
|
+
for k, v in serde.yaml.deser(file).items():
|
29
|
+
cdef = _parse_class_without_prop(schema, k, v)
|
30
|
+
assert k not in schema.classes
|
31
|
+
schema.classes[k] = cdef
|
32
|
+
raw_defs[k] = v
|
33
|
+
|
34
|
+
# now parse properties of the classes
|
35
|
+
for clsname, v in raw_defs.items():
|
36
|
+
cdef = schema.classes[clsname]
|
37
|
+
|
38
|
+
for propname, prop in (v["props"] or {}).items():
|
39
|
+
assert propname not in cdef.properties
|
40
|
+
cdef.properties[propname] = _parse_property(schema, propname, prop)
|
41
|
+
|
42
|
+
return schema
|
43
|
+
|
44
|
+
|
45
|
+
def _parse_class_without_prop(schema: Schema, clsname: str, cls: dict) -> Class:
|
46
|
+
db = None
|
47
|
+
if "db" in cls:
|
48
|
+
db = ClassDBMapInfo(table_name=cls["db"]["table_name"])
|
49
|
+
return Class(
|
50
|
+
name=clsname,
|
51
|
+
label=_parse_multi_lingual_string(cls["label"]),
|
52
|
+
description=_parse_multi_lingual_string(cls["desc"]),
|
53
|
+
properties={},
|
54
|
+
db=db,
|
55
|
+
)
|
56
|
+
|
57
|
+
|
58
|
+
def _parse_property(
|
59
|
+
schema: Schema, prop_name: str, prop: dict
|
60
|
+
) -> DataProperty | ObjectProperty:
|
61
|
+
if isinstance(prop, str):
|
62
|
+
datatype = prop
|
63
|
+
if datatype in schema.classes:
|
64
|
+
return ObjectProperty(
|
65
|
+
name=prop_name,
|
66
|
+
label=_parse_multi_lingual_string(prop_name),
|
67
|
+
description=_parse_multi_lingual_string(""),
|
68
|
+
target=schema.classes[datatype],
|
69
|
+
cardinality=Cardinality.ONE_TO_ONE,
|
70
|
+
is_private=False,
|
71
|
+
)
|
72
|
+
else:
|
73
|
+
return DataProperty(
|
74
|
+
name=prop_name,
|
75
|
+
label=_parse_multi_lingual_string(prop_name),
|
76
|
+
description=_parse_multi_lingual_string(""),
|
77
|
+
datatype=_parse_datatype(datatype),
|
78
|
+
is_private=False,
|
79
|
+
)
|
80
|
+
|
81
|
+
db = prop.get("db", {})
|
82
|
+
|
83
|
+
assert isinstance(prop, dict), prop
|
84
|
+
if "datatype" in prop:
|
85
|
+
return DataProperty(
|
86
|
+
name=prop_name,
|
87
|
+
label=_parse_multi_lingual_string(prop.get("label", prop_name)),
|
88
|
+
description=_parse_multi_lingual_string(prop.get("desc", "")),
|
89
|
+
datatype=_parse_datatype(prop["datatype"]),
|
90
|
+
is_private=prop.get("is_private", False),
|
91
|
+
db=(
|
92
|
+
DataPropDBInfo(
|
93
|
+
is_primary_key=db.get("is_primary_key", False),
|
94
|
+
is_auto_increment=db.get("is_auto_increment", False),
|
95
|
+
is_unique=db.get("is_unique", False),
|
96
|
+
)
|
97
|
+
if "db" in prop
|
98
|
+
else None
|
99
|
+
),
|
100
|
+
)
|
101
|
+
|
102
|
+
assert "target" in prop, prop
|
103
|
+
return ObjectProperty(
|
104
|
+
name=prop_name,
|
105
|
+
label=_parse_multi_lingual_string(prop.get("label", prop_name)),
|
106
|
+
description=_parse_multi_lingual_string(prop.get("desc", "")),
|
107
|
+
target=schema.classes[prop["target"]],
|
108
|
+
cardinality=Cardinality(prop.get("cardinality", "1:1")),
|
109
|
+
is_optional=prop.get("is_optional", False),
|
110
|
+
is_private=prop.get("is_private", False),
|
111
|
+
db=(
|
112
|
+
ObjectPropDBInfo(
|
113
|
+
is_embedded=db.get("is_embedded", None),
|
114
|
+
on_delete=ForeignKeyOnDelete(db.get("on_delete", "restrict")),
|
115
|
+
on_update=ForeignKeyOnUpdate(db.get("on_update", "restrict")),
|
116
|
+
)
|
117
|
+
if "db" in prop
|
118
|
+
else None
|
119
|
+
),
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
def _parse_multi_lingual_string(o: dict | str) -> MultiLingualString:
|
124
|
+
if isinstance(o, str):
|
125
|
+
return MultiLingualString.en(o)
|
126
|
+
assert isinstance(o, dict), o
|
127
|
+
assert "en" in o
|
128
|
+
return MultiLingualString(lang2value=o, lang="en")
|
129
|
+
|
130
|
+
|
131
|
+
def _parse_datatype(datatype: str) -> DataType:
|
132
|
+
if datatype.endswith("[]"):
|
133
|
+
datatype = datatype[:-2]
|
134
|
+
is_list = True
|
135
|
+
else:
|
136
|
+
is_list = False
|
137
|
+
|
138
|
+
if datatype == "string":
|
139
|
+
return DataType("str", is_list=is_list, parent=None)
|
140
|
+
if datatype == "integer":
|
141
|
+
return DataType("int", is_list=is_list, parent=None)
|
142
|
+
if datatype == "datetime":
|
143
|
+
return DataType("datetime", is_list=is_list, parent=None)
|
144
|
+
if datatype == "bool":
|
145
|
+
return DataType("bool", is_list=is_list, parent=None)
|
146
|
+
if datatype == "float":
|
147
|
+
return DataType("float", is_list=is_list, parent=None)
|
148
|
+
if datatype == "bytes":
|
149
|
+
return DataType("bytes", is_list=is_list, parent=None)
|
150
|
+
if datatype == "dict":
|
151
|
+
return DataType("dict", is_list=is_list, parent=None)
|
152
|
+
|
153
|
+
raise NotImplementedError(datatype)
|
sera/models/_property.py
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from enum import Enum
|
5
|
+
from typing import TYPE_CHECKING, Literal, Optional
|
6
|
+
|
7
|
+
from sera.models._datatype import DataType
|
8
|
+
from sera.models._multi_lingual_string import MultiLingualString
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from sera.models._class import Class
|
12
|
+
|
13
|
+
|
14
|
+
class ForeignKeyOnDelete(str, Enum):
|
15
|
+
CASCADE = "cascade"
|
16
|
+
SET_NULL = "set null"
|
17
|
+
RESTRICT = "restrict"
|
18
|
+
|
19
|
+
def to_sqlalchemy(self) -> str:
|
20
|
+
if self == ForeignKeyOnDelete.CASCADE:
|
21
|
+
return "CASCADE"
|
22
|
+
elif self == ForeignKeyOnDelete.SET_NULL:
|
23
|
+
return "SET NULL"
|
24
|
+
elif self == ForeignKeyOnDelete.RESTRICT:
|
25
|
+
return "RESTRICT"
|
26
|
+
raise NotImplementedError(self)
|
27
|
+
|
28
|
+
|
29
|
+
class ForeignKeyOnUpdate(str, Enum):
|
30
|
+
CASCADE = "cascade"
|
31
|
+
DELETE = "delete"
|
32
|
+
RESTRICT = "restrict"
|
33
|
+
|
34
|
+
def to_sqlalchemy(self) -> str:
|
35
|
+
if self == ForeignKeyOnUpdate.CASCADE:
|
36
|
+
return "CASCADE"
|
37
|
+
elif self == ForeignKeyOnUpdate.DELETE:
|
38
|
+
return "DELETE"
|
39
|
+
elif self == ForeignKeyOnUpdate.RESTRICT:
|
40
|
+
return "RESTRICT"
|
41
|
+
raise NotImplementedError(self)
|
42
|
+
|
43
|
+
|
44
|
+
class Cardinality(str, Enum):
|
45
|
+
ONE_TO_ONE = "1:1"
|
46
|
+
ONE_TO_MANY = "1:N"
|
47
|
+
MANY_TO_ONE = "N:1"
|
48
|
+
MANY_TO_MANY = "N:N"
|
49
|
+
|
50
|
+
def is_star_to_many(self) -> bool:
|
51
|
+
return self in [
|
52
|
+
Cardinality.ONE_TO_MANY,
|
53
|
+
Cardinality.MANY_TO_MANY,
|
54
|
+
]
|
55
|
+
|
56
|
+
|
57
|
+
@dataclass(kw_only=True)
|
58
|
+
class Property:
|
59
|
+
"""Represent a property of a class."""
|
60
|
+
|
61
|
+
# name of the property in the application layer
|
62
|
+
name: str = field(
|
63
|
+
metadata={
|
64
|
+
"description": "Name of the property in the application layer, so it must be a valid Python identifier"
|
65
|
+
}
|
66
|
+
)
|
67
|
+
# human-readable name of the property
|
68
|
+
label: MultiLingualString
|
69
|
+
# human-readable description of the property
|
70
|
+
description: MultiLingualString
|
71
|
+
# whether this property is private and cannot be accessed by the end users
|
72
|
+
# default it is false
|
73
|
+
is_private: bool = field(default=False)
|
74
|
+
|
75
|
+
|
76
|
+
@dataclass(kw_only=True)
|
77
|
+
class DataPropDBInfo:
|
78
|
+
"""Represent database information for a data property."""
|
79
|
+
|
80
|
+
# whether this property is a primary key or not
|
81
|
+
is_primary_key: bool = False
|
82
|
+
# if this property is an integer primary key, whether it is auto-incremented or not
|
83
|
+
is_auto_increment: bool = False
|
84
|
+
# whether this property contains unique values
|
85
|
+
is_unique: bool = False
|
86
|
+
|
87
|
+
|
88
|
+
@dataclass(kw_only=True)
|
89
|
+
class DataProperty(Property):
|
90
|
+
# data type of the property
|
91
|
+
datatype: DataType
|
92
|
+
# other database properties of this property
|
93
|
+
db: Optional[DataPropDBInfo] = None
|
94
|
+
|
95
|
+
|
96
|
+
@dataclass(kw_only=True)
|
97
|
+
class ObjectPropDBInfo:
|
98
|
+
"""Represent database information for an object property."""
|
99
|
+
|
100
|
+
# if the target class is not stored in the database, whether to store this property as a composite class
|
101
|
+
# (see SQLAlchemy composite) or embedded (JSON). Note that it doesn't make sense to embed in composite mode
|
102
|
+
# if the cardinality is not 1:1
|
103
|
+
is_embedded: Optional[Literal["composite", "json"]] = None
|
104
|
+
|
105
|
+
# if the target class is stored in the database, control the cascade behavior
|
106
|
+
on_delete: ForeignKeyOnDelete = ForeignKeyOnDelete.RESTRICT
|
107
|
+
on_update: ForeignKeyOnUpdate = ForeignKeyOnUpdate.RESTRICT
|
108
|
+
|
109
|
+
|
110
|
+
@dataclass(kw_only=True)
|
111
|
+
class ObjectProperty(Property):
|
112
|
+
# the target class of the property
|
113
|
+
target: Class
|
114
|
+
# the cardinality of the property -- is it one-to-one, many-to-one, etc.
|
115
|
+
# if the cardinality is many-to-many, a new joint class is going to be generated automatically
|
116
|
+
# to store the relationship -- users can overwrite this generated class by define the one with the same
|
117
|
+
# name
|
118
|
+
cardinality: Cardinality
|
119
|
+
# whether this property is optional or not
|
120
|
+
is_optional: bool = False
|
121
|
+
# whether this property is stored as a mapping dic[str, Target] or not
|
122
|
+
# only valid for *-to-many relationships
|
123
|
+
is_map: bool = False
|
124
|
+
db: Optional[ObjectPropDBInfo] = None
|
sera/models/_schema.py
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from graphlib import TopologicalSorter
|
5
|
+
|
6
|
+
from sera.models._class import Class
|
7
|
+
from sera.models._property import ObjectProperty
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class Schema:
|
12
|
+
classes: dict[str, Class]
|
13
|
+
|
14
|
+
def topological_sort(self) -> list[Class]:
|
15
|
+
"""
|
16
|
+
Sort classes in topological order using graphlib.TopologicalSorter.
|
17
|
+
"""
|
18
|
+
# Build the dependency graph
|
19
|
+
graph = {}
|
20
|
+
for cls_name, cls in self.classes.items():
|
21
|
+
dependencies = set()
|
22
|
+
for prop in cls.properties.values():
|
23
|
+
if isinstance(prop, ObjectProperty) and prop.target.name != cls_name:
|
24
|
+
dependencies.add(prop.target.name)
|
25
|
+
graph[cls_name] = dependencies
|
26
|
+
|
27
|
+
# Create topological sorter and get sorted class names
|
28
|
+
sorter = TopologicalSorter(graph)
|
29
|
+
sorted_names = list(sorter.static_order())
|
30
|
+
|
31
|
+
# Convert sorted names back to Class objects
|
32
|
+
return [self.classes[name] for name in sorted_names]
|
sera/namespace.py
ADDED
sera/typing.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: sera-2
|
3
|
+
Version: 1.1.0
|
4
|
+
Summary:
|
5
|
+
Author: Binh Vu
|
6
|
+
Author-email: bvu687@gmail.com
|
7
|
+
Requires-Python: >=3.12,<4.0
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
11
|
+
Requires-Dist: black (>=25.0.1,<26.0.0)
|
12
|
+
Requires-Dist: codegen-2 (>=2.1.4,<3.0.0)
|
13
|
+
Requires-Dist: litestar (>=2.15.1,<3.0.0)
|
14
|
+
Requires-Dist: msgspec (>=0.19.0,<0.20.0)
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
|
17
|
+
# Overview
|
18
|
+
|
19
|
+
This library enables rapid application development by leveraging a graph-based architecture.
|
20
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
sera/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
sera/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
sera/libs/api_helper.py,sha256=hUEy0INHM18lxTQ348tgbXNceOHcjiAnqmuL_8CRpLQ,2509
|
4
|
+
sera/libs/base_orm.py,sha256=IxsHjNPjF60YbLyTsMTOhQbjQX-L30UZtvE_vJDgZfo,2950
|
5
|
+
sera/libs/base_service.py,sha256=nVHODZjdTqC8rrDKxYUWrK95APrtdMSCYzflePNxXns,2379
|
6
|
+
sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
sera/make/__main__.py,sha256=TLHA3e9QVoE0Nic6HSQDNiAe1MrTTw5IdYlNSNxjN5Q,912
|
8
|
+
sera/make/make_app.py,sha256=e0DQpx6ilB76lxg2BYF6wjL7RFRQzvmOXq2322xm2SQ,4398
|
9
|
+
sera/make/make_python_api.py,sha256=ikZfJrJ4B606td4os1QgX-7G5QJypmR-3qgLvGOIRh0,8765
|
10
|
+
sera/make/make_python_model.py,sha256=1zC_NaFkyLYpZ3ytT-ZV6d8xmxtQHUpEFsqd0PkJ6x4,12031
|
11
|
+
sera/make/make_python_services.py,sha256=RsinYZdfkrTlTn9CT50VgqGs9w6IZawsJx-KEmqfnEY,2062
|
12
|
+
sera/make/make_typescript_model.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
|
13
|
+
sera/misc/__init__.py,sha256=hjW2Lf1itdqlDjf1X8hwSZZfmE0XKDFlYtMMC1AXkT4,300
|
14
|
+
sera/misc/_rdf.py,sha256=G0ekWLUSw-eiKUOaPSGkELbZld19S9d36gWwiswjrrs,1671
|
15
|
+
sera/misc/_utils.py,sha256=MY85CZruRLYI2bf44d-a8g4lJibznD2MnAfd9VY1UdQ,1094
|
16
|
+
sera/models/__init__.py,sha256=3ooJQulUc7oyUoMM89KT5f6nsoYagCjYEyC0AmyYyzY,661
|
17
|
+
sera/models/_class.py,sha256=vFCuzG0vfAHYBjk1HQMFszT7zUAqGex-VAVJ2CUPIaE,1824
|
18
|
+
sera/models/_collection.py,sha256=u3k4t7Z38FiZj9CstPArnf9KsIGr05zbwUn2ZrbQVJE,1078
|
19
|
+
sera/models/_datatype.py,sha256=JkD3aRQ11DWicS2xaiNPwhBzJNJPwSkktBunx3EVr1s,1714
|
20
|
+
sera/models/_module.py,sha256=-XW4bJ3v4QZPYPRMnbUW2zp0F5aJVH8uve_MrnwYz7w,3736
|
21
|
+
sera/models/_multi_lingual_string.py,sha256=cVoxge1SW68dVmOjDZJU93tUq2ccJse3kSPqxrNkmgc,1091
|
22
|
+
sera/models/_parse.py,sha256=l106WMAFTLjKio5L3rBK3B0NtwjnXh-sLF4Ofk2uFHE,5072
|
23
|
+
sera/models/_property.py,sha256=lrI6V2zEblUVuEy9lahbLPLFisFRX6zrkO8wFWzU35k,4018
|
24
|
+
sera/models/_schema.py,sha256=1F_Ict1NLvDcQDUZwcvnRS5MtsOTv584y3ChymUeUYA,1046
|
25
|
+
sera/namespace.py,sha256=5NJ0A7weZwblqkncpgY2Vfcat04mNtigNcVrqu7TGOc,123
|
26
|
+
sera/typing.py,sha256=CsazgVptDN-far3yISogTtA2KBJlFFOn0lK_WyZFDv8,230
|
27
|
+
sera_2-1.1.0.dist-info/METADATA,sha256=zmJ_UdY6SLvr7d6pkcQL-oCo9DYB8gxpiM3CikxzySU,599
|
28
|
+
sera_2-1.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
29
|
+
sera_2-1.1.0.dist-info/RECORD,,
|