@@ -609,11 +609,12 @@ def set_cwa_settings():
609609 boolean_settings = []
610610 string_settings = []
611611 list_settings = []
612- integer_settings = ['ingest_timeout_minutes' , 'auto_send_delay_minutes' , 'duplicate_scan_hour' , 'duplicate_scan_chunk_size' , 'duplicate_scan_debounce_seconds' ] # Special handling for integer settings
612+ integer_settings = ['ingest_timeout_minutes' , 'auto_send_delay_minutes' , 'hardcover_auto_fetch_batch_size' , 'hardcover_auto_fetch_schedule_hour' , 'duplicate_scan_hour' , 'duplicate_scan_chunk_size' , 'duplicate_scan_debounce_seconds' ] # Special handling for integer settings
613+ float_settings = ['hardcover_auto_fetch_min_confidence' , 'hardcover_auto_fetch_rate_limit' ] # Special handling for float settings
613614 json_settings = ['metadata_provider_hierarchy' , 'metadata_providers_enabled' , 'duplicate_format_priority' ] # Special handling for JSON settings
614615
615616 for setting in cwa_default_settings :
616- if setting in integer_settings or setting in json_settings :
617+ if setting in integer_settings or setting in float_settings or setting in json_settings :
617618 continue # Handle separately
618619 elif isinstance (cwa_default_settings [setting ], int ):
619620 boolean_settings .append (setting )
@@ -684,11 +685,15 @@ def set_cwa_settings():
684685 if value is not None :
685686 try :
686687 int_value = int (value )
687- # Validate timeout range
688+ # Validate range
688689 if setting == 'ingest_timeout_minutes' :
689690 int_value = max (5 , min (120 , int_value )) # Clamp between 5 and 120 minutes
690691 elif setting == 'auto_send_delay_minutes' :
691692 int_value = max (1 , min (60 , int_value )) # Clamp between 1 and 60 minutes
693+ elif setting == 'hardcover_auto_fetch_batch_size' :
694+ int_value = max (10 , min (200 , int_value )) # Clamp between 10 and 200
695+ elif setting == 'hardcover_auto_fetch_schedule_hour' :
696+ int_value = max (0 , min (23 , int_value )) # Clamp between 0 and 23 hours
692697 elif setting == 'duplicate_scan_hour' :
693698 int_value = max (0 , min (23 , int_value ))
694699 elif setting == 'duplicate_scan_chunk_size' :
@@ -702,16 +707,49 @@ def set_cwa_settings():
702707 result [setting ] = cwa_db .cwa_settings .get (setting , 15 ) # Default to 15 minutes
703708 elif setting == 'auto_send_delay_minutes' :
704709 result [setting ] = cwa_db .cwa_settings .get (setting , 5 ) # Default to 5 minutes
710+ elif setting == 'hardcover_auto_fetch_batch_size' :
711+ result [setting ] = cwa_db .cwa_settings .get (setting , 50 ) # Default to 50
712+ elif setting == 'hardcover_auto_fetch_schedule_hour' :
713+ result [setting ] = cwa_db .cwa_settings .get (setting , 2 ) # Default to 2 AM
705714 elif setting == 'duplicate_scan_debounce_seconds' :
706715 result [setting ] = cwa_db .cwa_settings .get (setting , 30 )
707716 else :
708717 if setting == 'ingest_timeout_minutes' :
709718 result [setting ] = cwa_db .cwa_settings .get (setting , 15 ) # Default to 15 minutes
710719 elif setting == 'auto_send_delay_minutes' :
711720 result [setting ] = cwa_db .cwa_settings .get (setting , 5 ) # Default to 5 minutes
721+ elif setting == 'hardcover_auto_fetch_batch_size' :
722+ result [setting ] = cwa_db .cwa_settings .get (setting , 50 ) # Default to 50
723+ elif setting == 'hardcover_auto_fetch_schedule_hour' :
724+ result [setting ] = cwa_db .cwa_settings .get (setting , 2 ) # Default to 2 AM
712725 elif setting == 'duplicate_scan_debounce_seconds' :
713726 result [setting ] = cwa_db .cwa_settings .get (setting , 30 )
714727
728+ # Handle float settings
729+ for setting in float_settings :
730+ value = request .form .get (setting )
731+ if value is not None :
732+ try :
733+ float_value = float (value )
734+ # Validate range
735+ if setting == 'hardcover_auto_fetch_min_confidence' :
736+ float_value = max (0.5 , min (1.0 , float_value )) # Clamp between 0.5 and 1.0
737+ elif setting == 'hardcover_auto_fetch_rate_limit' :
738+ float_value = max (0.0 , min (60.0 , float_value )) # Clamp between 0 and 60 seconds
739+ result [setting ] = float_value
740+ except (ValueError , TypeError ):
741+ # Use current value if conversion fails
742+ if setting == 'hardcover_auto_fetch_min_confidence' :
743+ result [setting ] = cwa_db .cwa_settings .get (setting , 0.85 ) # Default to 0.85
744+ elif setting == 'hardcover_auto_fetch_rate_limit' :
745+ result [setting ] = cwa_db .cwa_settings .get (setting , 5.0 ) # Default to 5.0 seconds
746+ else :
747+ if setting == 'hardcover_auto_fetch_min_confidence' :
748+ result [setting ] = cwa_db .cwa_settings .get (setting , 0.85 ) # Default to 0.85
749+ elif setting == 'hardcover_auto_fetch_rate_limit' :
750+ result [setting ] = cwa_db .cwa_settings .get (setting , 5.0 ) # Default to 5.0 seconds
751+
752+
715753 # Handle JSON settings
716754 for setting in json_settings :
717755 value = request .form .get (setting )
@@ -792,11 +830,19 @@ def set_cwa_settings():
792830 cwa_db = CWA_DB ()
793831 cwa_settings = cwa_db .get_cwa_settings ()
794832
833+ # Check if Hardcover token is available
834+ from os import getenv
835+ hardcover_token_available = bool (
836+ getattr (config , "config_hardcover_token" , None ) or
837+ getenv ("HARDCOVER_TOKEN" )
838+ )
839+
795840 next_scan_run = get_next_duplicate_scan_run (cwa_settings )
796841
797842 return render_title_template ("cwa_settings.html" , title = _ ("Calibre-Web Automated User Settings" ), page = "cwa-settings" ,
798843 cwa_settings = cwa_settings , ignorable_formats = ignorable_formats , target_formats = target_formats ,
799844 automerge_options = automerge_options , autoingest_options = autoingest_options ,
845+ hardcover_token_available = hardcover_token_available ,
800846 next_duplicate_scan_run = next_scan_run , config = config )
801847
802848
@@ -820,6 +866,7 @@ def get_next_duplicate_scan_run(settings):
820866 except Exception :
821867 return None
822868
869+
823870##————————————————————————————————————————————————————————————————————————————##
824871## ##
825872## CWA SHOW HISTORY ##
@@ -988,6 +1035,38 @@ def cwa_stats_show():
9881035 data_epub_fixer = cwa_db .get_epub_fixer_history (fixes = False , verbose = False )
9891036 data_epub_fixer_with_fixes = cwa_db .get_epub_fixer_history (fixes = True , verbose = False )
9901037
1038+ # Get Hardcover auto-fetch stats
1039+ hardcover_stats = None
1040+ try :
1041+ # Get total stats from hardcover_auto_fetch_stats table
1042+ total_processed = cwa_db .execute_read (
1043+ "SELECT SUM(books_processed) FROM hardcover_auto_fetch_stats"
1044+ )
1045+ total_auto_matched = cwa_db .execute_read (
1046+ "SELECT SUM(auto_matched) FROM hardcover_auto_fetch_stats"
1047+ )
1048+
1049+ # Get pending review count
1050+ pending_review = ub .session .query (ub .HardcoverMatchQueue ).filter (
1051+ ub .HardcoverMatchQueue .reviewed == 0
1052+ ).count ()
1053+
1054+ # Get manually reviewed count
1055+ manually_reviewed = ub .session .query (ub .HardcoverMatchQueue ).filter (
1056+ ub .HardcoverMatchQueue .reviewed == 1 ,
1057+ ub .HardcoverMatchQueue .review_action == 'accept'
1058+ ).count ()
1059+
1060+ hardcover_stats = {
1061+ 'total_processed' : total_processed [0 ][0 ] if total_processed and total_processed [0 ][0 ] else 0 ,
1062+ 'total_auto_matched' : total_auto_matched [0 ][0 ] if total_auto_matched and total_auto_matched [0 ][0 ] else 0 ,
1063+ 'pending_review' : pending_review ,
1064+ 'manually_reviewed' : manually_reviewed
1065+ }
1066+ except Exception as e :
1067+ log .debug (f"Error fetching Hardcover stats: { e } " )
1068+ hardcover_stats = None
1069+
9911070 return render_title_template ("cwa_stats_tabs.html" , title = _ ("Calibre-Web Automated Stats & Activity" ),
9921071 page = "cwa-stats" ,
9931072 active_tab = active_tab ,
@@ -1024,14 +1103,14 @@ def cwa_stats_show():
10241103 active_users = active_users ,
10251104 selected_user_id = user_id ,
10261105 cwa_stats = get_cwa_stats (),
1106+ hardcover_stats = hardcover_stats ,
10271107 data_enforcement = data_enforcement , headers_enforcement = headers ["enforcement" ]["no_paths" ],
10281108 data_enforcement_with_paths = data_enforcement_with_paths , headers_enforcement_with_paths = headers ["enforcement" ]["with_paths" ],
10291109 data_imports = data_imports , headers_import = headers ["imports" ],
10301110 data_conversions = data_conversions , headers_conversion = headers ["conversions" ],
10311111 data_epub_fixer = data_epub_fixer , headers_epub_fixer = headers ["epub_fixer" ]["no_fixes" ],
10321112 data_epub_fixer_with_fixes = data_epub_fixer_with_fixes , headers_epub_fixer_with_fixes = headers ["epub_fixer" ]["with_fixes" ])
10331113
1034-
10351114@cwa_stats .route ("/cwa-stats-export-csv/<tab_name>" , methods = ["GET" ])
10361115@login_required_if_no_ano
10371116@admin_required
0 commit comments