o
    &_hy                     @   s  d Z ddlZddlZddlmZ ddlmZ ddlmZm	Z	m
Z
 ddlmZ ddlZddlmZmZ ddlZddlZddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlm Z m!Z!m"Z" ddl#m$Z$ ddl%Z&eG dd dZ'eG dd dZ(eG dd dZ)G dd dZ*G dd dZ+G dd dZ,G dd dZ-G dd dZ.G d d! d!Z/d"d# Z0G d$d% d%Z1d&d' Z2e3d(kre2  dS dS ))zu
NT License Checker
================================================================================================
    N)Path)	dataclass)ListDictOptional)datetime)ThreadPoolExecutoras_completed	webdriverOptionsBy)KeysWebDriverWaitexpected_conditions)TimeoutExceptionWebDriverExceptionNoSuchElementException)fuzzc                   @   sr   e Zd ZU dZdZeed< dZeed< dZ	eed< dZ
eed	< d
Zeed< dZeed< dZeed< dZeed< dS )SimpleConfigzSimple configuration-https://licensingnt.nt.gov.au/PublicRegister/website_urlU   name_similarity_required   max_retry_attempts   request_timeout_secondsg333333?delay_between_requests   parallel_browsersF	test_mode
   test_record_limitN)__name__
__module____qualname____doc__r   str__annotations__r   intr   r!   r"   floatr$   r%   boolr'    r1   r1   /var/www/html/scrapers/ntc.pyr   #   s   
 r   c                   @   sL   e Zd ZU dZeed< eed< eed< dZeed< dd Zd	e	fd
dZ
dS )EmployeeRecordz)Holds one employee's information from CSVpayroll_numberemployee_namelicense_numberr   csv_row_numberc                 C   s4   t | j | _t | j | _t | j | _dS )z)Remove extra spaces and clean up the dataN)r,   r4   stripr5   r6   selfr1   r1   r2   
clean_dataJ   s   zEmployeeRecord.clean_datareturnc                 C   s   t | jo| jS )z9Check if this record has the minimum required information)r0   r6   r5   r9   r1   r1   r2   has_required_dataP      z EmployeeRecord.has_required_dataN)r(   r)   r*   r+   r,   r-   r7   r.   r;   r0   r=   r1   r1   r1   r2   r3   B   s   
 r3   c                   @   s   e Zd ZU dZdZeed< dZeed< dZeed< dZ	eed< dZ
eed	< d
Zeed< dZeed< dZeed< defddZdS )SearchResultz(Holds the result of searching NT website	Not Found
found_namelicense_typelicense_status
No Licensename_matches error_messager   how_many_retries        search_time_secondsF
was_cachedr<   c                 C   s   | j dko| j S )z-Did we successfully find license information?r@   )rA   rG   r9   r1   r1   r2   is_successfula   s   zSearchResult.is_successfulN)r(   r)   r*   r+   rA   r,   r-   rB   rC   rE   rG   rH   r.   rJ   r/   rK   r0   rL   r1   r1   r1   r2   r?   U   s   
 r?   c                   @   sN   e Zd ZdZdefddZdd Zdejfdd	Z	d
d Z
dd Zdd ZdS )OptimizedBrowserManagerz6Manages multiple Chrome browsers for reliable scrapingconfigc                 C   s*   || _ t | _g | _d| _t | _d S )NF)	rN   queueQueuebrowser_poolall_browserssetup_complete	threadingLocklock)r:   rN   r1   r1   r2   __init__m   s
   
z OptimizedBrowserManager.__init__c                 C   s   | j rdS | jI | j r	 W d   dS td| jj d t| jjD ]}|  }| j| | j	
| td|d  d q%d| _ td W d   dS 1 sTw   Y  dS )	z.Create Chrome browsers for parallel processingNzSetting up z Chrome browsers...z   Browser    z readyTz+All browsers ready for parallel processing!)rS   rV   printrN   r$   range_create_chrome_browserrR   appendrQ   put)r:   ibrowserr1   r1   r2   setup_browserst   s   
"z&OptimizedBrowserManager.setup_browsersr<   c                 C   s   t  }|d |d |d |d |d |d |d |d |d	 |d
 |d |d |d |d |d |d tj|dS )z/Create one Chrome browser with optimal settings
