SilverStreamingApplication 1.4__tar.gz → 1.6__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.
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/PKG-INFO +1 -1
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/Operations.py +86 -1
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/SilverMerge.py +10 -5
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/WorkflowBuilder.py +15 -3
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/setup.py +1 -1
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/silverstreamingapplication.egg-info/PKG-INFO +1 -1
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/Maintenance.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/PartitionAnalyzer.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/PerfAnalyzer.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/__init__.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/logging.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/sparksession.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/utils.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/utilsDatabricksAPI.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/OperationsTest.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/__init__.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/main_test.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/utils_test.py +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/setup.cfg +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/silverstreamingapplication.egg-info/SOURCES.txt +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/silverstreamingapplication.egg-info/dependency_links.txt +0 -0
- {silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/silverstreamingapplication.egg-info/top_level.txt +0 -0
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/Operations.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from pyspark.sql.utils import AnalysisException
|
|
2
2
|
from com.phida.main.sparksession import spark
|
|
3
3
|
from pyspark.sql.functions import col
|
|
4
|
+
from pyspark.sql.types import _parse_datatype_string, StructType, StructField
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def tableExists(dbName, tblName):
|
|
@@ -92,6 +93,89 @@ def hiveDDL(df):
|
|
|
92
93
|
|
|
93
94
|
return tblDDL
|
|
94
95
|
|
|
96
|
+
def describe_table_to_schema(desc_rows):
|
|
97
|
+
"""
|
|
98
|
+
Convert table description rows into a Spark StructType schema.
|
|
99
|
+
This function parses the output from a DESCRIBE TABLE command and organizes
|
|
100
|
+
the columns into a StructType schema, separating regular data fields from
|
|
101
|
+
partition fields.
|
|
102
|
+
Args:
|
|
103
|
+
desc_rows: An iterable of row objects containing table metadata.
|
|
104
|
+
Each row should have 'col_name' and 'data_type' attributes.
|
|
105
|
+
Returns:
|
|
106
|
+
StructType: A Spark StructType object representing the table schema,
|
|
107
|
+
with data fields listed first, followed by partition fields.
|
|
108
|
+
Example:
|
|
109
|
+
>>> desc_rows = [
|
|
110
|
+
... Row(col_name='id', data_type='bigint'),
|
|
111
|
+
... Row(col_name='name', data_type='string'),
|
|
112
|
+
... Row(col_name='# Partition Information', data_type=''),
|
|
113
|
+
... Row(col_name='date', data_type='date')
|
|
114
|
+
... ]
|
|
115
|
+
>>> schema = describe_table_to_schema(desc_rows)
|
|
116
|
+
"""
|
|
117
|
+
data_fields = []
|
|
118
|
+
partition_fields = []
|
|
119
|
+
|
|
120
|
+
in_partition_section = False
|
|
121
|
+
|
|
122
|
+
for r in desc_rows:
|
|
123
|
+
col = r.col_name
|
|
124
|
+
dtype = r.data_type
|
|
125
|
+
|
|
126
|
+
# Enter partition section
|
|
127
|
+
if col.startswith("# Partition Information"):
|
|
128
|
+
in_partition_section = True
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Skip header rows
|
|
132
|
+
if col.startswith("#"):
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Skip empty rows (defensive)
|
|
136
|
+
if not col or not dtype:
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
field = StructField(
|
|
140
|
+
col,
|
|
141
|
+
_parse_datatype(dtype),
|
|
142
|
+
True
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if in_partition_section:
|
|
146
|
+
partition_fields.append(field)
|
|
147
|
+
else:
|
|
148
|
+
data_fields.append(field)
|
|
149
|
+
|
|
150
|
+
return StructType(data_fields + partition_fields)
|
|
151
|
+
|
|
152
|
+
def _parse_datatype(dt):
|
|
153
|
+
"""
|
|
154
|
+
Parse a data type string and return the corresponding Spark data type.
|
|
155
|
+
|
|
156
|
+
This function serves as a wrapper that leverages Spark's internal parsing
|
|
157
|
+
mechanism to correctly handle complex data types including nested structures,
|
|
158
|
+
arrays, maps, and other composite types.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
dt (str): A string representation of a Spark data type
|
|
162
|
+
(e.g., 'IntegerType', 'StructType', 'ArrayType(StringType)')
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
pyspark.sql.types.DataType: The parsed Spark data type object corresponding
|
|
166
|
+
to the input string.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
ParseException: If the data type string is invalid or cannot be parsed.
|
|
170
|
+
|
|
171
|
+
Examples:
|
|
172
|
+
>>> _parse_datatype('IntegerType')
|
|
173
|
+
IntegerType
|
|
174
|
+
>>> _parse_datatype('ArrayType(StringType)')
|
|
175
|
+
ArrayType(StringType, containsNull=true)
|
|
176
|
+
"""
|
|
177
|
+
# Let Spark parse complex types correctly
|
|
178
|
+
return _parse_datatype_string(dt)
|
|
95
179
|
|
|
96
180
|
def hasColumn(df, columnName):
|
|
97
181
|
"""
|
|
@@ -299,9 +383,10 @@ def schemaDiff(df1, df2):
|
|
|
299
383
|
1. Make sure that the first dataframe has got more columns than the second
|
|
300
384
|
"""
|
|
301
385
|
df1_columns = df1.columns
|
|
386
|
+
df2_columns_lower = [col.lower() for col in df2.columns]
|
|
302
387
|
|
|
303
388
|
for columnName in df1.columns:
|
|
304
|
-
if columnName in
|
|
389
|
+
if columnName.lower() in df2_columns_lower:
|
|
305
390
|
df1_columns.remove(columnName)
|
|
306
391
|
|
|
307
392
|
df = df1.select(df1_columns)
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/SilverMerge.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from datetime import date, timedelta
|
|
1
2
|
from com.phida.main.sparksession import spark, logger
|
|
2
|
-
from com.phida.main.Operations import tableExists, addDerivedColumns, createDeltaTable, alterDeltaTable, \
|
|
3
|
+
from com.phida.main.Operations import describe_table_to_schema, tableExists, addDerivedColumns, createDeltaTable, alterDeltaTable, \
|
|
3
4
|
dropColumns, getDerivedColumnsList, schemaDiff, getKeyCols, buildJoinCondition, buildColumnsDict, hiveDDL, schemaDataTypeDiff
|
|
4
5
|
from com.phida.main.utils import pathExists, convertStrToList
|
|
5
6
|
from pyspark.sql.functions import row_number, col, broadcast,expr, max as spark_max
|
|
@@ -43,7 +44,7 @@ class SilverMerge:
|
|
|
43
44
|
"""
|
|
44
45
|
def __init__(self, srcDatabaseName, srcTableName, tgtDatabaseName, tgtTableName,
|
|
45
46
|
tgtCheckpoint, tgtTablePath, tgtPartitionColumns, derivedColExpr,
|
|
46
|
-
triggerOnce, dropColumnStr, pruneColumn, cluster_table_path, enable_delete_filter, use_auto_loader):
|
|
47
|
+
triggerOnce, dropColumnStr, pruneColumn, cluster_table_path, enable_delete_filter, use_auto_loader, modified_after=None, backfill_interval=None):
|
|
47
48
|
"""
|
|
48
49
|
desc:
|
|
49
50
|
Initialize the required class variables
|
|
@@ -80,7 +81,9 @@ class SilverMerge:
|
|
|
80
81
|
self.pruneColumn = pruneColumn
|
|
81
82
|
self.cluster_table_path = cluster_table_path
|
|
82
83
|
self.enable_delete_filter = enable_delete_filter.strip().upper() if enable_delete_filter else 'N'
|
|
83
|
-
self.use_auto_loader = use_auto_loader.strip().upper() if use_auto_loader and use_auto_loader.strip().upper()
|
|
84
|
+
self.use_auto_loader = use_auto_loader.strip().upper() if use_auto_loader and use_auto_loader.strip().upper() in ('Y') else 'N'
|
|
85
|
+
self.modified_after = modified_after.strip() if modified_after else (date.today() - timedelta(days=15)).strftime('%Y-%m-%d')
|
|
86
|
+
self.backfill_interval = backfill_interval if backfill_interval else "1 month"
|
|
84
87
|
|
|
85
88
|
logger.info(f"phida_log: Check if source table {self.srcDatabaseName}.{self.srcTableName} exists")
|
|
86
89
|
|
|
@@ -93,8 +96,10 @@ class SilverMerge:
|
|
|
93
96
|
if self.use_auto_loader == 'N':
|
|
94
97
|
self.srcDF = spark.readStream.table(self.srcDatabaseName + "." + self.srcTableName)
|
|
95
98
|
elif self.use_auto_loader == 'Y':
|
|
99
|
+
desc = spark.sql(f"DESCRIBE TABLE {self.srcDatabaseName}.{self.srcTableName}").collect()
|
|
100
|
+
schema = describe_table_to_schema(desc)
|
|
96
101
|
src_table_path = spark.sql(f"DESCRIBE DETAIL {self.srcDatabaseName}.{self.srcTableName}").collect()[0]['location']
|
|
97
|
-
self.srcDF = spark.readStream.format("cloudFiles").option("cloudFiles.format", "parquet").option("cloudFiles.schemaLocation", src_table_path + "/_cloudfiles/_schemas").load(src_table_path)
|
|
102
|
+
self.srcDF = spark.readStream.format("cloudFiles").option("cloudFiles.format", "parquet").option("cloudFiles.schemaLocation", src_table_path + "/_cloudfiles/_schemas").option("modifiedAfter", f"{self.modified_after} 00:00:00.000000 UTC+0").option("cloudFiles.backfillInterval", self.backfill_interval).schema(schema).load(src_table_path).drop("_rescued_data")
|
|
98
103
|
|
|
99
104
|
self.keyCols = getKeyCols(self.srcDatabaseName, self.srcTableName)
|
|
100
105
|
|
|
@@ -240,7 +245,7 @@ class SilverMerge:
|
|
|
240
245
|
|
|
241
246
|
filtered_df = (joined_df.filter(
|
|
242
247
|
(col("delete_hvr_integ_key").isNull()) |
|
|
243
|
-
(col("hvr_integ_key")
|
|
248
|
+
(col("hvr_integ_key") >= col("delete_hvr_integ_key"))
|
|
244
249
|
)
|
|
245
250
|
.drop("delete_hvr_integ_key"))
|
|
246
251
|
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/WorkflowBuilder.py
RENAMED
|
@@ -72,7 +72,7 @@ class WorkflowBuilder:
|
|
|
72
72
|
@staticmethod
|
|
73
73
|
def createTask(taskKey, jobClusterKey, notebookPath, srcDatabaseName, srcTableName, tgtDatabaseName, tgtTableName,
|
|
74
74
|
tgtCheckpoint, tgtTablePath, tgtPartitionColumns, derivedColExpr, triggerOnce, dropColumnStr,
|
|
75
|
-
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, library):
|
|
75
|
+
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, modified_after, backfill_interval, library):
|
|
76
76
|
"""
|
|
77
77
|
desc:
|
|
78
78
|
A method for creating the dict/json for a single task
|
|
@@ -96,6 +96,8 @@ class WorkflowBuilder:
|
|
|
96
96
|
cluster_table_path: String - The path of the table in the cluster
|
|
97
97
|
enable_delete_filter: String - Flag to enable/disable filtering of deleted records (default: 'N')
|
|
98
98
|
enable_autoloader: String - Flag to enable/disable autoloader (default: 'N')
|
|
99
|
+
modified_after: String - The modified after parameter for autoloader to read only the files modified after the specified timestamp (optional, format: "2026-01-01", "2025-06-01" etc.)
|
|
100
|
+
backfill_interval: String - The backfill interval for autoloader to read the historical data by looking back a certain interval from the current timestamp (optional, format: "1 month", "10 months", "15 days", "12 hours" etc.)
|
|
99
101
|
pythonWhlPath: String - Path of the python wheel file
|
|
100
102
|
library: String - Library to be installed for the job (with version number)
|
|
101
103
|
|
|
@@ -137,7 +139,9 @@ class WorkflowBuilder:
|
|
|
137
139
|
"drop_column_list": dropColumnStr,
|
|
138
140
|
"cluster_table_path": cluster_table_path,
|
|
139
141
|
"enable_delete_filter": str(enable_delete_filter),
|
|
140
|
-
"enable_autoloader": enable_autoloader
|
|
142
|
+
"enable_autoloader": enable_autoloader,
|
|
143
|
+
"modified_after": modified_after,
|
|
144
|
+
"backfill_interval": backfill_interval
|
|
141
145
|
}
|
|
142
146
|
},
|
|
143
147
|
"timeout_seconds": timeout_seconds,
|
|
@@ -348,11 +352,19 @@ class WorkflowBuilder:
|
|
|
348
352
|
enable_autoloader = task['enable_autoloader']
|
|
349
353
|
except:
|
|
350
354
|
enable_autoloader = ""
|
|
355
|
+
try:
|
|
356
|
+
modified_after = task['modified_after']
|
|
357
|
+
except:
|
|
358
|
+
modified_after = ""
|
|
359
|
+
try:
|
|
360
|
+
backfill_interval = task['backfill_interval']
|
|
361
|
+
except:
|
|
362
|
+
backfill_interval = ""
|
|
351
363
|
|
|
352
364
|
taskTemp = self.createTask(taskKey, jobClusterKey, notebookPath, srcDatabaseName, srcTableName,
|
|
353
365
|
tgtDatabaseName, tgtTableName, tgtCheckpoint, tgtTablePath,
|
|
354
366
|
tgtPartitionColumns, derivedColExpr, triggerOnce, dropColumnStr,
|
|
355
|
-
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, self.library)
|
|
367
|
+
pruneColumn, cluster_table_path, enable_delete_filter, enable_autoloader, modified_after, backfill_interval, self.library)
|
|
356
368
|
|
|
357
369
|
tasks.append(taskTemp)
|
|
358
370
|
if jobId is None:
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/Maintenance.py
RENAMED
|
File without changes
|
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/PerfAnalyzer.py
RENAMED
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/main/sparksession.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/OperationsTest.py
RENAMED
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/__init__.py
RENAMED
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/main_test.py
RENAMED
|
File without changes
|
{silverstreamingapplication-1.4 → silverstreamingapplication-1.6}/com/phida/tests/utils_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|