Skip to content

Commit 3d3dabc

Browse files
Merge pull request #1 from GabrielTorelo/feat/ComparingSQLite
feat/ComparingSQLite - OK
2 parents 2d4d79f + 37cb8b0 commit 3d3dabc

17 files changed

+336
-131
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*$py.class
55

66
# Local use
7-
MOCK/*
87
Output.json
98

109
# Distribution / packaging

MOCK/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore directory contents
2+
/OUTPUT_DB/*

MOCK/DATA/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore everything in this directory
2+
*
3+
!.gitignore

MOCK/PATTERN/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore everything in this directory
2+
*
3+
!.gitignore

constants/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .general import *
1+
from .general import *
2+
from .sqlite_queries import *

constants/general.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
TRACKING_CODE = 'PRJ0000.X.X'
1+
TRACKING_CODE = "PRJ0000.X.X"
2+
DB_PATHS = "MOCK/DATA"
3+
PATTERN_PATH = "MOCK/PATTERN"
4+
PATTERN_PATH_JSON = "MOCK/PATTERN/padrao_db.json"
5+
OUTPUT_DB_PATH = "MOCK/OUTPUT_DB"
6+
EXCLUDED_TABLES = {"android_metadata", "sqlite_sequence"}
7+
EXCLUDED_COLUMNS = {""}

constants/sqlite_queries.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SELECT_TABLES = "SELECT name FROM sqlite_master WHERE type='table';"
2+
PRAGMA_TABLE_INFO = "PRAGMA table_info(%s);"

dao/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .db_sqlite import *

dao/db_sqlite.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from sqlite3 import connect
2+
from err import Errors
3+
from constants import SELECT_TABLES, EXCLUDED_TABLES, EXCLUDED_COLUMNS, PRAGMA_TABLE_INFO
4+
5+
6+
def list_tables(db_path):
7+
tables_info = {}
8+
9+
try:
10+
connection = connect(db_path)
11+
cursor = connection.cursor()
12+
13+
cursor.execute(SELECT_TABLES)
14+
tables = [row[0] for row in cursor.fetchall()]
15+
16+
tables = sorted(tables)
17+
18+
for table in tables:
19+
if table in EXCLUDED_TABLES:
20+
continue
21+
22+
cursor.execute(PRAGMA_TABLE_INFO % table)
23+
columns = cursor.fetchall()
24+
25+
tables_info[table] = sorted([
26+
{'nome': col[1], 'tipo': col[2]} for col in columns if col[1] not in EXCLUDED_COLUMNS
27+
], key=lambda x: x['nome'])
28+
29+
connection.close()
30+
return tables_info
31+
except Exception as e:
32+
raise Errors(code=6, message=f"Error listing database tables: {e}")

err/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .errors import *
1+
from .errors import *

err/errors.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
from constants import TRACKING_CODE
2-
3-
class Errors(Exception):
4-
def __init__(self, code: int, message: str):
5-
self.code = code
6-
self.message = message
7-
8-
def get_response(self):
9-
return {
10-
"trackingCode": f"{TRACKING_CODE}.{str(self.get_code).zfill(2)}",
11-
"message": self.get_message
12-
}
13-
14-
@property
15-
def get_code(self):
16-
return self.__code
17-
18-
@get_code.setter
19-
def code(self, code: int):
20-
self.__code = code
21-
22-
@property
23-
def get_message(self):
24-
return self.__message
25-
26-
@get_message.setter
27-
def message(self, message: str):
28-
self.__message = message
1+
from constants import TRACKING_CODE
2+
3+
4+
class Errors(Exception):
5+
def __init__(self, code: int, message: str):
6+
self.code = code
7+
self.message = message
8+
9+
def get_response(self):
10+
return {
11+
"trackingCode": f"{TRACKING_CODE}.{str(self.get_code).zfill(2)}",
12+
"message": self.get_message
13+
}
14+
15+
@property
16+
def get_code(self):
17+
return self.__code
18+
19+
@get_code.setter
20+
def code(self, code: int):
21+
self.__code = code
22+
23+
@property
24+
def get_message(self):
25+
return self.__message
26+
27+
@get_message.setter
28+
def message(self, message: str):
29+
self.__message = message

main.py

Lines changed: 43 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,43 @@
1-
from json import dumps, load, loads
2-
from os import listdir, path
3-
from err import Errors
4-
from utils import get_ok_response
5-
6-
7-
def load_data(file_name: str) -> dict:
8-
try:
9-
with open(file_name, 'r', encoding='utf-8') as file:
10-
data = load(file)
11-
12-
return data
13-
except FileNotFoundError:
14-
raise Errors(code=2, message=f"File {file_name} not found")
15-
except Exception as e:
16-
raise Errors(code=2, message=f"Error loading file {file_name}: {str(e)}")
17-
18-
def compare_with_default(data_default: dict, data: dict) -> tuple:
19-
missing_tables = []
20-
missing_data = {}
21-
22-
try:
23-
for table, schema in data_default.items():
24-
if table not in data:
25-
missing_tables.append(table)
26-
else:
27-
missing_columns = [
28-
col for col in schema if col not in data[table]
29-
]
30-
if missing_columns:
31-
missing_data[table] = missing_columns
32-
33-
return missing_tables, missing_data
34-
except Exception as e:
35-
raise Errors(code=4, message=f"Error comparing data: {str(e)}")
36-
37-
def compare_all_with_default(data_default: dict, *data_files: dict) -> dict:
38-
results = {}
39-
40-
try:
41-
for data in data_files:
42-
file_name = data.get('file_name', 'unknown').replace('.json', '')
43-
missing_tables, missing_data = compare_with_default(data_default, data)
44-
results[file_name] = {
45-
"Missing_Tables": missing_tables,
46-
"Missing_Data": missing_data
47-
}
48-
49-
return results
50-
except Exception as e:
51-
raise Errors(code=4, message=f"Error comparing all data: {str(e)}")
52-
53-
def read_json_files_from_directory(directory: str) -> list:
54-
data_files = []
55-
56-
try:
57-
for filename in listdir(directory):
58-
if filename.endswith('.json'):
59-
file_path = path.join(directory, filename)
60-
with open(file_path, 'r', encoding='utf-8') as file:
61-
data = load(file)
62-
data['file_name'] = filename
63-
data_files.append(data)
64-
65-
return data_files
66-
except FileNotFoundError:
67-
raise Errors(code=3, message=f"Directory {directory} not found")
68-
except Exception as e:
69-
raise Errors(code=3, message=f"Error reading files from directory {directory}: {str(e)}")
70-
71-
def write_json_output(data: dict) -> None:
72-
try:
73-
with open("Output.json", 'w') as file:
74-
file.write(dumps(data, indent=4))
75-
76-
return get_ok_response()
77-
except Exception as e:
78-
raise Errors(code=5, message=f"Error writing output file: {str(e)}")
79-
80-
def main():
81-
RESPONSE = None
82-
83-
try:
84-
default_data = load_data(file_name="MOCK/PATTERN/data_default.json")
85-
data_files = read_json_files_from_directory(directory="MOCK/DATA")
86-
87-
comparison_results = compare_all_with_default(default_data, *data_files)
88-
89-
RESPONSE = write_json_output(data=comparison_results)
90-
except Errors as e:
91-
RESPONSE = e.get_response()
92-
except Exception as e:
93-
RESPONSE = Errors(code=1, message=f"{str(e)}").get_response()
94-
finally:
95-
print(RESPONSE)
96-
97-
if __name__ == "__main__":
98-
main()
1+
from utils import generate_json, compare_all_with_default, write_json_output, load_data, read_json_files_from_directory
2+
from err import Errors
3+
4+
5+
def main():
6+
RESPONSE = None
7+
8+
try:
9+
# Gerar JSON de um único arquivo
10+
# generate_json()
11+
12+
# Gerar JSON de múltiplos arquivos
13+
generate_json(multiple_files=True)
14+
15+
# Gerar JSON de múltiplos arquivos de um mesmo banco
16+
# generate_json(multiple_db_files=True)
17+
18+
default_data = load_data()
19+
data_files = read_json_files_from_directory()
20+
comparison_results = compare_all_with_default(default_data, *data_files)
21+
22+
RESPONSE = write_json_output(data=comparison_results)
23+
except Errors as e:
24+
RESPONSE = e.get_response()
25+
except Exception as e:
26+
RESPONSE = Errors(code=1, message=f"{str(e)}").get_response()
27+
finally:
28+
print(RESPONSE)
29+
30+
if __name__ == '__main__':
31+
# RUNTIME TEST
32+
from time import time
33+
34+
start_time = time()
35+
# ---------------
36+
37+
main()
38+
39+
# RUNTIME TEST
40+
end_time = time()
41+
42+
print(f"\nTempo de execução: {round(end_time - start_time, 2)} segundos")
43+
# ---------------

utils/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
from .messages import *
1+
from .files_handler import *
2+
from .iterable_handler import *
3+
from .json_handler import *

utils/files_handler.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from os import path
2+
from json import load
3+
from err import Errors
4+
from constants import PATTERN_PATH_JSON
5+
from utils.json_handler import create_json_from_data
6+
7+
def load_data() -> dict:
8+
try:
9+
if not path.exists(PATTERN_PATH_JSON):
10+
create_json_from_data()
11+
12+
with open(PATTERN_PATH_JSON, 'r', encoding='utf-8') as file:
13+
data = load(file)
14+
15+
return data
16+
except FileNotFoundError:
17+
raise Errors(code=2, message=f"File {PATTERN_PATH_JSON} not found")
18+
except Errors as err:
19+
raise err
20+
except Exception as e:
21+
raise Errors(code=2, message=f"Error loading file {PATTERN_PATH_JSON}: {str(e)}")

utils/iterable_handler.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from os import walk, path
2+
from err import Errors
3+
from constants import EXCLUDED_COLUMNS, EXCLUDED_TABLES
4+
5+
6+
def find_db_files(root_dir) -> list:
7+
db_files = []
8+
9+
try:
10+
for dirpath, _, filenames in walk(root_dir):
11+
for filename in filenames:
12+
if filename.endswith('.db'):
13+
db_files.append(path.join(dirpath, filename))
14+
15+
return db_files
16+
17+
except Exception as e:
18+
raise Errors(code=8, message=f"Error finding db files: {str(e)}")
19+
20+
def compare_with_default(data_default: dict, data: dict) -> tuple:
21+
missing_tables = []
22+
missing_data = {}
23+
24+
try:
25+
for table, schema in data_default.items():
26+
if table in EXCLUDED_TABLES:
27+
continue
28+
29+
if table not in data:
30+
missing_tables.append(table)
31+
else:
32+
missing_columns = [
33+
col for col in schema if col not in data[table] and col['nome'] not in EXCLUDED_COLUMNS
34+
]
35+
36+
if missing_columns:
37+
missing_data[table] = missing_columns
38+
39+
missing_tables.sort()
40+
missing_data = {table: sorted(columns, key=lambda x: x['nome']) for table, columns in sorted(missing_data.items())}
41+
42+
return missing_tables, missing_data
43+
except Exception as e:
44+
raise Errors(code=4, message=f"Error comparing data: {str(e)}")
45+
46+
def compare_all_with_default(data_default: dict, *data_files: dict) -> dict:
47+
results = {}
48+
49+
try:
50+
for data in data_files:
51+
database_name = data.get("database_name", "unknown")
52+
file_name = data.get('file_name', 'unknown').replace('.json', '')
53+
54+
missing_tables, missing_data = compare_with_default(data_default, data)
55+
56+
if database_name not in results:
57+
results[database_name] = {}
58+
59+
results[database_name][file_name] = {
60+
"Missing_Tables": missing_tables,
61+
"Missing_Data": missing_data
62+
}
63+
64+
return {db: dict(sorted(results[db].items())) for db in sorted(results)}
65+
except Exception as e:
66+
raise Errors(code=4, message=f"Error comparing all data: {str(e)}")

0 commit comments

Comments
 (0)