--headless--no-sandbox--disable-dev-shm-usagez--disable-gpuz--disable-extensionsz--disable-loggingz--disable-web-securityz--window-size=1280,720z%--disable-background-timer-throttlingz --disable-renderer-backgroundingz(--disable-backgrounding-occluded-windowsz!--disable-ipc-flooding-protectionz--disable-features=TranslateUIz--disable-default-appsz--no-first-runz--memory-pressure-offoptions)r   add_argumentr   Chrome)r:   chrome_optionsr1   r1   r2   r[      s$   















z.OptimizedBrowserManager._create_chrome_browserc                 C   s.   | j s|   | j }t|| jj}||fS )z)Get a browser from the pool (thread-safe))rS   r`   rQ   getr   rN   r!   )r:   r_   wait_helperr1   r1   r2   get_browser   s
   
z#OptimizedBrowserManager.get_browserc                 C   s   | j | dS )z Return browser to pool when doneN)rQ   r]   )r:   r_   r1   r1   r2   return_browser   r>   z&OptimizedBrowserManager.return_browserc                 C   sN   d}| j D ]}z|  W q   |d7 }Y q|dkr%td| d dS dS )z$Close all browsers when program endsr   rX   z	WARNING: z" browsers failed to close properlyN)rR   quitrY   )r:   failed_cleanupsr_   r1   r1   r2   cleanup_all_browsers   s   
z,OptimizedBrowserManager.cleanup_all_browsersN)r(   r)   r*   r+   r   rW   r`   r   rg   r[   rk   rl   ro   r1   r1   r1   r2   rM   j   s    
rM   c                   @   s   e Zd ZdZdedefddZdedefdd	Zdedefd
dZ	dededefddZ
dedefddZdedefddZdejdededefddZdS )SeleniumLicenseSearcherz.Searches NT government website using Selenium rN   browser_managerc                 C   s&   || _ || _i | _t | _i | _d S N)rN   rq   search_cacherT   rU   
cache_lock
name_cache)r:   rN   rq   r1   r1   r2   rW      s
   

z SeleniumLicenseSearcher.__init__namer<   c                 C   s~   |sdS || j v r| j | S | |}|   }t|dkr2|d  dd|dd  }n|  }|| j |< |S )zEConvert name to standard format for comparison with enhanced cleaningrF   r   ,  N)ru   _clean_name_for_matchingr8   uppersplitlenjoin)r:   rv   cleaned_nameparts
normalizedr1   r1   r2   normalize_employee_name   s   


"
z/SeleniumLicenseSearcher.normalize_employee_namec                 C   s   |sdS t | }d| }|  }g }|D ] }t|dkr*t|dkr*q|dr6t|dkr6q|| qg }|D ]}d|v rQ|d}|| q@|| q@dd |D }|red|S |S )	z>Clean name by removing common variations and formatting issuesrF   ry   rX   .r#   -c                 S   s   g | ]}|  r|qS r1   )r8   ).0wordr1   r1   r2   
<listcomp>  s    zDSeleniumLicenseSearcher._clean_name_for_matching.<locals>.<listcomp>)	r,   r8   r~   r|   r{   r}   endswithr\   extend)r:   rv   cleanedwordsfiltered_wordsr   processed_wordshyphen_partsr1   r1   r2   rz      s(   
z0SeleniumLicenseSearcher._clean_name_for_matchingcsv_namewebsite_namec           	      C   s   |dkrdS |r
|sdS |  |}|  |}| |}| |}||kr&dS | | kr0dS t||t| | t| | t| | g}t|}|| j	j
kr^dS d|ddS )z<Compare names with enhanced cleaning and return match statusr@   zNo MatchYesNo (.1fz%))rz   r   r{   r   token_set_ratiotoken_sort_ratiopartial_ratioratiomaxrN   r   )	r:   r   r   cleaned_csvcleaned_websitenorm_csvnorm_websitesimilarity_methods
similarityr1   r1   r2   check_name_similarity  s*   




