-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathdb_services.py
More file actions
237 lines (205 loc) · 7.94 KB
/
Copy pathdb_services.py
File metadata and controls
237 lines (205 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import logging
from collections import defaultdict
from typing import Any, Dict, List, Tuple
from airflow.providers.postgres.hooks.postgres import PostgresHook
from libs.enums import JobStageType, StageStatusType
from libs.queries import create_fields_query
from libs.settings import AIRFLOW_DAGRUN_TIMEOUT, AIRFLOW_DEBUG_MODE
from libs.utils import update_job_status
from openpyxl.worksheet.worksheet import Worksheet
# PostgreSQL connection hook
pg_hook = PostgresHook(
postgres_conn_id="postgres_db_conn",
options=f"-c statement_timeout={float(AIRFLOW_DAGRUN_TIMEOUT) * 60 * 1000}ms",
)
def create_field_entries(
worksheet: Worksheet, table_pairs: List[Tuple[str, int]]
) -> None:
"""
Creates field entries in the database for a scan report table.
Args:
row: The worksheet row containing field data
scan_report_table_id: The ID of the scan report table
Returns:
The ID of the newly created field
"""
try:
previous_row_value = None
for row in worksheet.iter_rows(min_row=2, max_row=worksheet.max_row + 2):
# Guard against unnecessary rows beyond the last true row with contents
if (previous_row_value is None or previous_row_value == "") and (
row[0].value is None or row[0].value == ""
):
break
previous_row_value = row[0].value
# If the row is not empty, then it is a field in a table
if row[0].value != "" and row[0].value is not None:
current_table_name = row[0].value
# table_pair[0] is table name, table_pair[1] is table id
table = next(
table_pair
for table_pair in table_pairs
if table_pair[0] == current_table_name
)
# Extract values from the row, handling possible None values
field_name = str(row[1].value) if row[1].value is not None else ""
description = str(row[2].value) if row[2].value is not None else ""
type_column = str(row[3].value) if row[3].value is not None else ""
pg_hook.run(
create_fields_query,
parameters={
# table[1] is table id
"scan_report_table_id": table[1],
"name": field_name, # NOTE: without BOM removal to keep the consistency with Azure functions
"description_column": description,
"type_column": type_column,
},
)
except Exception as e:
logging.error(f"Error creating field entry: {str(e)}")
raise e
def update_temp_data_dictionary_table(
data_dictionary: Dict[str, Dict[str, Dict[str, str]]], scan_report_id: int
) -> None:
"""
Updates the temporary table to store data dictionary information.
Args:
data_dictionary: A dictionary of data dictionary information
scan_report_id: The ID of the scan report
Returns:
None
"""
# Skip updating the temporary table if no data dictionary provided
if not data_dictionary:
logging.info(
"No data dictionary (4 items list) available, skipping dictionary table creation"
)
return
try:
# Prepare data for insertion
dictionary_records = []
for table_name, fields in data_dictionary.items():
for field_name, values in fields.items():
for value, description in values.items():
dictionary_records.append(
{
"table_name": table_name,
"field_name": field_name,
"value": value,
"value_description": description,
}
)
# Insert records into the temporary table
if dictionary_records:
pg_hook.insert_rows(
table=f"temp_data_dictionary_{scan_report_id}",
rows=[
(
d["table_name"],
d["field_name"],
d["value"],
d["value_description"],
)
for d in dictionary_records
],
target_fields=[
"table_name",
"field_name",
"value",
"value_description",
],
)
logging.info(
f"Created temporary data dictionary table with {len(dictionary_records)} records"
)
except Exception as e:
logging.error(f"Error creating data dictionary table: {str(e)}")
update_job_status(
stage=JobStageType.UPLOAD_SCAN_REPORT,
status=StageStatusType.FAILED,
scan_report=scan_report_id,
details=f"Upload failed: {str(e)}",
)
raise e
def create_temp_field_values_table(
field_values_dict: defaultdict[Any, List], table_id: int
) -> None:
"""
Creates a temporary table to store field values and their frequencies.
Args:
field_values_dict: A dictionary of field values and their frequencies
table_id: The ID of the table
Returns:
None
"""
if not field_values_dict:
logging.info("No field-values available, skipping field values table creation")
return
try:
# Create temp table to store field value frequencies
pg_hook.run(
"""
CREATE TABLE IF NOT EXISTS temp_field_values_%(table_id)s (
field_name VARCHAR(255),
value TEXT,
frequency INTEGER
)
""",
parameters={"table_id": table_id},
)
# Insert field values data into temp table
field_values_data = []
for field_name, values in field_values_dict.items():
for value, frequency in values:
# TODO: confirm about frequency of "List truncated..."
# Convert empty strings or None to 0 for frequency
if frequency == "" or frequency is None:
frequency = 0
# Ensure frequency is an integer
try:
frequency = int(frequency)
except (ValueError, TypeError):
frequency = 0
field_values_data.append(
{
"field_name": field_name,
"value": value,
"frequency": frequency,
}
)
if field_values_data:
pg_hook.insert_rows(
table=f"temp_field_values_{table_id}",
rows=[
(
d["field_name"],
d["value"],
d["frequency"],
)
for d in field_values_data
],
target_fields=[
"field_name",
"value",
"frequency",
],
)
except Exception as e:
logging.error(f"Error creating data dictionary table: {str(e)}")
raise e
def delete_temp_tables(scan_report_id: int, table_pairs: List[Tuple[str, int]]) -> None:
"""
Deletes the temporary tables for a scan report.
Args:
scan_report_id: The ID of the scan report
table_pairs: A list of tuples containing the table name and ID
"""
try:
if AIRFLOW_DEBUG_MODE == "true":
return
pg_hook.run(f"DROP TABLE IF EXISTS temp_data_dictionary_{scan_report_id}")
for _, table_id in table_pairs:
pg_hook.run(f"DROP TABLE IF EXISTS temp_field_values_{table_id}")
except Exception as e:
logging.error(f"Error deleting temporary tables: {str(e)}")
raise e