20
20
from selenium .webdriver .remote .webdriver import WebDriver
21
21
from selenium .webdriver .support .expected_conditions import (
22
22
invisibility_of_element ,
23
+ text_to_be_present_in_element ,
23
24
visibility_of_element_located ,
24
25
)
25
26
from selenium .webdriver .support .ui import Select , WebDriverWait
@@ -166,27 +167,36 @@ def switch_to_frame(self, frame: str) -> Generator[None, None, None]:
166
167
167
168
@alert_handler
168
169
@exception_retry_skip_handler
169
- def wait (self ) -> None :
170
+ def wait (self ) -> bool :
170
171
"""
171
172
Wait until the loading circle in the targeted site disappears.
173
+ If the frontend locks up while loading, this function will attempt to resolve this
174
+ with a page refresh (and the return value indicates if a refresh occurred).
172
175
"""
173
176
177
+ wait_timeout_seconds = 30
174
178
logging .debug ("Waiting until MPC loading circle disappears..." )
175
179
try :
176
180
wait_elem = self .driver .find_element (by = By .ID , value = "sysdiv_wait" )
177
181
# Wait for the element to become invisible
178
182
while True :
179
183
try :
180
- WebDriverWait (self .driver , 100 , poll_frequency = 0.1 ).until (invisibility_of_element (wait_elem ))
184
+ WebDriverWait (self .driver , wait_timeout_seconds , poll_frequency = 0.1 ).until (
185
+ invisibility_of_element (wait_elem )
186
+ )
181
187
except sl_exc .TimeoutException :
182
- logging .debug ("Timed out while waiting for the loading circle to disappear." )
183
- continue
188
+ logging .info (
189
+ f"Waited for longer than { wait_timeout_seconds } s for the { self .target_site .name } page "
190
+ f"to respond - attempting to resolve with a page refresh..."
191
+ )
192
+ self .driver .refresh ()
193
+ return True
184
194
logging .debug ("The loading circle has disappeared!" )
185
195
break
186
196
except (sl_exc .NoSuchElementException , sl_exc .NoSuchFrameException , sl_exc .WebDriverException ) as e :
187
197
logging .debug ("Attempted to locate the loading circle but encountered an exception:" )
188
198
logging .debug (e )
189
- return
199
+ return False
190
200
191
201
def set_state (self , state : str , action : Optional [str ] = None ) -> None :
192
202
self .state = state
@@ -373,7 +383,7 @@ def upload_image(self, image: CardImage, max_tries: int = 3) -> Optional[str]:
373
383
return None
374
384
375
385
@exception_retry_skip_handler
376
- def insert_image (self , pid : Optional [str ], image : CardImage , slots : list [int ]) -> None :
386
+ def insert_image (self , pid : Optional [str ], image : CardImage , slots : list [int ], max_tries : int = 3 ) -> None :
377
387
"""
378
388
Inserts the image identified by `pid` into `slots`.
379
389
"""
@@ -382,15 +392,28 @@ def insert_image(self, pid: Optional[str], image: CardImage, slots: list[int]) -
382
392
self .wait_until_javascript_object_is_defined ("PageLayout.prototype.applyDragPhoto" )
383
393
384
394
if pid :
385
- logging .debug (f" Inserting { image .name } into slots { slots } ..." )
395
+ logging .debug (f' Inserting " { image .name } " into slots { slots } ...' )
386
396
for i , slot in enumerate (slots , start = 1 ):
387
397
logging .debug (f"Inserting into slot { slot } ..." )
388
- self .set_state (state = self .state , action = f"Inserting { image .name } into slot { slot + 1 } ({ i } /{ len (slots )} )" )
389
- # Insert the card into each slot and wait for the page to load before continuing
390
- self .execute_javascript (
391
- f'PageLayout.prototype.applyDragPhoto({ self .get_element_for_slot_js (slot )} , 0, "{ pid } ")'
398
+ self .set_state (
399
+ state = self .state , action = f'Inserting "{ image .name } " into slot { slot + 1 } ({ i } /{ len (slots )} )'
392
400
)
393
- self .wait ()
401
+ # Insert the card into each slot and wait for the page to load before continuing
402
+ tries = 0
403
+ page_refreshed_while_inserting_image = True
404
+ while page_refreshed_while_inserting_image :
405
+ self .execute_javascript (
406
+ f'PageLayout.prototype.applyDragPhoto({ self .get_element_for_slot_js (slot )} , 0, "{ pid } ")'
407
+ )
408
+
409
+ page_refreshed_while_inserting_image = self .wait ()
410
+ tries += 1
411
+ if tries >= max_tries :
412
+ logging .warning (
413
+ f"Attempted to insert image { bold (image .name )} { max_tries } times, "
414
+ f"but no attempt succeeded! Skipping this image."
415
+ )
416
+ break
394
417
logging .debug (f"All done inserting { image .name } into slots { slots } !" )
395
418
self .set_state (self .state )
396
419
else :
@@ -624,6 +647,20 @@ def save_project_to_user_account(self, order: CardOrder) -> None:
624
647
self .wait_until_javascript_object_is_defined ("oDesign.setTemporarySave" )
625
648
self .execute_javascript ("oDesign.setTemporarySave();" )
626
649
self .wait ()
650
+
651
+ wait_timeout_seconds = 15
652
+ try :
653
+ WebDriverWait (self .driver , wait_timeout_seconds ).until (
654
+ text_to_be_present_in_element (
655
+ (By .ID , "div_temporarysavestatus" ), self .target_site .value .saved_successfully_text
656
+ )
657
+ )
658
+ except sl_exc .TimeoutException :
659
+ logging .info (
660
+ f"Waited for longer than { wait_timeout_seconds } s for the { self .target_site .name } page to respond - "
661
+ "attempting to resolve with a page refresh..."
662
+ )
663
+ self .driver .refresh ()
627
664
logging .debug ("Finished saving the project to the user's account!" )
628
665
629
666
# endregion
@@ -775,7 +812,7 @@ def execute_orders(
775
812
auto_save_threshold : Optional [int ],
776
813
post_processing_config : Optional [ImagePostProcessingConfig ],
777
814
) -> None :
778
- logging .info (f"{ bold (len (orders ))} projects are scheduled to be auto-filled. They are:" )
815
+ logging .info (f"{ bold (len (orders ))} project/s are scheduled to be auto-filled. They are:" )
779
816
for i , order in enumerate (orders , start = 1 ):
780
817
logging .info (f"{ i } . { bold (order .name or 'Unnamed Project' )} " )
781
818
logging .info (" " + order .get_overview ())
0 commit comments