z-SeleniumLicenseSearcher.check_name_similarityemployeec                 C   s~   |j | jv r| j|j  }| |j|j|_d|_|S | |}| r=| j	 || j|j < W d   |S 1 s8w   Y  |S )z0Search for one employee's license using SeleniumTN)
r6   rs   r   r5   rA   rE   rK   _search_with_retriesrL   rt   )r:   r   cached_resultresultr1   r1   r2   search_single_license5  s   

z-SeleniumLicenseSearcher.search_single_licensec           
      C   s(  |j  st S d}t| jjD ]s}zJ| j \}}z9t }| 	|||j }t | }|
 rL| |j|j|_||_||_|W | j| W   S W | j| n| j| w W q ty }	 zt|	}|| jjd k rytd|d   W Y d}	~	qd}	~	ww td| jj d| | jjdS )z(Try searching multiple times if it failsrF   rX         ?NzFailed after z attempts: )rG   rH   )r6   r8   r?   rZ   rN   r   rq   rk   time_do_selenium_searchrL   r   r5   rA   rE   rH   rJ   rl   	Exceptionr,   sleep)
r:   r   
last_errorattemptr_   rj   
start_timer   search_timeer1   r1   r2   r   L  s<   
 	z,SeleniumLicenseSearcher._search_with_retriesr_   rj   r6   c           	   
   C   sv  zp| | jj |ttjdf}|  |	|
  |	tj |ttjdf}td |tjd}t|dkrm|d tjd}t|dkrmt|d j
 pYd	|d j
 pad	|d
 j
 pid	dW S t W S  ty~   td|  ty } z	tdt| d}~w ty } z	tdt| d}~w ty } z	tdt| d}~ww )z8Actually search the NT government website using SeleniumLicencenumber%ctl00_searchpage_ResultBox_ResultGridr   z)#ctl00_searchpage_ResultBox_ResultGrid trrX   td   r   r@      )rA   rB   rC   zWebsite timeout for license: zPage element not found: NzWebDriver error: zSelenium search error: )ri   rN   r   untilECelement_to_be_clickabler   IDclear	send_keysr8   r   RETURNpresence_of_element_locatedr   r   find_elementsCSS_SELECTORr}   TAG_NAMEr?   textr   r   r   r,   r   )	r:   r_   rj   r6   license_inputresults_tablerowscellsr   r1   r1   r2   r   r  sD   
z+SeleniumLicenseSearcher._do_selenium_searchN)r(   r)   r*   r+   r   rM   rW   r,   r   rz   r   r3   r?   r   r   r   rg   r   r   r1   r1   r1   r2   rp      s    *(&rp   c                   @   s.   e Zd ZdZedededee fddZ	dS )SimpleCSVHandlerz"Reads employee data from CSV files	file_pathrN   r<   c              
      s|  zt j| tdd  jj jdd _g d} fdd|D }|r5tdd	| d
t j dg } 	 D ]6\}}t
|d |d |d |d d}|  | rq|| |jrqt||jkrqtd|j d  nq;|sxtdg }t }	d}
|D ]}|j|	vr|	|j || q|
d7 }
q|
dkrtd|
 d |W S  ty } z
tdt|   d}~ww )z Load employee data from CSV fileF)dtype	na_filteru   ﻿rF   )Payroll NumberEmployee NameLicense Numberc                    s   g | ]	}| j vr|qS r1   )columns)r   coldfr1   r2   r     s    z7SimpleCSVHandler.load_employee_data.<locals>.<listcomp>z"
ERROR: Missing required columns: rx   z

Your CSV file MUST have these exact column names:
- Payroll Number
- Employee Name
- License Number

Current columns in your file: z
                r   r   r   r   r4   r5   r6   r7   z!TEST MODE: Processing only first  recordsz2ERROR: No valid employee records found in CSV filer   rX   zNOTE: Removed z duplicate license numberszERROR reading CSV file: N)pdread_csvr,   r   r8   replace
ValueErrorr~   listiterrowsr3   r;   r=   r\   r%   r}   r'   rY   setr6   addr   )r   rN   required_columnsmissing_columns	employeesindexrowr   unique_employeesseen_licensesduplicates_removedr   r1   r   r2   load_employee_data  sX   



