rtgl 0.0.3__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.
Files changed (40) hide show
  1. rtgl/__init__.py +9 -0
  2. rtgl/base/__init__.py +6 -0
  3. rtgl/base/database.py +57 -0
  4. rtgl/base/table.py +65 -0
  5. rtgl/converter/__init__.py +7 -0
  6. rtgl/converter/converter.py +484 -0
  7. rtgl/converter/static_converter.py +250 -0
  8. rtgl/converter/temporal_converter.py +565 -0
  9. rtgl/converter/utils.py +117 -0
  10. rtgl/parser/.antlr/LexerRTGL.interp +156 -0
  11. rtgl/parser/.antlr/LexerRTGL.java +670 -0
  12. rtgl/parser/.antlr/LexerRTGL.tokens +50 -0
  13. rtgl/parser/.antlr/ParserRTGL.interp +121 -0
  14. rtgl/parser/.antlr/ParserRTGL.java +1743 -0
  15. rtgl/parser/.antlr/ParserRTGL.tokens +50 -0
  16. rtgl/parser/.antlr/ParserRTGLBaseListener.java +303 -0
  17. rtgl/parser/.antlr/ParserRTGLListener.java +229 -0
  18. rtgl/parser/LexerRTGL.g4 +252 -0
  19. rtgl/parser/ParserRTGL.g4 +134 -0
  20. rtgl/parser/__init__.py +7 -0
  21. rtgl/parser/gen/LexerRTGL.interp +156 -0
  22. rtgl/parser/gen/LexerRTGL.py +415 -0
  23. rtgl/parser/gen/LexerRTGL.tokens +50 -0
  24. rtgl/parser/gen/ParserRTGL.interp +121 -0
  25. rtgl/parser/gen/ParserRTGL.py +1911 -0
  26. rtgl/parser/gen/ParserRTGL.tokens +50 -0
  27. rtgl/parser/gen/ParserRTGLListener.py +210 -0
  28. rtgl/parser/gen/ParserRTGLVisitor.py +123 -0
  29. rtgl/validator/__init__.py +8 -0
  30. rtgl/validator/error.py +124 -0
  31. rtgl/validator/static_validator.py +132 -0
  32. rtgl/validator/temporal_validator.py +229 -0
  33. rtgl/validator/validator.py +458 -0
  34. rtgl/visitor/__init__.py +6 -0
  35. rtgl/visitor/parsed_value.py +32 -0
  36. rtgl/visitor/visitor.py +531 -0
  37. rtgl-0.0.3.dist-info/METADATA +227 -0
  38. rtgl-0.0.3.dist-info/RECORD +40 -0
  39. rtgl-0.0.3.dist-info/WHEEL +4 -0
  40. rtgl-0.0.3.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,250 @@
