88##########################################################################
99
1010"""Utility functions for storing and retrieving user configuration settings."""
11-
11+ import os
1212import traceback
1313import json
1414
1515from flask import Response , request , render_template , url_for , current_app
1616from flask_babel import gettext
1717from flask_login import current_user
18+ from selenium .webdriver .support .expected_conditions import \
19+ element_selection_state_to_be
20+ from sqlalchemy import false
21+
1822from pgadmin .user_login_check import pga_login_required
1923from pgadmin .utils import PgAdminModule
2024from pgadmin .utils .ajax import make_json_response , bad_request ,\
@@ -262,26 +266,32 @@ def get_file_format_setting():
262266 info = get_file_type_setting (list (data .values ())))
263267
264268
269+
270+
265271@blueprint .route (
266272 '/save_application_state' ,
267273 methods = ["POST" ], endpoint = 'save_application_state'
268274)
269275@pga_login_required
270276def save_application_state ():
271277 """
272- Args:
273- sid: server id
274- did: database id
278+ Expose an api to save the application state which stores the data from
279+ query tool, ERD, schema-diff, psql
275280 """
276281 data = json .loads (request .data )
277- id = data ['trans_id' ]
282+ trans_id = data ['trans_id' ]
278283 fernet = Fernet (current_app .config ['SECRET_KEY' ].encode ())
279284 tool_data = fernet .encrypt (json .dumps (data ['tool_data' ]).encode ())
280285 connection_info = data ['connection_info' ] \
281286 if 'connection_info' in data else None
287+ if 'open_file_name' in connection_info and connection_info ['open_file_name' ] and 'is_editor_dirty' in connection_info and connection_info ['is_editor_dirty' ]:
288+ last_saved_file_hash = compute_sha256_large_file (connection_info ['open_file_name' ])
289+ print (last_saved_file_hash )
290+ connection_info ['last_saved_file_hash' ] = last_saved_file_hash
291+
282292 try :
283293 data_entry = ApplicationState (
284- uid = current_user .id , id = id ,connection_info = connection_info ,
294+ uid = current_user .id , id = trans_id ,connection_info = connection_info ,
285295 tool_name = data ['tool_name' ], tool_data = tool_data )
286296
287297 db .session .merge (data_entry )
@@ -304,6 +314,9 @@ def save_application_state():
304314)
305315@pga_login_required
306316def get_application_state ():
317+ """
318+ Returns application state if any stored.
319+ """
307320 fernet = Fernet (current_app .config ['SECRET_KEY' ].encode ())
308321 result = db .session \
309322 .query (ApplicationState ) \
@@ -312,8 +325,26 @@ def get_application_state():
312325
313326 res = []
314327 for row in result :
328+ connection_info = row .connection_info
329+ print (connection_info )
330+ if 'open_file_name' in connection_info and connection_info ['open_file_name' ]:
331+ file_path = connection_info ['open_file_name' ]
332+ file_deleted = False if os .path .exists (file_path ) else True
333+ connection_info ['file_deleted' ] = file_deleted
334+ if not file_deleted and connection_info ['is_editor_dirty' ]:
335+ if 'last_saved_file_hash' in connection_info and connection_info ['last_saved_file_hash' ]:
336+ connection_info ['external_file_changes' ] = check_external_file_changes (file_path , connection_info ['last_saved_file_hash' ])
337+
338+
339+ # if 'open_file_name' in connection_info and connection_info['open_file_name']:
340+ # initial_file_hash = connection_info['initial_file_hash']
341+ # file_deleted, file_modified_in_pgadmin, file_modified_externally = detect_file_change(connection_info['open_file_name'], tool_data, initial_file_hash )
342+ # connection_info['file_deleted'] = file_deleted
343+ # connection_info['file_modified_in_pgadmin'] = file_modified_in_pgadmin
344+ # connection_info['file_modified_externally'] = file_modified_externally
345+
315346 res .append ({'tool_name' : row .tool_name ,
316- 'connection_info' : row . connection_info ,
347+ 'connection_info' : connection_info ,
317348 'tool_data' : fernet .decrypt (row .tool_data ).decode (),
318349 'id' : row .id
319350 })
@@ -335,10 +366,16 @@ def delete_application_state():
335366 if request .data :
336367 data = json .loads (request .data )
337368 trans_id = int (data ['panelId' ].split ('_' )[- 1 ])
338- return delete_tool_data (trans_id )
369+ status , msg = delete_tool_data (trans_id )
370+ return make_json_response (
371+ data = {
372+ 'status' : status ,
373+ 'msg' : msg ,
374+ }
375+ )
339376
340377
341- def delete_tool_data (trans_id ):
378+ def delete_tool_data (trans_id = None ):
342379 try :
343380 if trans_id :
344381 results = db .session \
@@ -354,17 +391,66 @@ def delete_tool_data(trans_id):
354391 for result in results :
355392 db .session .delete (result )
356393 db .session .commit ()
357- return make_json_response (
358- data = {
359- 'status' : True ,
360- 'msg' : 'Success' ,
361- }
362- )
394+ return True , 'Success'
363395 except Exception as e :
364396 db .session .rollback ()
365- return make_json_response (
366- data = {
367- 'status' : False ,
368- 'msg' : str (e ),
369- }
370- )
397+ return False , str (e )
398+
399+
400+ import hashlib
401+
402+ def compute_sha256_large_data_in_memory (data ):
403+ """Hash large data (in-memory) by processing in chunks."""
404+ sha256_hash = hashlib .sha256 ()
405+ # Process data in 8 KB chunks
406+ string_data = json .loads (data )
407+ chunk_size = 8192
408+ for i in range (0 , len (string_data ), chunk_size ):
409+ chunk = string_data [i :i + chunk_size ]
410+ sha256_hash .update (chunk .encode ("utf-8" ))
411+
412+ return sha256_hash .hexdigest ()
413+
414+
415+ def compute_sha256_large_file (file_path ):
416+ """Compute SHA-256 hash for large files by reading in chunks."""
417+ sha256_hash = hashlib .sha256 ()
418+
419+ # Open the file in binary mode
420+ with open (file_path , "rb" ) as file :
421+ # Read and hash in 8 KB chunks (can adjust the chunk size if needed)
422+ for chunk in iter (lambda : file .read (8192 ), b"" ):
423+ print (chunk )
424+ sha256_hash .update (chunk )
425+
426+ return sha256_hash .hexdigest ()
427+
428+
429+ def detect_file_change (file_path , data , initial_file_hash ):
430+ file_deleted = False
431+ file_modified_in_pgadmin = False
432+ file_modified_externally = False
433+ if os .path .exists (file_path ):
434+ current_file_hash = compute_sha256_large_file (file_path )
435+ stored_data_hash = compute_sha256_large_data_in_memory (data )
436+ if stored_data_hash != current_file_hash :
437+ if stored_data_hash != initial_file_hash :
438+ # file changes in pgadmin
439+ file_modified_in_pgadmin = True
440+
441+ if current_file_hash != initial_file_hash :
442+ # file is changed externally
443+ file_modified_externally = True
444+
445+ else :
446+ file_deleted = True
447+ file_modified_in_pgadmin = True
448+ return file_deleted , file_modified_in_pgadmin , file_modified_externally
449+
450+
451+ def check_external_file_changes (file_path , last_saved_file_hash ):
452+ current_file_hash = compute_sha256_large_file (file_path )
453+ if current_file_hash != last_saved_file_hash :
454+ return True
455+ return False
456+
0 commit comments