z#SimpleCSVHandler.load_employee_dataN)
r(   r)   r*   r+   staticmethodr,   r   r   r3   r   r1   r1   r1   r2   r          r   c                   @   st   e Zd ZdZdefddZdd Zdedefd	d
Z	dde
dedefddZde
defddZde
defddZdS )SimpleProgressTrackerz1Shows nice progress bar and processing statisticstotal_employeesc                 C   s<   || _ d| _d| _d| _d| _t | _d| _t	 | _
d S )Nr   )r   	completed
successfulfailedcachedr   r   last_updaterT   rU   rV   )r:   r   r1   r1   r2   rW     s   
zSimpleProgressTracker.__init__c              
   C   sd   t dd  t d| j d t d  t ddddd	dd
dddddd 	 t d  dS )zShow the processing header
U=====================================================================================z NT LICENSE CHECKER - PROCESSING z
 EMPLOYEESProgress<15ry   Employee<25LicenseStatus<12SpeedzU-------------------------------------------------------------------------------------N)rY   r   r9   r1   r1   r2   show_header   s
   
,z!SimpleProgressTracker.show_headerr   r   c                 C   sl  | j - |  jd7  _|jr|  jd7  _n| r"|  jd7  _n|  jd7  _W d   n1 s3w   Y  t }|| j dk rEdS || _| j| j	 d }| 
|}t|| j d}| j| d }t|jdkrt|jdd d	 n|j}t|jd
kr|jdd d	 n|j}	|jrd}
n	| rd}
nd}
td| d|dd|	dd|
dd|ddddd dS )zUpdate progress displayrX   Nr   d   g{Gz?<         z..r       CachedSuccessFailedry   r   r   r   .0fz/minrF   T)endflush)rV   r   rK   r   rL   r   r   r   r   r   _make_progress_barr   r   r}   r5   r6   rY   )r:   r   r   current_timepercentprogress_barelapsedspeeddisplay_namedisplay_licensestatusr1   r1   r2   update_progress  sN   
.
z%SimpleProgressTracker.update_progress   r  widthr<   c                 C   s8   t || d }d| d||   }d| d|ddS )zCreate ASCII progress barr   Xr   [z] z5.1f%r.   )r:   r  r  filledbarr1   r1   r2   r  4  s   z(SimpleProgressTracker._make_progress_barelapsed_timeexcel_file_pathc                 C   s   t dd  t d t d  t d| j  t d| j  t d| j  t d| j  t d| jt| jd	 d
 dd t d| |  t d| j| d dd t d|  t d  dS )zShow final processing summaryz

r   zPROCESSING COMPLETED!zTotal employees processed: zSuccessful searches: zFailed searches: zCached results: zSuccess rate: rX   r   r   r  zTotal time: zProcessing speed: r   z employees/minutezExcel report saved: N)rY   r   r   r   r   r   _format_time)r:   r  r  r1   r1   r2   show_final_summary:  s   
$z(SimpleProgressTracker.show_final_summarysecondsc                 C   sp   |dk r
|ddS |dk r"t |d }t |d }| d| dS t |d }t |d d }| d| dS )	z"Convert seconds to readable formatr   r   z secondsi  zm szh mr  )r:   r  minutessecshoursr1   r1   r2   r  I  s   z"SimpleProgressTracker._format_timeN)r  )r(   r)   r*   r+   r.   rW   r   r3   r?   r  r/   r,   r  r  r  r1   r1   r1   r2   r     s    
,r   c                   @   s.   e Zd ZdZedee dedefddZdS )SimpleExcelGeneratorz'Creates Excel reports with color codingresultsoriginal_file_pathr<   c                 C   s  z1t |}t d}|j|j d| d }g }| D ]J}|dd}|dd}|dd}	|dd}
|d	kp_|d
kp_|dkp_|dp_|d	kp_|dkp_|	d	kp_|	dkp_|
d	kp_|
dk}|rg|| q|sdddt	|  ddddd}|g}|j|j d| d }t
|}t
j|dd}t	|dksdt|d ddvrdnd}|j|d|d |j}|j| }|dd d!dd"d#dd$}t|jD ]5\}}|d||| tt	t||js|| tj	  nd}tt|d% d&d'}|||| q|dd( |ddt	|t	|jd  |dd W d)   n	1 s)w   Y  t|W S  tyI } z
td*t|   d)}~ww )+zDCreate Excel report - EXCEPTIONS ONLY (problems that need attention)z%Y%m%d_%H%M%S_Exceptions_z.xlsx
Name MatchrF   NT Register NameLicense Typer   r@   rD   Errorr   zN/Az"ALL RECORDS PROCESSED SUCCESSFULLYr   zNo exceptionsz	All foundz	All validr   r   zRolecall Namer#  r"  r$  r   _NT_ALL_SUCCESS_
xlsxwriter)enginerX   zALL RECORDSr   r   
ExceptionszProcessing SummaryF)r   
sheet_nameTz#8C1E31whitecentervcenter)boldbg_color
font_colorborderalignvalign	text_wrapr#      2   r  NzERROR creating Excel report: )r   r   nowstrftimeparentstemri   
startswithr\   r}   r   	DataFrameExcelWriterr,   to_excelbooksheets
add_format	enumerater   writer   emptyastypemin
set_columnset_row
autofilterfreeze_panesr   rY   )r  r   original_path	timestamp
excel_pathexception_resultsr   
name_matchnt_register_namerB   r
  is_exceptionsummary_resultr   writerr+  workbook	worksheetheader_colorcol_numheader
