bluer-objects 6.3.1__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.
Potentially problematic release.
This version of bluer-objects might be problematic. Click here for more details.
- bluer_objects/.abcli/abcli.sh +9 -0
- bluer_objects/.abcli/actions.sh +11 -0
- bluer_objects/.abcli/aka.sh +3 -0
- bluer_objects/.abcli/alias.sh +36 -0
- bluer_objects/.abcli/blue_objects.sh +11 -0
- bluer_objects/.abcli/cache.sh +5 -0
- bluer_objects/.abcli/clone.sh +94 -0
- bluer_objects/.abcli/download.sh +53 -0
- bluer_objects/.abcli/file.sh +8 -0
- bluer_objects/.abcli/gif.sh +27 -0
- bluer_objects/.abcli/host.sh +29 -0
- bluer_objects/.abcli/ls.sh +24 -0
- bluer_objects/.abcli/metadata/get.sh +24 -0
- bluer_objects/.abcli/metadata/post.sh +22 -0
- bluer_objects/.abcli/metadata.sh +16 -0
- bluer_objects/.abcli/mlflow/browse.sh +36 -0
- bluer_objects/.abcli/mlflow/cache.sh +31 -0
- bluer_objects/.abcli/mlflow/list_registered_models.sh +9 -0
- bluer_objects/.abcli/mlflow/log_artifacts.sh +10 -0
- bluer_objects/.abcli/mlflow/log_run.sh +10 -0
- bluer_objects/.abcli/mlflow/run.sh +11 -0
- bluer_objects/.abcli/mlflow/tags/clone.sh +15 -0
- bluer_objects/.abcli/mlflow/tags/get.sh +10 -0
- bluer_objects/.abcli/mlflow/tags/search.sh +12 -0
- bluer_objects/.abcli/mlflow/tags/set.sh +13 -0
- bluer_objects/.abcli/mlflow/tags.sh +16 -0
- bluer_objects/.abcli/mlflow/test.sh +11 -0
- bluer_objects/.abcli/mlflow/transition.sh +20 -0
- bluer_objects/.abcli/mlflow.sh +29 -0
- bluer_objects/.abcli/mysql/cache.sh +65 -0
- bluer_objects/.abcli/mysql/relations.sh +83 -0
- bluer_objects/.abcli/mysql/tags.sh +85 -0
- bluer_objects/.abcli/mysql.sh +16 -0
- bluer_objects/.abcli/object.sh +54 -0
- bluer_objects/.abcli/publish.sh +58 -0
- bluer_objects/.abcli/select.sh +34 -0
- bluer_objects/.abcli/storage/clear.sh +45 -0
- bluer_objects/.abcli/storage/download_file.sh +9 -0
- bluer_objects/.abcli/storage/exists.sh +8 -0
- bluer_objects/.abcli/storage/list.sh +8 -0
- bluer_objects/.abcli/storage/rm.sh +11 -0
- bluer_objects/.abcli/storage/status.sh +11 -0
- bluer_objects/.abcli/storage.sh +15 -0
- bluer_objects/.abcli/tags.sh +5 -0
- bluer_objects/.abcli/tests/README.sh +8 -0
- bluer_objects/.abcli/tests/clone.sh +32 -0
- bluer_objects/.abcli/tests/help.sh +85 -0
- bluer_objects/.abcli/tests/host.sh +7 -0
- bluer_objects/.abcli/tests/ls.sh +13 -0
- bluer_objects/.abcli/tests/metadata.sh +53 -0
- bluer_objects/.abcli/tests/mlflow_cache.sh +14 -0
- bluer_objects/.abcli/tests/mlflow_logging.sh +12 -0
- bluer_objects/.abcli/tests/mlflow_tags.sh +29 -0
- bluer_objects/.abcli/tests/mlflow_test.sh +7 -0
- bluer_objects/.abcli/tests/mysql_cache.sh +15 -0
- bluer_objects/.abcli/tests/mysql_relations.sh +20 -0
- bluer_objects/.abcli/tests/mysql_tags.sh +16 -0
- bluer_objects/.abcli/tests/test_gif.sh +13 -0
- bluer_objects/.abcli/tests/version.sh +10 -0
- bluer_objects/.abcli/upload.sh +73 -0
- bluer_objects/README/__init__.py +29 -0
- bluer_objects/README/functions.py +285 -0
- bluer_objects/README/items.py +30 -0
- bluer_objects/__init__.py +19 -0
- bluer_objects/__main__.py +16 -0
- bluer_objects/config.env +22 -0
- bluer_objects/env.py +72 -0
- bluer_objects/file/__init__.py +41 -0
- bluer_objects/file/__main__.py +51 -0
- bluer_objects/file/classes.py +38 -0
- bluer_objects/file/functions.py +290 -0
- bluer_objects/file/load.py +219 -0
- bluer_objects/file/save.py +280 -0
- bluer_objects/graphics/__init__.py +4 -0
- bluer_objects/graphics/__main__.py +84 -0
- bluer_objects/graphics/frame.py +15 -0
- bluer_objects/graphics/gif.py +86 -0
- bluer_objects/graphics/screen.py +63 -0
- bluer_objects/graphics/signature.py +97 -0
- bluer_objects/graphics/text.py +165 -0
- bluer_objects/help/__init__.py +0 -0
- bluer_objects/help/__main__.py +10 -0
- bluer_objects/help/functions.py +5 -0
- bluer_objects/host/__init__.py +1 -0
- bluer_objects/host/__main__.py +84 -0
- bluer_objects/host/functions.py +66 -0
- bluer_objects/logger/__init__.py +4 -0
- bluer_objects/logger/matrix.py +209 -0
- bluer_objects/markdown.py +43 -0
- bluer_objects/metadata/__init__.py +8 -0
- bluer_objects/metadata/__main__.py +110 -0
- bluer_objects/metadata/enums.py +29 -0
- bluer_objects/metadata/get.py +89 -0
- bluer_objects/metadata/post.py +101 -0
- bluer_objects/mlflow/__init__.py +28 -0
- bluer_objects/mlflow/__main__.py +271 -0
- bluer_objects/mlflow/cache.py +13 -0
- bluer_objects/mlflow/logging.py +81 -0
- bluer_objects/mlflow/models.py +57 -0
- bluer_objects/mlflow/objects.py +76 -0
- bluer_objects/mlflow/runs.py +100 -0
- bluer_objects/mlflow/tags.py +90 -0
- bluer_objects/mlflow/testing.py +39 -0
- bluer_objects/mysql/cache/__init__.py +8 -0
- bluer_objects/mysql/cache/__main__.py +91 -0
- bluer_objects/mysql/cache/functions.py +181 -0
- bluer_objects/mysql/relations/__init__.py +9 -0
- bluer_objects/mysql/relations/__main__.py +138 -0
- bluer_objects/mysql/relations/functions.py +180 -0
- bluer_objects/mysql/table.py +144 -0
- bluer_objects/mysql/tags/__init__.py +1 -0
- bluer_objects/mysql/tags/__main__.py +130 -0
- bluer_objects/mysql/tags/functions.py +203 -0
- bluer_objects/objects.py +167 -0
- bluer_objects/path.py +194 -0
- bluer_objects/sample.env +16 -0
- bluer_objects/storage/__init__.py +3 -0
- bluer_objects/storage/__main__.py +114 -0
- bluer_objects/storage/classes.py +237 -0
- bluer_objects/tests/__init__.py +0 -0
- bluer_objects/tests/test_README.py +5 -0
- bluer_objects/tests/test_env.py +27 -0
- bluer_objects/tests/test_file_load_save.py +105 -0
- bluer_objects/tests/test_fullname.py +5 -0
- bluer_objects/tests/test_graphics.py +28 -0
- bluer_objects/tests/test_graphics_frame.py +11 -0
- bluer_objects/tests/test_graphics_gif.py +29 -0
- bluer_objects/tests/test_graphics_screen.py +8 -0
- bluer_objects/tests/test_graphics_signature.py +80 -0
- bluer_objects/tests/test_graphics_text.py +14 -0
- bluer_objects/tests/test_logger.py +5 -0
- bluer_objects/tests/test_logger_matrix.py +73 -0
- bluer_objects/tests/test_markdown.py +10 -0
- bluer_objects/tests/test_metadata.py +204 -0
- bluer_objects/tests/test_mlflow.py +60 -0
- bluer_objects/tests/test_mysql_cache.py +14 -0
- bluer_objects/tests/test_mysql_relations.py +16 -0
- bluer_objects/tests/test_mysql_table.py +9 -0
- bluer_objects/tests/test_mysql_tags.py +13 -0
- bluer_objects/tests/test_objects.py +180 -0
- bluer_objects/tests/test_path.py +7 -0
- bluer_objects/tests/test_storage.py +7 -0
- bluer_objects/tests/test_version.py +5 -0
- bluer_objects/urls.py +3 -0
- bluer_objects-6.3.1.dist-info/METADATA +57 -0
- bluer_objects-6.3.1.dist-info/RECORD +149 -0
- bluer_objects-6.3.1.dist-info/WHEEL +5 -0
- bluer_objects-6.3.1.dist-info/licenses/LICENSE +121 -0
- bluer_objects-6.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
from functools import reduce
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from blue_options import string
|
|
6
|
+
|
|
7
|
+
from bluer_objects.mysql.table import Table
|
|
8
|
+
from bluer_objects.logger import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
columns = "keyword,value,timestamp".split(",")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create() -> bool:
|
|
15
|
+
return Table.Create(
|
|
16
|
+
"cache",
|
|
17
|
+
[
|
|
18
|
+
"keyword VARCHAR(1024) NOT NULL",
|
|
19
|
+
"value VARCHAR(4096) NOT NULL",
|
|
20
|
+
],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def clone(
|
|
25
|
+
source: str,
|
|
26
|
+
destination: str,
|
|
27
|
+
) -> bool:
|
|
28
|
+
lut = search(f"{source}.%")
|
|
29
|
+
|
|
30
|
+
lut = {
|
|
31
|
+
"{}.{}".format(
|
|
32
|
+
destination,
|
|
33
|
+
string.after(
|
|
34
|
+
keyword,
|
|
35
|
+
f"{source}.",
|
|
36
|
+
),
|
|
37
|
+
): value
|
|
38
|
+
for keyword, value in lut.items()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return reduce(
|
|
42
|
+
lambda x, y: x and y,
|
|
43
|
+
[write(keyword, value) for keyword, value in lut.items()],
|
|
44
|
+
True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def read(
|
|
49
|
+
keyword: str,
|
|
50
|
+
all: bool = False,
|
|
51
|
+
dataframe: bool = False,
|
|
52
|
+
like: bool = False,
|
|
53
|
+
unique: bool = False,
|
|
54
|
+
) -> Any:
|
|
55
|
+
if dataframe:
|
|
56
|
+
all = True
|
|
57
|
+
|
|
58
|
+
table = Table(name="cache")
|
|
59
|
+
|
|
60
|
+
if isinstance(keyword, list):
|
|
61
|
+
keyword = ".".join(keyword)
|
|
62
|
+
|
|
63
|
+
if not table.connect():
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
success, output = table.execute(
|
|
67
|
+
(
|
|
68
|
+
"SELECT {} FROM ".format(
|
|
69
|
+
",".join(["c.{}".format(column) for column in columns])
|
|
70
|
+
)
|
|
71
|
+
+ "abcli.cache c "
|
|
72
|
+
)
|
|
73
|
+
+ (
|
|
74
|
+
(
|
|
75
|
+
"INNER JOIN ( "
|
|
76
|
+
"SELECT keyword, MAX(timestamp) AS max_timestamp "
|
|
77
|
+
"From abcli.cache "
|
|
78
|
+
"GROUP BY keyword "
|
|
79
|
+
") cm ON c.keyword = cm.keyword AND c.timestamp = cm.max_timestamp "
|
|
80
|
+
)
|
|
81
|
+
if unique
|
|
82
|
+
else ""
|
|
83
|
+
)
|
|
84
|
+
+ (
|
|
85
|
+
"WHERE c.keyword {} '{}' ".format("like" if like else "=", keyword)
|
|
86
|
+
+ "ORDER BY c.timestamp DESC "
|
|
87
|
+
+ "{};".format(
|
|
88
|
+
"" if all else "LIMIT 1",
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
if not success:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
if not table.disconnect():
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
output = [
|
|
99
|
+
{keyword: item for keyword, item in zip(columns, thing)} for thing in output
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
if not dataframe:
|
|
103
|
+
output = [item["value"] for item in output]
|
|
104
|
+
|
|
105
|
+
if not all:
|
|
106
|
+
output = "" if not output else output[0]
|
|
107
|
+
|
|
108
|
+
if dataframe:
|
|
109
|
+
import pandas as pd
|
|
110
|
+
|
|
111
|
+
output = pd.DataFrame.from_dict(output)
|
|
112
|
+
|
|
113
|
+
return output
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def search(keyword: str) -> Dict[str, str]:
|
|
117
|
+
table = Table(name="cache")
|
|
118
|
+
|
|
119
|
+
if isinstance(keyword, list):
|
|
120
|
+
keyword = ".".join(keyword)
|
|
121
|
+
|
|
122
|
+
if not table.connect():
|
|
123
|
+
return {}
|
|
124
|
+
|
|
125
|
+
success, output = table.execute(
|
|
126
|
+
"SELECT keyword,value FROM abcli.cache "
|
|
127
|
+
f"WHERE keyword like '{keyword}' "
|
|
128
|
+
"ORDER BY timestamp ASC;"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if success:
|
|
132
|
+
success = table.disconnect()
|
|
133
|
+
|
|
134
|
+
return {thing[0]: thing[1] for thing in output} if success else {}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def search_value(value: str) -> List[str]:
|
|
138
|
+
table = Table(name="cache")
|
|
139
|
+
|
|
140
|
+
if not table.connect():
|
|
141
|
+
return []
|
|
142
|
+
|
|
143
|
+
success, output = table.execute(
|
|
144
|
+
"SELECT keyword,value FROM abcli.cache "
|
|
145
|
+
f"WHERE value = '{value}' "
|
|
146
|
+
"ORDER BY timestamp DESC;"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if success:
|
|
150
|
+
success = table.disconnect()
|
|
151
|
+
|
|
152
|
+
return [thing[0] for thing in output] if success else []
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def write(
|
|
156
|
+
keyword: str,
|
|
157
|
+
value: str,
|
|
158
|
+
) -> bool:
|
|
159
|
+
table = Table(name="cache")
|
|
160
|
+
|
|
161
|
+
if isinstance(keyword, list):
|
|
162
|
+
keyword = ".".join(keyword)
|
|
163
|
+
|
|
164
|
+
if isinstance(value, dict):
|
|
165
|
+
value = json.dumps(value)
|
|
166
|
+
|
|
167
|
+
if not table.connect():
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
success = table.insert(
|
|
171
|
+
["keyword", "value"],
|
|
172
|
+
[keyword, value],
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if success:
|
|
176
|
+
success = table.disconnect()
|
|
177
|
+
|
|
178
|
+
if success:
|
|
179
|
+
logger.info("cache[{}] <- {}".format(keyword, value))
|
|
180
|
+
|
|
181
|
+
return success
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from blueness import module
|
|
4
|
+
from blueness.argparse.generic import sys_exit
|
|
5
|
+
|
|
6
|
+
from bluer_objects import file, NAME
|
|
7
|
+
from bluer_objects.mysql.relations.functions import (
|
|
8
|
+
clone,
|
|
9
|
+
create,
|
|
10
|
+
get,
|
|
11
|
+
search,
|
|
12
|
+
set_,
|
|
13
|
+
inverse_of,
|
|
14
|
+
list_of,
|
|
15
|
+
)
|
|
16
|
+
from bluer_objects.logger import logger
|
|
17
|
+
|
|
18
|
+
NAME = module.name(__file__, NAME)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
parser = argparse.ArgumentParser(NAME)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"task",
|
|
24
|
+
type=str,
|
|
25
|
+
default="get",
|
|
26
|
+
help="clone|create|get|list|search|set",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--count",
|
|
30
|
+
type=int,
|
|
31
|
+
default=-1,
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--delim",
|
|
35
|
+
type=str,
|
|
36
|
+
default=", ",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--filename",
|
|
40
|
+
type=str,
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--item_name",
|
|
44
|
+
default="object",
|
|
45
|
+
type=str,
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--log",
|
|
49
|
+
default=1,
|
|
50
|
+
type=int,
|
|
51
|
+
help="0|1",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--object_1",
|
|
55
|
+
type=str,
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--object_2",
|
|
59
|
+
type=str,
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--relation",
|
|
63
|
+
default="",
|
|
64
|
+
type=str,
|
|
65
|
+
)
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--return_list",
|
|
68
|
+
type=int,
|
|
69
|
+
default=0,
|
|
70
|
+
)
|
|
71
|
+
args = parser.parse_args()
|
|
72
|
+
|
|
73
|
+
delim = " " if args.delim == "space" else args.delim
|
|
74
|
+
|
|
75
|
+
success = False
|
|
76
|
+
if args.task == "clone":
|
|
77
|
+
success = clone(args.object_1, args.object_2)
|
|
78
|
+
elif args.task == "create":
|
|
79
|
+
success = create()
|
|
80
|
+
elif args.task == "get":
|
|
81
|
+
relation = get(args.object_1, args.object_2)
|
|
82
|
+
if args.log:
|
|
83
|
+
logger.info(
|
|
84
|
+
f"{args.object_1} -{f'{relation}->' if relation else 'X-'} {args.object_2}"
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
print(relation)
|
|
88
|
+
success = True
|
|
89
|
+
elif args.task == "list":
|
|
90
|
+
if args.return_list:
|
|
91
|
+
output = [relation for relation in list_of if relation]
|
|
92
|
+
|
|
93
|
+
if args.count != -1:
|
|
94
|
+
output = output[: args.count]
|
|
95
|
+
|
|
96
|
+
if args.log:
|
|
97
|
+
logger.info(f"{len(output):,} relations(s): {delim.join(output)}")
|
|
98
|
+
else:
|
|
99
|
+
print(delim.join(output))
|
|
100
|
+
else:
|
|
101
|
+
if args.log:
|
|
102
|
+
for thing in sorted(
|
|
103
|
+
[
|
|
104
|
+
"{} : {}".format(this, that)
|
|
105
|
+
for this, that in inverse_of.items()
|
|
106
|
+
if this
|
|
107
|
+
]
|
|
108
|
+
):
|
|
109
|
+
logger.info(thing)
|
|
110
|
+
else:
|
|
111
|
+
print(
|
|
112
|
+
delim.join(
|
|
113
|
+
[
|
|
114
|
+
"{}:{}".format(this, that)
|
|
115
|
+
for this, that in inverse_of.items()
|
|
116
|
+
if this
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
success = True
|
|
121
|
+
elif args.task == "search":
|
|
122
|
+
output = search(args.object_1, args.relation, args.count)
|
|
123
|
+
|
|
124
|
+
if args.relation:
|
|
125
|
+
if args.log:
|
|
126
|
+
logger.info(f"{len(output):,} {args.item_name}(s): {delim.join(output)}")
|
|
127
|
+
else:
|
|
128
|
+
print(delim.join(output))
|
|
129
|
+
else:
|
|
130
|
+
print(output)
|
|
131
|
+
|
|
132
|
+
success = file.save_json(args.filename, output) if args.filename else True
|
|
133
|
+
elif args.task == "set":
|
|
134
|
+
success = set_(args.object_1, args.object_2, args.relation)
|
|
135
|
+
else:
|
|
136
|
+
success = None
|
|
137
|
+
|
|
138
|
+
sys_exit(logger, NAME, args.task, success, log=args.log)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Union, Dict, List
|
|
3
|
+
from functools import reduce
|
|
4
|
+
|
|
5
|
+
from bluer_objects import file
|
|
6
|
+
from bluer_objects.mysql.table import Table
|
|
7
|
+
from bluer_objects.logger import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_, inverse_of = file.load_json(
|
|
11
|
+
os.path.join(
|
|
12
|
+
file.path(__file__),
|
|
13
|
+
"relations.json",
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
inverse_of.update({inverse_of[relation]: relation for relation in inverse_of})
|
|
17
|
+
inverse_of[""] = ""
|
|
18
|
+
|
|
19
|
+
list_of = sorted(list(set(list(inverse_of.keys()) + list(inverse_of.values()))))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
columns = "object_1,object_2,relation,timestamp".split(",")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def clone(
|
|
26
|
+
object_1: str,
|
|
27
|
+
object_2: str,
|
|
28
|
+
) -> bool:
|
|
29
|
+
return reduce(
|
|
30
|
+
lambda x, y: x and y,
|
|
31
|
+
[
|
|
32
|
+
reduce(
|
|
33
|
+
lambda x, y: x and y,
|
|
34
|
+
[set_(object_2, object, relation) for object in object_list],
|
|
35
|
+
True,
|
|
36
|
+
)
|
|
37
|
+
for relation, object_list in search(object_1).items()
|
|
38
|
+
],
|
|
39
|
+
True,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create() -> bool:
|
|
44
|
+
return Table.Create(
|
|
45
|
+
"relations",
|
|
46
|
+
[
|
|
47
|
+
"object_1 VARCHAR(256) NOT NULL",
|
|
48
|
+
"object_2 VARCHAR(256) NOT NULL",
|
|
49
|
+
"relation VARCHAR(256) NOT NULL",
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get(
|
|
55
|
+
object_1: str,
|
|
56
|
+
object_2: str,
|
|
57
|
+
) -> str:
|
|
58
|
+
if object_1 > object_2:
|
|
59
|
+
return inverse_of[get(object_1=object_2, object_2=object_1)]
|
|
60
|
+
|
|
61
|
+
table = Table(name="relations")
|
|
62
|
+
|
|
63
|
+
if not table.connect():
|
|
64
|
+
return ""
|
|
65
|
+
|
|
66
|
+
success, output = table.execute(
|
|
67
|
+
"SELECT r.relation "
|
|
68
|
+
"FROM abcli.relations r "
|
|
69
|
+
"INNER JOIN ( "
|
|
70
|
+
"SELECT MAX(timestamp) AS max_timestamp "
|
|
71
|
+
"FROM abcli.relations "
|
|
72
|
+
f'WHERE object_1="{object_1}" AND object_2="{object_2}" '
|
|
73
|
+
") rm "
|
|
74
|
+
"ON r.timestamp=rm.max_timestamp "
|
|
75
|
+
f'WHERE object_1="{object_1}" AND object_2="{object_2}";'
|
|
76
|
+
)
|
|
77
|
+
if not success:
|
|
78
|
+
return ""
|
|
79
|
+
|
|
80
|
+
if not table.disconnect():
|
|
81
|
+
return ""
|
|
82
|
+
|
|
83
|
+
return "" if not output else output[-1][0]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def search(
|
|
87
|
+
object: str,
|
|
88
|
+
relation: str = "",
|
|
89
|
+
count: int = -1,
|
|
90
|
+
) -> Union[
|
|
91
|
+
Dict[str, List[str]],
|
|
92
|
+
List[str],
|
|
93
|
+
]:
|
|
94
|
+
table = Table(name="relations")
|
|
95
|
+
|
|
96
|
+
output = [] if relation else {}
|
|
97
|
+
|
|
98
|
+
if not table.connect():
|
|
99
|
+
return output
|
|
100
|
+
|
|
101
|
+
success, output_right = table.execute(
|
|
102
|
+
"SELECT r.relation, r.object_2 "
|
|
103
|
+
"FROM abcli.relations r "
|
|
104
|
+
"INNER JOIN ( "
|
|
105
|
+
"SELECT object_2, MAX(timestamp) AS max_timestamp "
|
|
106
|
+
"FROM abcli.relations "
|
|
107
|
+
f'WHERE object_1="{object}" GROUP BY object_2'
|
|
108
|
+
") rm "
|
|
109
|
+
"ON r.timestamp=rm.max_timestamp "
|
|
110
|
+
f'WHERE object_1="{object}"; '
|
|
111
|
+
)
|
|
112
|
+
if not success:
|
|
113
|
+
return {}
|
|
114
|
+
|
|
115
|
+
success, output_left = table.execute(
|
|
116
|
+
"SELECT r.relation, r.object_1 "
|
|
117
|
+
"FROM abcli.relations r "
|
|
118
|
+
"INNER JOIN ( "
|
|
119
|
+
"SELECT object_1, MAX(timestamp) AS max_timestamp "
|
|
120
|
+
"FROM abcli.relations "
|
|
121
|
+
f'WHERE object_2="{object}" GROUP BY object_1'
|
|
122
|
+
") rm "
|
|
123
|
+
"ON r.timestamp=rm.max_timestamp "
|
|
124
|
+
f'WHERE object_2="{object}"; '
|
|
125
|
+
)
|
|
126
|
+
if not success:
|
|
127
|
+
return output
|
|
128
|
+
|
|
129
|
+
if not table.disconnect():
|
|
130
|
+
return output
|
|
131
|
+
|
|
132
|
+
raw_output = {thing[1]: thing[0] for thing in output_right}
|
|
133
|
+
raw_output.update({thing[1]: inverse_of[thing[0]] for thing in output_left})
|
|
134
|
+
|
|
135
|
+
raw_output = {thing: relation for thing, relation in raw_output.items() if relation}
|
|
136
|
+
|
|
137
|
+
output = {}
|
|
138
|
+
for object_, relation_ in raw_output.items():
|
|
139
|
+
output[relation_] = output.get(relation_, []) + [object_]
|
|
140
|
+
|
|
141
|
+
if not relation:
|
|
142
|
+
return output
|
|
143
|
+
|
|
144
|
+
output = output.get(relation, [])
|
|
145
|
+
|
|
146
|
+
if count != -1:
|
|
147
|
+
output = output[-count:]
|
|
148
|
+
|
|
149
|
+
return output
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def set_(
|
|
153
|
+
object_1: str,
|
|
154
|
+
object_2: str,
|
|
155
|
+
relation: str,
|
|
156
|
+
) -> bool:
|
|
157
|
+
if relation not in list_of:
|
|
158
|
+
logger.error(f"-relations: set: {relation}: relation not found.")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
if object_1 > object_2:
|
|
162
|
+
return set_(object_2, object_1, inverse_of[relation])
|
|
163
|
+
|
|
164
|
+
table = Table(name="relations")
|
|
165
|
+
|
|
166
|
+
if not table.connect():
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
if not table.insert(
|
|
170
|
+
["object_1", "object_2", "relation"],
|
|
171
|
+
[object_1, object_2, relation],
|
|
172
|
+
):
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
if not table.disconnect():
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
logger.info("{} ={}=> {}".format(object_1, relation if relation else "X", object_2))
|
|
179
|
+
|
|
180
|
+
return True
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from typing import List, Union, Tuple, Any
|
|
2
|
+
import pymysql
|
|
3
|
+
|
|
4
|
+
from blueness import module
|
|
5
|
+
|
|
6
|
+
from bluer_objects import NAME
|
|
7
|
+
from bluer_objects.env import (
|
|
8
|
+
ABCLI_AWS_RDS_DB,
|
|
9
|
+
ABCLI_AWS_RDS_PORT,
|
|
10
|
+
ABCLI_AWS_RDS_USER,
|
|
11
|
+
ABCLI_AWS_RDS_HOST,
|
|
12
|
+
ABCLI_AWS_RDS_PASSWORD,
|
|
13
|
+
)
|
|
14
|
+
from blue_options.logger import crash_report
|
|
15
|
+
|
|
16
|
+
NAME = module.name(__file__, NAME)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Table:
|
|
20
|
+
def __init__(self, name):
|
|
21
|
+
self.name = name
|
|
22
|
+
|
|
23
|
+
self.db = ABCLI_AWS_RDS_DB
|
|
24
|
+
self.port = int(ABCLI_AWS_RDS_PORT)
|
|
25
|
+
self.user = ABCLI_AWS_RDS_USER
|
|
26
|
+
|
|
27
|
+
self.host = ABCLI_AWS_RDS_HOST
|
|
28
|
+
self.password = ABCLI_AWS_RDS_PASSWORD
|
|
29
|
+
|
|
30
|
+
self.connection = None
|
|
31
|
+
|
|
32
|
+
def connect(
|
|
33
|
+
self,
|
|
34
|
+
create_command: str = "",
|
|
35
|
+
) -> bool:
|
|
36
|
+
if self.connection is not None:
|
|
37
|
+
self.disconnect()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
self.connection = pymysql.connect(
|
|
41
|
+
host=self.host,
|
|
42
|
+
user=self.user,
|
|
43
|
+
port=self.port,
|
|
44
|
+
password=self.password,
|
|
45
|
+
database=self.db,
|
|
46
|
+
)
|
|
47
|
+
except:
|
|
48
|
+
crash_report(f"-{NAME}: connect: failed on host: {self.host}.")
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
return True if not create_command else self.create(create_command)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def Create(
|
|
55
|
+
table_name: str,
|
|
56
|
+
create_command: List[str],
|
|
57
|
+
) -> bool:
|
|
58
|
+
table = Table(name=table_name)
|
|
59
|
+
|
|
60
|
+
return table.disconnect() if table.connect(create_command) else False
|
|
61
|
+
|
|
62
|
+
def create(
|
|
63
|
+
self,
|
|
64
|
+
create_command: List[str],
|
|
65
|
+
) -> bool:
|
|
66
|
+
return self.execute(
|
|
67
|
+
"CREATE TABLE IF NOT EXISTS {} ({})".format(
|
|
68
|
+
self.name,
|
|
69
|
+
",".join(
|
|
70
|
+
[
|
|
71
|
+
"id INT(24) NOT NULL AUTO_INCREMENT",
|
|
72
|
+
"timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
|
|
73
|
+
]
|
|
74
|
+
+ create_command
|
|
75
|
+
+ ["PRIMARY KEY (`id`)", "INDEX `index_timestamp` (`timestamp`)"]
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
commit=True,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def disconnect(self) -> bool:
|
|
82
|
+
if self.connection is None:
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
success = True
|
|
86
|
+
try:
|
|
87
|
+
self.connection.close()
|
|
88
|
+
except:
|
|
89
|
+
crash_report(f"-{NAME}: disconnect: failed.")
|
|
90
|
+
success = False
|
|
91
|
+
|
|
92
|
+
self.connection = None
|
|
93
|
+
return success
|
|
94
|
+
|
|
95
|
+
def drop(self) -> bool:
|
|
96
|
+
return self.execute(f"DROP table {self.name};")
|
|
97
|
+
|
|
98
|
+
def execute(
|
|
99
|
+
self,
|
|
100
|
+
sql: str,
|
|
101
|
+
commit: bool = False,
|
|
102
|
+
returns_output: bool = True,
|
|
103
|
+
) -> Union[bool, Tuple[bool, Any]]:
|
|
104
|
+
output = []
|
|
105
|
+
success = False
|
|
106
|
+
try:
|
|
107
|
+
with self.connection.cursor() as cursor:
|
|
108
|
+
if isinstance(sql, tuple):
|
|
109
|
+
cursor.execute(sql[0], sql[1])
|
|
110
|
+
else:
|
|
111
|
+
cursor.execute(sql)
|
|
112
|
+
|
|
113
|
+
if returns_output:
|
|
114
|
+
output = cursor.fetchall()
|
|
115
|
+
|
|
116
|
+
if commit:
|
|
117
|
+
# connection is not autocommit by default. So you must commit to save
|
|
118
|
+
# your changes.
|
|
119
|
+
self.connection.commit()
|
|
120
|
+
|
|
121
|
+
success = True
|
|
122
|
+
except:
|
|
123
|
+
crash_report(f"-{NAME}: execute({sql}): failed.")
|
|
124
|
+
|
|
125
|
+
return (success, output) if returns_output else success
|
|
126
|
+
|
|
127
|
+
def insert(
|
|
128
|
+
self,
|
|
129
|
+
columns: List[str],
|
|
130
|
+
values: List[Any],
|
|
131
|
+
) -> bool:
|
|
132
|
+
return self.execute(
|
|
133
|
+
(
|
|
134
|
+
f"INSERT INTO {self.name}"
|
|
135
|
+
+ " ("
|
|
136
|
+
+ ", ".join(columns)
|
|
137
|
+
+ ") VALUES ("
|
|
138
|
+
+ ", ".join(len(columns) * ["%s"])
|
|
139
|
+
+ ")",
|
|
140
|
+
values,
|
|
141
|
+
),
|
|
142
|
+
commit=True,
|
|
143
|
+
returns_output=True,
|
|
144
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from bluer_objects.mysql.tags.functions import clone, create, get, search, set_
|