1
+ """Static RTGL converter class for non-temporal queries."""
2
+
3
+ from rtgl.base import Database, Table
4
+ from rtgl.converter.converter import Converter
5
+ from rtgl.converter.utils import get_div_line
6
+ from rtgl.validator import SValidator
7
+
8
+
9
+ class SConverter(Converter):
10
+ r"""Static RTGL converter class for static conversion RTGL -> SQL.
11
+
12
+ Converts static (non-temporal) RTGL queries into SQL queries.
13
+ Extends the base Converter class with concrete implementations
14
+ for static prediction tasks.
15
+ """
16
+
17
+ def __init__(self, db: Database) -> None:
18
+ r"""Initializes a static RTGL converter.
19
+
20
+ Args:
21
+ db (Database): Database object containing the tables.
22
+
23
+ Returns:
24
+ out (None):
25
+ """
26
+ super().__init__(db)
27
+ # initialize static validator
28
+ self.validator = SValidator(self.collector, self.db)
29
+
30
+ def convert(self, rtgl_query: str, execute: bool = False) -> str | Table:
31
+ r"""Converts the static RTGL query string into an executable SQL query.
32
+
33
+ Args:
34
+ rtgl_query (str): The RTGL query string to be converted and executed.
35
+ execute (bool): If True, executes the generated SQL query and returns the result as a Table.
36
+
37
+ Returns:
38
+ out (str | Table): The *`Table`* object containing the result of the executed SQL query (if execute=True),
39
+ with columns (*fk*, *label*) corresponding to the translated RTGL query output.
40
+ Otherwise, returns the generated SQL query string (if execute=False).
41
+ """
42
+ # parse RTGL query into dictionary
43
+ query_dict = self.parse_query(rtgl_query)
44
+ query_dict = query_dict["QueryStat"].value
45
+
46
+ # build FOR EACH query
47
+ for_each_dict = query_dict["ForEach"].value
48
+ ptable, ppk, for_each_query = self.build_for_each(for_each_dict)
49
+
50
+ # build PREDICT query using FOR EACH query as base
51
+ predict_dict = query_dict["Predict"].value
52
+ sql_query = self.build_predict(predict_dict, ptable, ppk, for_each_query)
53
+
54
+ # build WHERE query if exists, using PREDICT query as base
55
+ if where := query_dict["Where"]:
56
+ where_dict = where.value
57
+ sql_query = self.build_where(where_dict, ptable, ppk, sql_query)
58
+
59
+ # fiter and add semicolon to end of SQL query
60
+ label_fk = None
61
+ select_clause = "*"
62
+ filt = "label IS NOT NULL"
63
+ if aggr := predict_dict["Aggregation"]:
64
+ aggr_dict = aggr.value
65
+ if aggr_dict["AggrType"].value.lower() == "list_distinct":
66
+ filt = f"{filt} AND label != [NULL]"
67
+ select_clause = "fk, list_filter(label, x -> x IS NOT NULL) AS label"
68
+ table, table_obj = self._find_table(aggr_dict["Table"].value)
69
+ column = self._find_column(table, aggr_dict["Column"].value)
70
+
71
+ label_fk = table if table_obj.pkey_col == column else table_obj.fkey_col_to_pkey_table.get(column)
72
+
73
+ sql_query = f"SELECT\n {select_clause}\nFROM\n ({sql_query}\n)\nWHERE {filt}\nORDER BY fk ASC\n;\n"
74
+
75
+ if not execute:
76
+ return sql_query
77
+
78
+ self._register_db()
79
+
80
+ ptable_orig, _ = self._find_table(ptable)
81
+
82
+ # execute SQL query and return result as Table
83
+ df = self.conn.sql(sql_query).df()
84
+ fkey_col_to_pkey_table = {"fk": ptable_orig} # fk column in output table corresponds to pk of parent table
85
+ if label_fk: # label column in output table corresponds to pk or fk of aggregation table
86
+ fkey_col_to_pkey_table["label"] = label_fk # if aggregarion operation is LIST_DISTINCT
87
+
88
+ return Table(
89
+ df=df,
90
+ fkey_col_to_pkey_table=fkey_col_to_pkey_table,
91
+ pkey_col=None,
92
+ time_col=None,
93
+ )
94
+
95
+ def build_for_each(self, for_each_dict: dict) -> tuple[str, str, str]:
96
+ r"""Builds a SQL query for the FOR EACH clause in static conversion.
97
+
98
+ Args:
99
+ for_each_dict (dict): Parsed dictionary of the FOR EACH clause.
100
+
101
+ Returns:
102
+ ptable (str): Name of the parent table.
103
+ ppk (str): Name of the primary key column in the parent table.
104
+ for_each_query (str): SQL subquery returning the foreign keys of
105
+ the parent table (optionally filtered).
106
+ """
107
+ # extract parent table and primary key column
108
+ ptable = ptable_name = for_each_dict["Table"].value
109
+ ppk = self._find_column(ptable, for_each_dict["Column"].value)
110
+
111
+ # build static WHERE query if exists to filter parent table rows before prediction
112
+ if where := for_each_dict["Where"]:
113
+ ptable = self.build_stat_where(where.value, ptable, ppk)
114
+ ptable = ptable.replace("\n", "\n" + 4 * " ") + "\n"
115
+ ptable = f"({ptable})"
116
+
117
+ # create division markers for formatted output
118
+ div_line1 = get_div_line("FOR_EACH_START")
119
+ div_line2 = get_div_line("FOR_EACH_END")
120
+
121
+ # build final FOR EACH query
122
+ for_each_query = f"{div_line1}\nSELECT\n {ppk} AS fk\nFROM\n {ptable}\n{div_line2}"
123
+
124
+ return ptable_name, ppk, for_each_query
125
+
126
+ def build_predict(self, query_dict: dict, ptable: str, ppk: str, for_each_query: str) -> str:
127
+ r"""Builds a SQL query for the PREDICT clause in static conversion.
128
+
129
+ Args:
130
+ query_dict (dict): Parsed dictionary of the PREDICT clause.
131
+ ptable (str): Name of the parent table.
132
+ ppk (str): Name of the primary key column in the parent table.
133
+ for_each_query (str): SQL subquery from the FOR_EACH WHERE clause, providing base fk column.
134
+
135
+ Returns:
136
+ predict_query (str): SQL subquery returning (fk, label) pairs.
137
+ """
138
+ # check predict type, build main_query and label_query accordingly
139
+ # expr / id_dot_id
140
+ pred_type = query_dict["PredType"]
141
+ if pred_type == "aggregation":
142
+ main_query = self.build_aggregation(query_dict["Aggregation"].value, ptable, ppk)
143
+ label_query = "__MAIN__.comp_col"
144
+ if pred_type == "expr":
145
+ main_query = self.build_expr(query_dict["Expr"].value, ptable, ppk)
146
+ label_query = "CASE\n WHEN __MAIN__.fk IS NOT NULL THEN TRUE\n ELSE FALSE\nEND"
147
+ elif pred_type == "id_dot_id":
148
+ main_query = self.build_id_dot_id(query_dict, ptable, ppk)
149
+ label_query = "__MAIN__.comp_col"
150
+ else:
151
+ pass
152
+
153
+ main_query = main_query.replace("\n", "\n" + 4 * " ") + "\n"
154
+ label_query = label_query.replace("\n", "\n" + 4 * " ") + "\n"
155
+ for_each_query = for_each_query.replace("\n", "\n" + 4 * " ") + "\n"
156
+
157
+ # create division markers for formatted output
158
+ div_line_pred1 = get_div_line("PREDICT_START")
159
+ div_line_pred2 = get_div_line("PREDICT_END")
160
+
161
+ # build final PREDICT query
162
+ predict_query = (
163
+ f"{div_line_pred1}\n"
164
+ "SELECT\n"
165
+ " __FOR_EACH__.fk AS fk,\n"
166
+ f" {label_query} AS label\n"
167
+ "FROM\n"
168
+ f" ({for_each_query}) __FOR_EACH__\n"
169
+ "LEFT JOIN\n"
170
+ f" ({main_query}) __MAIN__\n"
171
+ "ON\n"
172
+ " __MAIN__.fk = __FOR_EACH__.fk\n"
173
+ f"{div_line_pred2}"
174
+ )
175
+
176
+ return predict_query
177
+
178
+ def build_expr(self, expr_dict: dict, ptable: str, ppk: str) -> str:
179
+ r"""Builds a SQL query for a logical expression tree.
180
+
181
+ Just uses existing *build_stat_expr* method from base *`Converter`* class.
182
+
183
+ Args:
184
+ expr_dict (dict): Parsed dictionary of the expression (can contain 'Op',
185
+ 'Left', 'Right' keys or a single condition).
186
+ ptable (str): Name of the parent table.
187
+ ppk (str): Name of the primary key column in the parent table.
188
+
189
+ Returns:
190
+ expr_query (str): SQL query returning foreign keys where the expression is true.
191
+ """
192
+ expr_query = self.build_stat_expr(expr_dict, ptable, ppk)
193
+
194
+ return expr_query
195
+
196
+ def build_where(self, where_dict: dict, ptable: str, ppk: str, predict_query: str) -> str:
197
+ r"""Builds a SQL query for the WHERE clause in static conversion.
198
+
199
+ Combines the PREDICT query with the expression from the WHERE clause using JOIN
200
+ to filter the predicted foreign keys based on the expression.
201
+
202
+ Args:
203
+ where_dict (dict): Parsed dictionary of the WHERE clause.
204
+ ptable (str): Name of the parent table.
205
+ ppk (str): Name of the primary key column in the parent table.
206
+ predict_query (str): SQL query from the PREDICT clause, providing fk and label columns.
207
+
208
+ Returns:
209
+ where_query (str): SQL query returning (fk, label) pairs filtered by the WHERE expression.
210
+ """
211
+ expr_query = self.build_expr(where_dict["Expr"].value, ptable, ppk)
212
+ expr_query = expr_query.replace("\n", "\n" + 4 * " ") + "\n"
213
+
214
+ # create division markers for formatted output
215
+ div_line1 = get_div_line("WHERE_START")
216
+ div_line2 = get_div_line("WHERE_END")
217
+
218
+ where_query = (
219
+ f"{div_line1}\n"
220
+ "SELECT\n"
221
+ " *\n"
222
+ "FROM\n"
223
+ f" ({predict_query}\n) __PREDICT__\n"
224
+ "JOIN\n"
225
+ f" ({expr_query}\n) __EXPR__\n"
226
+ "ON\n"
227
+ " __PREDICT__.fk = __EXPR__.fk\n"
228
+ "ORDER BY\n"
229
+ " __PREDICT__.fk ASC\n"
230
+ f"{div_line2}"
231
+ )
232
+
233
+ return where_query
234
+
235
+ def build_aggregation(self, aggr_dict: dict, ptable: str, ppk: str) -> str:
236
+ r"""Builds a SQL query for a static RTGL aggregation.
237
+
238
+ Just uses existing *build_stat_aggregation* method from base *`Converter`* class.
239
+
240
+ Args:
241
+ aggr_dict (dict): Parsed aggregation dictionary containing 'Table', 'Column', 'Where'(optional) keys.
242
+ ptable (str): Name of the parent table.
243
+ ppk (str): Name of the primary key column in the parent table.
244
+
245
+ Returns:
246
+ aggr_query (str): SQL query returning pairs (fk, comp_col).
247
+ """
248
+ aggr_query = self.build_stat_aggregation(aggr_dict, ptable, ppk)
249
+
250
+ return aggr_query