max_length	col_widthr   r1   r1   r2   create_excel_report^  s   	
	



 
,z(SimpleExcelGenerator.create_excel_reportN)	r(   r)   r*   r+   r   r   r   r,   r\  r1   r1   r1   r2   r  [  r   r  c                   @   st   e Zd ZdZdd Zdd Zdd Zdefd	d
Zde	de
fddZdee dee fddZdedefddZdS )SeleniumNTLicenseCheckerz'Main application class - using reliablec                 C   s   t  | _t| j| _d | _d S rr   )r   rN   rM   rq   progress_trackerr9   r1   r1   r2   rW     s   
z!SeleniumNTLicenseChecker.__init__c              
   C   s4  zz?|    |  }t|| j}| t|s%td W W | j	  dS | 
|}t||}t | jj }| j|| W n; tyM   td Y n7 ty{ } z#tdt|  td td td td td	 W Y d}~nd}~ww W | j	  dS W | j	  dS W | j	  dS | j	  w )
z0Main function - this is where everything happenszProcess cancelled by userNz*

Process stopped by user (Ctrl+C pressed)z
ERROR: z
TROUBLESHOOTING TIPS:zj1. Make sure your CSV file has the exact column names: 'Payroll Number', 'Employee Name', 'License Number'z)2. Check that Chrome browser is installedz)3. Make sure you have internet connectionz<4. Try running as administrator if you get permission errors)_show_welcome_get_csv_file_from_userr   r   rN   _ask_user_to_continuer}   rY   rq   ro   _process_all_employeesr  r\  r   r^  r   r  KeyboardInterruptr   r,   )r:   csv_file_pathr   r  rN  r  r   r1   r1   r2   run  s>   

zSeleniumNTLicenseChecker.runc                 C   s   dS )zShow welcome messageNr1   r9   r1   r1   r2   r_    s    z&SeleniumNTLicenseChecker._show_welcomer<   c                 C   s   t tjdkrtjd  d}td|  ntd td td td td td	 d}|s<td
t| sItd| tdt|j	  |S )zGet CSV file path from userrX   z"'zUsing file from command line: z
Please provide your CSV file:z   You can either:z   1. Type the full file pathz-   2. Drag and drop the file into this windowz"   3. Copy and paste the file pathz
Enter CSV file path: zNo file path providedzFile not found: zFile found: )
r}   sysargvr8   rY   inputr   r   existsrv   )r:   r   r1   r1   r2   r`    s   z0SeleniumNTLicenseChecker._get_csv_file_from_useremployee_countc                 C   s~   | j jrtdt|| j j d dS td| d td| j j d 	 td  }|dv r4dS |d	v r:d
S td q&)z.Ask user if they want to process the employeesz
TEST MODE: Will process z
 employeesTz
Ready to process z	Will use z parallel Chrome browsersz"
Continue with processing? (Y/N): )yyes)nnoFz&Please enter 'y' for yes or 'n' for no)	rN   r%   rY   rG  r'   r$   rh  r8   lower)r:   rj  responser1   r1   r2   ra    s   z.SeleniumNTLicenseChecker._ask_user_to_continuer   c                    sP  t t|| _t| j| jg }t| jjd  fdd|D }t|D ]o}|| }z*|	 }|j
|j|j|j|j|j|jd}|| | j|| t| jj W q& ty } z3td|j dt|  |j
|j|jddddd}	||	 tt|d}
| j||
 W Y d	}~q&d	}~ww W d	   |S 1 sw   Y  |S )
zAProcess all employees with minimal output unless there are issues)max_workersc                    s   i | ]
}  j||qS r1   )submitr   )r   r   executorsearcherr1   r2   
<dictcomp>7  s    zCSeleniumNTLicenseChecker._process_all_employees.<locals>.<dictcomp>r&  zERROR processing z: r%  )rG   N)r   r}   r^  rp   rN   rq   r   r$   r	   r   r4   r6   r5   rA   rE   rB   rC   r\   r  r   r   r"   r   rY   r,   r?   )r:   r   all_resultsfuture_to_employeefuturer   search_resultresult_dictr   error_resulterror_search_resultr1   rs  r2   rb  )  sT   




99z/SeleniumNTLicenseChecker._process_all_employeesr   c                 C   s   g }|j r
|d |jdkr|d|jdd |jdkr(|d|j  |jr5|d|j  n|d |rAd	|S d
S )z-Create informative notes for the Excel reportz!Cached result (duplicate license)r   zSearch time: z.2fr  z	Retries: zError: zSelenium scrapingz | zProcessed successfully)rK   r\   rJ   rH   rG   r~   )r:   r   notesr1   r1   r2   _create_notes_for_resulto  s   



z1SeleniumNTLicenseChecker._create_notes_for_resultN)r(   r)   r*   r+   rW   re  r_  r,   r`  r.   r0   ra  r   r3   r   rb  r?   r  r1   r1   r1   r2   r]    s    (Fr]  c            
   
   C   s8  zkddl m}  ddlm} ddlm} ddlm} ddlm	} | }|
d |
d |
d	 | j|d
}||d}z*|d |||jdf}|r[td W |  W dS td W |  W dS |  w  ty }	 z$tdt|	  td td td td td W Y d }	~	dS d }	~	ww )Nr   r
   r   r   r   r   ra   rb   rc   rd   r    r   r   z Selenium connection test PASSED!Tz8License input field not found - website may have changedFz!Selenium connection test failed: zCommon fixes:z 1. Install Google Chrome browserz"2. Update Chrome to latest version3. Run as administratorz4. Check internet connection)seleniumr   !selenium.webdriver.chrome.optionsr   selenium.webdriver.common.byr   selenium.webdriver.support.uir   selenium.webdriver.supportr   rf   rg   ri   r   r   r   rY   rm   r   r,   )
r   r   r   r   r   rh   driverwaitr   r   r1   r1   r2   test_selenium_connection  sD   




r  c                   @   s   e Zd ZdZdd Zdd Zdedefdd	Zd
e	e
 de	e fddZde	e defddZde	e de	e fddZdd ZdS )NTLicenseCheckerAPIz>API class for NT License Checker that can be called from Flaskc                 C   s   t  | _d | _d | _d S rr   )r   rN   rq   ru  r9   r1   r1   r2   rW     s   
zNTLicenseCheckerAPI.__init__c                 C   s0   | j du s	| j jst| j| _ | j   dS dS )z/Ensure browser manager is initialized and readyN)rq   rS   rM   rN   r`   r9   r1   r1   r2   _ensure_browser_manager  s   z+NTLicenseCheckerAPI._ensure_browser_managerrd  r<   c              
   C   sb   z|    t|| j}t| j| j| _| |}|W S  ty0 } z	tdt	| d}~ww )z;Process CSV file and return results as JSON-compatible listzFailed to process CSV file: N)
r  r   r   rN   rp   rq   ru  _process_employees_for_apir   r,   )r:   rd  r   r  r   r1   r1   r2   process_csv_file  s   
z$NTLicenseCheckerAPI.process_csv_filer   c                 C   s   g }|D ]Z}z-| j |}|j|j|j|j|j|j|j|j	|j
|j|jd}|| t| jj W q ty^ } z|j|j|jddddddt||jd}|| W Y d}~qd}~ww |S )z4Process employees and return JSON-compatible results)r4   r6   r5   rQ  rP  rB   rC   rK   rJ   rG   r7   r%  FrI   N)ru  r   r4   r6   r5   rA   rE   rB   rC   rK   rJ   rG   r7   r\   r   r   rN   r"   r   r,   )r:   r   r  r   rz  r{  r   r|  r1   r1   r2   r    sF   
z.NTLicenseCheckerAPI._process_employees_for_apir  c                 C   sz   t |}tdd |D }tdd |D }tdd |D }tdd |D }||||||dkr9|| d dS ddS )	zCalculate processing statisticsc                 s   s,    | ]}| d dkr| dsdV  qdS rQ  r@   rG   rX   Nri   r   rr1   r1   r2   	<genexpr>     * z;NTLicenseCheckerAPI.get_processing_stats.<locals>.<genexpr>c                 s   s,    | ]}| d dks| drdV  qdS r  r  r  r1   r1   r2   r    r  c                 s   s     | ]}| d drdV  qdS )rK   FrX   Nr  r  r1   r1   r2   r    s    c                 s   s    | ]
}| d rdV  qdS )rG   rX   Nr  r  r1   r1   r2   r    s    r   r   )total_recordssuccessful_searchesfailed_searchescached_resultserrorssuccess_rate)r}   sum)r:   r  totalr   r   r   r  r1   r1   r2   get_processing_stats  s   z(NTLicenseCheckerAPI.get_processing_statsrecords_datac              
   C   s   zJ|    g }t|D ]/\}}tt|dd t|dd t|dd |d d}|  || qt| j	| j
| _| |}|W S  ty_ } z	tdt| d}~ww )	zBProcess list of records and return results as list of dictionariesr   rF   r   r   rX   r   zFailed to process records: N)r  rC  r3   r,   ri   r8   r;   r\   rp   rN   rq   ru  r  r   )r:   r  r   r^   record_datar   r  r   r1   r1   r2   process_records%  s&   
z#NTLicenseCheckerAPI.process_recordsc                 C   s$   | j r| j   d| _ d| _dS dS )z<Clean up browsers when completely done with the API instanceN)rq   ro   ru  r9   r1   r1   r2   cleanupC  s
   

zNTLicenseCheckerAPI.cleanupN)r(   r)   r*   r+   rW   r  r,   r   r  r   r3   r   r  r  r  r  r1   r1   r1   r2   r    s    1r  c               
   C   s   t tjdkrtjd  } | dv rt  dS ztd td t s+td td t }|  W dS  tyB   td Y dS  t	y} } z0td	t
|  td
 td td td td td td td W Y d}~dS d}~ww )zMain entry pointrX   )z--testz-ttestNzStarting NT License Checker...z-
Testing Selenium connection to NT website...z4Connection issues detected, but continuing anyway...zKIf all searches fail, the website may have changed or Chrome needs updatingz%
Selenium application stopped by userz
FATAL ERROR: z
SELENIUM TROUBLESHOOTING:z,1. Check your CSV file has the right columnsz'2. Install/update Google Chrome browserr  z!4. Check your internet connectionz85. Try reducing parallel_browsers to 1 if getting errorsz16. Run with --test to check Selenium connectivityz'7. Run with --help for more information)r}   rf  rg  ro  r  rY   r]  re  rc  r   r,   )argappr   r1   r1   r2   mainN  s6   r  __main__)4r+   jsonr   pathlibr   dataclassesr   typingr   r   r   r   rf  concurrent.futuresr   r	   rT   rO   r  r   r  r   r  r   selenium.webdriver.common.keysr   r  r   r  r   r   selenium.common.exceptionsr   r   r   	rapidfuzzr   pandasr   r   r3   r?   rM   rp   r   r   r  r]  r  r  r  r(   r1   r1   r1   r2   <module>   sR    X eMhm G2 ,
