
    @jw                        S r SSKrSSKrSSKrSSKrSSKrSSKrSSKJ	r	J
r
  SSKJr  SSKJrJr  SSKJr  \" \R$                  R'                  SS5      5      r\R*                  R-                  \R*                  R/                  \R*                  R1                  \5      5      S	5      rS
rSrSS1rSr\R*                  R/                  \R*                  R1                  \5      5      r\R*                  R-                  \S5      r \R*                  R-                  \S5      r!\R*                  R-                  \S5      \R*                  R-                  \S5      \R*                  R-                  \S5      /r"\R*                  R-                  \S5      \R*                  R-                  \S5      \R*                  R-                  \S5      /r#Sr$Sr%Sr&Sq'Sq(\RR                  " 5       r*Sr+Sr,Sr-SS1r.SS1r/SS1r0Sq1Sq2\RR                  " 5       r3/ q4\RR                  " 5       r5S  r6S! r7S" r8S# r9S$ r:S% r;S& r<S' r=S( r>S) r?S* r@S+ rAS, rBS- rCS. rDS/ rE " S0 S1\
5      rF " S2 S3\	5      rGS4 rH\IS5:X  a  \H" 5         gg)6u  
NWALScanner Broadcast Server v070
-----------------------------
Endpoints:
  GET  /proxy/wxdata            — proxies CumulusMX wx data
  GET  /api/calls               — returns active calls (scanner + auto-traffic)
  POST /api/calls               — adds a new scanner call
  POST /api/calls/<id>/extend   — extends expiresAt on a call
  DELETE /api/calls/<id>        — removes a call
  GET  /api/traffic             — roadwork events for Lauderdale/Colbert (ticker)
    N)ThreadingHTTPServerSimpleHTTPRequestHandler)urlparse)datetimetimezone)get_local_outages
NWALS_PORT9000z
calls.jsonz*http://127.0.0.1:5000/api/data/currentdataz/https://api.algotraffic.com/v4.0/TrafficEvents/
lauderdalecolbert,  zspark_lightning.jsonzspark_lightning_status.jsonzglm_lightning.jsonzgoes_glm_lightning.jsonzgoes_lightning.jsonzglm_lightning_status.jsonzgoes_glm_lightning_status.jsonzgoes_lightning_status.jsoni  x   zhttps://api.weather.com/v3/wx/forecast/daily/5day?geocode=34.7998%2C-87.6773&format=json&units=e&language=en-US&apiKey=e1f10a1e78da46f5b10a1e78da96f525zUhttps://api.weather.gov/alerts/active?area=AL&status=actual&message_type=alert,update   ALC077ALC033001077001033c                      [        U SSS9 n[        R                  " U5      sS S S 5        $ ! , (       d  f       g = f! [         a    Us $ f = f)Nrutf-8)encoding)openjsonload	Exception)pathdefaultfs      serve.pyload_json_filer    S   s?    $g.!99Q< /.. s#   > -	> 
;> > AAc                     U  H6  n[         R                  R                  U5      (       d  M)  [        X!5      U4s  $    US 4$ N)osr   existsr    )pathsr   	candidates      r   _first_existing_jsonr'   \   s;    	77>>)$$!)5y@@  D=    c                    U c  g [        U 5      nUS:  a  US-  n[        U5      $ ! [         a     Of = f [        [        R                  " [        U 5      R                  SS5      5      R                  5       5      $ ! [         a     gf = f)zENormalize seconds, milliseconds, or an ISO timestamp to Unix seconds.Nl    J)g     @@Z+00:00)floatintr   r   fromisoformatstrreplace	timestamp)valuenumbers     r   _timestamp_secondsr4   c   s    }u%%fF6{ 8))#e**<*<S(*KLVVXYY s    ' 
44AA> >
B
Bc                    [        U [        [        45      (       a   [        U 5      S:  a  U S   U S   U S   pCnS nO[        U [        5      (       a  U R                  SU R                  S5      5      nU R                  SU R                  S5      5      nU R                  S	U R                  S
U R                  S5      5      5      nU R                  S5      nOg  [        U5      n[        U5      n[        U5      nUc  g [        [        R                  " 5       5      nXg[        -
  :  d  XgS-   :  a  g U(       d  US SUS SU 3n[        U5      UUUUUS-  [        SXv-
  5      US.$ ! [         a     g f = f)N   r         latlalonlor1   ttimestamp_msidr   z.5f:i  )r?   r9   r;   r=   r1   r>   age_secondsprovider)
isinstancelisttuplelendictgetr,   r   r4   r-   timeLIGHTNING_MAX_AGE_Sr/   max)recordrB   r9   r;   raw_ts	strike_idtsnow_ss           r   _normalize_strikerQ   t   sm   &4-((S[A-=!!9fQi&		FD	!	!jj

4 01jj

4 01JJsFJJ~67
 JJt$	CjCj 
F	#B	z		E	'''2+;3iqS	2$/	 )nT	1ej)	 	  s   E& &
E32E3c                 t   [        U [        5      (       a`  U R                  S5      n[        U[        5      (       d  U R                  S5      nU R                  S5      n[        U[        5      (       d  / nO[        U [        5      (       a  U n/ nO/ n/ n/ n[	        5       nU HE  n[        Xa5      nU(       a	  US   U;   a  M   UR                  US   5        UR                  U5        MG     [	        5       nU H+  n[        Xa5      nU(       d  M  UR                  US   5        M-     UR                  S SS9  XH4$ )Nstrikesdatanew_strikesr?   c                     U S   $ )Nr=    )items    r   <lambda>-_normalize_provider_payload.<locals>.<lambda>   s    T#Yr(   T)keyreverse)	rC   rG   rH   rD   setrQ   addappendsort)	payloadrB   recordsnew_records
normalizedseenrL   strikenew_idss	            r   _normalize_provider_payloadrh      s   '4  ++i('4((kk&)Gkk-0+t,,K	GT	"	"J5D"64-&!  eG"646KKt% 
 OO.O=r(   c                 ,   [        U [        5      (       d  gU R                  S5      SL a/  U R                  S5      n US L =(       d    [        U5      [        :*  $ [        U R                  SS5      5      R                  5       S:H  $ ! [
         a     gf = f)NFhealthyTcapture_age_secondsstatus )rC   rG   rH   r,   LIGHTNING_PROVIDER_STALE_Sr   r/   lower)rl   ages     r   _spark_is_healthyrq      s    fd##zz)$jj./	$;J%*0J"JJ vzz(B'(..0I==  		s   B 
BBc           	      |   U(       d  g[        U [        5      (       d  gU R                  S5      SL a  g[        U R                  SU R                  SS5      5      5      R	                  5       nUS;   a  gU R                  SU R                  S	5      5      n Ub  [        U5      [        :  a  gg! [         a     gf = f)
NFTrj   staterl   rm   >   errorstalefailedofflinerk   rA   )rC   rG   rH   r/   ro   r,   rn   r   )rl   rS   rs   rp   s       r   _glm_is_healthyrx      s    fd##zz)%

7FJJx$<=>DDFE77
***FJJ},E
FC?uSz,FF   s   B. .
B;:B;c                     [        [        R                  " 5       5      n [        [        0 5      n[        [        0 5      n[        US5      u  p4[        U5      (       a  [        U[        5      (       a  UR                  S/ 5      O/ nU Vs/ s H  nUS   U;   d  M  UPM     nn[        U[        5      (       a  [        U5      O0 nUR                  SSSSU [        U5      [        U5      UUS.	5        U$ [        [        / 5      u  p[        [        0 5      u  p[        U	S5      u  p[        X5      (       a?  SS	S
SU S[        U5      SU/ U
(       a!  [         R"                  R%                  U
5      S.$ SS.$ SSSU SSS/ / SS.
$ s  snf )z=Return Spark when healthy, otherwise dormant GLM when usable.weatherbug_sparkrU   r?   zWeatherBug SparkprimaryT)	rB   provider_labelprovider_modefallback_availablegenerated_atstrike_countnew_strike_countrS   rU   goes_glmzGOES GLMfallbackz!Spark bridge unavailable or stale   r   N)rB   r|   r}   fallback_reasonr   history_minutesr   r   rS   rU   source_filenonezLightning Unavailablerw   z:Spark is unavailable and no healthy GLM fallback was found)
rB   r|   r}   r   r   r   r   rS   rU   rt   )r-   rI   r    SPARK_LIGHTNING_FILESPARK_LIGHTNING_STATUS_FILErh   rq   rC   rG   rH   updaterF   r'   GLM_LIGHTNING_FILE_CANDIDATESGLM_LIGHTNING_STATUS_CANDIDATESrx   r#   r   basename)rP   spark_payloadspark_statusspark_strikesspark_new_idsoriginal_newrf   rU   packageglm_payloadglm_path
glm_statusglm_status_pathglm_strikesglm_new_idss                  r   load_lightning_packager      s   		E"#7<M!"=rBL#>$ M
 && -.. mR0 	 (
'd|}, ' 	 

 *4M4)H)H$}%b*0&"&!. #K 0$&

 
	 0%
K #7'
#J  ; K
 z//"('B!!, !"9A277++H5
 	
 HL
 	
 1"M _
s   
FFc                     [        5       n [        [        0 5      n[        [        0 5      u  p#U R                  S5      U R                  S5      U R                  S5      U R                  S5      S;  a  SOSU R                  S5      S;  U R                  SS5      U R                  S	S5      U R                  S
5      U R                  S5      UUU(       a  [        R                  R                  U5      OS U R                  S5      S.$ )NrB   r|   r}   >   Nr   onlinerw   r   r   r   nearest_strikeradius_countsrt   )rB   r|   r}   rs   rj   r   r   r   r   sparkglmglm_status_filert   )	r   r    r   r'   r   rH   r#   r   r   )r   r   r   r   s       r   load_lightning_statusr   1  s    $&G!"=rBL"6'
#J KK
+!++&67 _5 {{:&n< ;;z*.@NA6#KK(:A>!++&67 _5  GG_-W%+ r(   c                  6  ^ [         R                  R                  [        5      (       d  / $  [	        [        S5       n [
        R                  " U 5      nS S S 5        [        R                  " 5       mU4S jnW Vs/ s H  o2" U5      (       d  M  UPM     nn[        U5      [        U5      :  a0  [	        [        S5       n [
        R                  " X@SS9  S S S 5        U$ U$ ! , (       d  f       N= fs  snf ! , (       d  f       U$ = f! [         a    / s $ f = f)Nr   c                    > U R                  S5      nU(       d  g [        R                  " UR                  SS5      5      nUR	                  5       T:  $ ! [
         a     gf = f)N	expiresAtFr*   r+   )rH   r   r.   r0   r1   r   )c
expires_atexpnows      r   	is_activeload_calls.<locals>.is_activeY  s\    {+J,,Z-?-?X-NO}},, s   8A 
A"!A"wr8   indent)r#   r   r$   
CALLS_FILEr   r   r   rI   rF   dumpr   )r   callsr   r   activer   s        @r   
load_callsr   R  s    77>>*%%	*c"aIIaLE #iik	 #3Uil!U3v;U#j#&!		&A. 'v! #" 4&& 	s^   D	 C!'D	 ;C2C2*D	 ?C7	D	 D	 !
C/+D	 7
DD	 D	 	DDc                     [        [        S5       n[        R                  " XSS9  S S S 5        g ! , (       d  f       g = f)Nr   r8   r   )r   r   r   r   )r   r   s     r   
save_callsr   j  s(    	j#	!		%1% 
		s   0
>c                     U=(       d    SR                  5       nSU;   d  SU;   a  gSU;   a  gSU;   d  SU;   a  g	U S
:X  a  gg)z8Map AlgoTraffic severity/title to a broadcast call type.rm   crash	collisionWRECKfirezVEHICLE FIREdebrishazardzROAD HAZARDClosedROAD CLOSEDzTRAFFIC INCIDENT)ro   )severitytitletitle_lowers      r   algo_severity_to_typer   n  sQ    ;B%%'K+!;;(k"98r(   c                 <   U R                  S5      nU(       a   [        R                  " UR                  SS5      5      n[        R                  " [
        R                  5      nX#-
  R                  5       nUS:  a9  [        R                  " [        R                  " 5       S-   [
        R                  S9nUR                  S5      $ [        R                  " [        R                  " 5       S-   [
        R                  S9R                  S5      $ ! [         a     NTf = f)	u;   Return ISO expiresAt — use event end or 2 hours from now.endr*   r+   iQ i  )tz%Y-%m-%dT%H:%M:%SZi   )rH   r   r.   r0   r   r   utctotal_secondsfromtimestamprI   strftimer   )eventr   r   now_dtdeltas        r   algo_expires_isor   {  s    
))E
C
		((S()CDC\\(,,/F\002Eu},,TYY[4-?HLLQ<< 455 !!$))+"4FOOPdee  		s   B+D 
DDc                 j  ^^ [        U [        5      (       d  gU R                  S5      =(       d    0 mSR                  U4S jS 5       5      R	                  5       m[        U4S j[         5       5      (       a  gTR                  S5      =(       d    0 nUR                  S	5      =(       d    /  Vs1 s H  n[        U5      R                  5       iM     nnUR                  S
5      =(       d    /  Vs1 s H  n[        U5      iM     nn[        U[        -  =(       d	    U[        -  5      $ s  snf s  snf )NF
properties c              3   j   >#    U  H(  n[        TR                  U5      =(       d    S 5      v   M*     g7f)rm   N)r/   rH   ).0r[   propss     r   	<genexpr>"_alert_is_local.<locals>.<genexpr>  s(     R9Q#C		#,"--9Qs   03)areaDescheadlinec              3   ,   >#    U  H	  oT;   v   M     g 7fr"   rW   )r   countytexts     r   r   r     s     
;(:fT>(:s   TgeocodeUGCSAME)rC   rG   rH   joinro   anyNWS_LOCAL_COUNTIESr/   upperboolNWS_LOCAL_UGCNWS_LOCAL_SAME)featurer   r2   ugcsamer   r   s        @@r   _alert_is_localr     s    gt$$KK%+E88R9QRRXXZD

;(:
;;;ii	"(bG,3KK,>,D",D
F,D%3u:,DC
F%,[[%8%>B%>@%>5CJ%>D@m#<tn'<== G@s   1#D+2D0c                     [         R                   " 5       n [           [        n[        nS S S 5        Wb  U W-
  [        :  a  U$ [
        R                  R                  [        SSS.S9n [
        R                  R                  USS9 n[        R                  " UR                  5       R                  S5      5      nS S S 5        WR                  S5      =(       d    /  Vs/ s H  n[        U5      (       d  M  UPM     nnS	U[         R"                  " [$        R&                  5      R)                  5       S
S/[        SS.n[           Uq[         R                   " 5       qS S S 5        U$ ! , (       d  f       GND= f! , (       d  f       N= fs  snf ! , (       d  f       U$ = f! [*         a  n	[-        SU	 3SS9  [           [        n
S S S 5        O! , (       d  f       O= fW
b%  [/        U
5      nSUS'   [1        U	5      US'   Us S n	A	$ S	/ [         R"                  " [$        R&                  5      R)                  5       S[1        U	5      SS.s S n	A	$ S n	A	ff = f)Nzapplication/geo+jsonz5NWALScannerWeatherV3/1.0 (operations@nwalscanner.com)Acceptz
User-Agentheaders   timeoutr   featuresFeatureCollection
LauderdaleColbertzapi.weather.gov)typer   r   county_filtercache_secondssourcez[ALERTS] NWS fetch failed: Tflushru   rt   )r   r   r   ru   rt   r   )rI   _nws_alert_lock_nws_alert_cache_nws_alert_cache_timeNWS_ALERT_CACHE_SurllibrequestRequestNWS_ALERTS_URLurlopenr   loadsreaddecoderH   r   r   r   r   r   	isoformatr   printrG   r/   )r   cachedcached_timer  responsera   r   r   resultexcru   
stale_copys               r   load_local_alertsr    s   
))+C	!+ 
 cK/2CCnn$$,Q
 % G#
^^##GR#8Hjj!7!7!@AG 9 %,KK
$;$Ar$A
$Aw' $A 	 

 ( $LL6@@B*I6.'
 %$(IIK!  ? 
 98
 _  
+C51>$E __eJ"&Jw"%c(Jw'$LL6@@BX'
 	

s   E&)F  4E8;$F  F	7F	=AF  F	F  &
E58
FF  
FF  F   
I*I=G	I
G	)I IAIIIc                       [         R                  R                  [        SS0S9n [         R                  R	                  U SS9 n[
        R                  " UR                  5       R                  5       5      nS S S 5        / n/ nW H  nUR                  S5      (       d  M  UR                  S5      =(       d    0 nUR                  S5      =(       d    S	R                  5       nU[        ;  a  Mi  UR                  S
S	5      nUS:X  a  UR                  U5        M  US;   d  M  UR                  U5        M     / n	U GH0  nUR                  S5      =(       d    0 nUR                  S5      =(       d    UR                  S5      =(       d    S	n
UR                  S5      =(       d    S	nUR                  S5      =(       d    UR                  S5      =(       d    S	nUR                  SS	5      nU	R                  UR                  S5      UR                  SS	5      U
UUR                  SS	5      UR                  S5      =(       d    UR                  SS	5      UUUR                  SS	5      S.	5        GM3     [           U	qS S S 5        [        5       nU Vs1 s H+  oR                  S5      (       d  M  UR                  S5      iM-     nn[!        5       nU GHF  n[#        UR                  S5      5      nUR%                  U5        UR                  S5      =(       d    0 nUR                  SS	5      nUR                  S5      =(       d    Un
UR                  S5      =(       d    S	nUR                  S5      =(       d    S	nU(       d  U(       a  U SU 3R'                  S5      OS	nUR                  S5      =(       d    UR                  S5      =(       d    SnUR                  S5      =(       d    S	nUR                  SS	5      nSSSSS .nUR                  UU5      nUU;  a  0 SS!U-   _SU_S"S#_S
[)        UU5      _SS$U 3_SU
_S%U_S&U_SU_S'U_SU_S(S	_S)S*_S+S,_S-[*        R*                  " 5       _S.[-        U5      _S/[*        R.                  " S0[*        R0                  " 5       5      _nUR3                  S1U5        UR%                  U5        [5        S2U S3U S435        GM  U H3  nUR                  S5      U:X  d  M  [-        U5      US.'   UUS''   XS'     GMD     GMI     U Vs/ s H2  nUR                  S5      (       a  UR                  S5      U;   d  M0  UPM4     nn[7        U5        [*        R:                  " [<        5        GM  ! , (       d  f       GN= f! , (       d  f       GN!= fs  snf s  snf ! [8         a  n[5        S5U 35         S nANhS nAff = f)6Nr   application/jsonr   
   r   r   startLocationr   rm   r   Incident)RoadworkFacilitycitydisplayRouteDesignatordescriptionr   r   r?   shortSubTitlesubTitlepermLink)	r?   r   r  roadr   subtitler   r  r  algoIddisplayCrossStreetz at z atzTraffic Incidentr   zMAJOR DELAYzMODERATE DELAYzMINOR DELAY)r   
MajorDelayModerateDelay
MinorDelayalgo_r   algotrafficz
[TRAFFIC] locationaddressrl   unitscautionzPLEASE USE CAUTION IN THIS AREAholdSeconds   rO   r   receivedr   r   z[ALGO] New incident: z in z Countyz[ALGO] Poll error: )r  r  r  ALGO_URLr  r   r  r  r  rH   ro   ALGO_COUNTIESr_   _cache_lock_roadwork_cacher   r]   r/   r^   stripr   rI   r   r   gmtimeinsertr
  r   r   sleepALGO_POLL_S)reqr   eventslocal_incidentslocal_roadworkevlocr   ev_typeroadwork_itemsr  r  descsevr   r   existing_algo_idsactive_algo_idsalgo_idcrossr)  r   r   sev_map	sev_labelcalles                              r   poll_algotrafficrJ    sw   
m	-..((HFX;Y(ZC''R'8AAFFHOO$56 9 !O Nvvh''ff_-3''(+1r88:.&&,j(#**2. 88"))"-   N$vvo.4"wwvA#''(*;Arww78>Bvvm,EwE2vvj"-%% "t #" 5 $ $ "w 3 " 7 Q266*b;Q ##' "z2 6
' 
 %" "0  LE */!).A%%/h  ! "eO%rvvd|,##G,66/28b778R0776?4f77#;<B77#78>BAEdV4w/55e<UW66'?[bff_.E[I[66-06B66*b1%2,<-Y#KK(;	"33w'8 w !} '<Xu'M	
  E7'; t #w "w !v !y &t  r "'H &r tyy{  $'7';!" #t}}5I4;;='Y#D& LLD)%))'21%VHGLM #55?g5-=b-AAkN-6AhK/3m,! #U &f ! auuX!%%/_*L 5   u
 	

;a  98N !n  	-'s+,,	-s{   AV/ 3V8B&V/ "EV/ 6V9V/ V%,V%H3V/ 8&V/ /V*V*V/ 
VV/ 
V"V/ /
W9WWc                   L   ^  \ rS rSrS rU 4S jrS rS rS rS r	S r
S	rU =r$ )
HandleriF  c                 f    U R                  S5        U R                  5         U R                  5         g )N   )send_response_corsend_headersselfs    r   
do_OPTIONSHandler.do_OPTIONSH  s%    3

r(   c                 J  > [        U R                  5      nUR                  R                  S5      nUS:X  a   [        R                  R                  [        SS9 nUR                  5       nS S S 5        U R                  S5        U R                  SS5        U R                  5         U R                  5         U R                  R                  W5        g US
:X  a   U R                  S[!        5       5        g US:X  a  U R                  S['        5       5        g US:X  Gad  [(        R(                  " 5       n[*           [,        n[.        nS S S 5        Wb   UW-
  [0        :  a  U R                  SU5        g  [        R                  R3                  [4        SSS.S9n	[        R                  R                  U	SS9 n
U
R                  5       n[6        R8                  " UR;                  S5      5      nS S S 5        WR=                  S5      =(       d    S /S   =(       d    0 n[?        UR=                  S5      [@        5      (       d  [C        S5      e[*           Uq[(        R(                  " 5       qS S S 5        U R                  SU5        g US:X  a  U R                  S[E        5       5        g US:X  a  U R                  S[G        5       5        g US :X  a  [I        5       n[6        RJ                  " U5      RM                  5       nU R                  S5        U R                  SS5        U R                  S!S"5        U R                  5         U R                  5         U R                  R                  U5        g US#:X  a  [N           [A        [P        5      nS S S 5        [6        RJ                  " W5      RM                  5       nU R                  S5        U R                  SS5        U R                  S!S"5        U R                  5         U R                  5         U R                  R                  U5        g [R        TU ]  5         g ! , (       d  f       GN= f! [         aO    U R                  S5        U R                  5         U R                  5         U R                  R                  S	5         g f = f! [         a6  n[#        SU 3SS9  U R                  SS[%        U5      / S.5         S nAg S nAff = f! , (       d  f       GN= f! , (       d  f       GN= f! , (       d  f       GN= f! [         ao  n[#        SU 3SS9  [*           [,        nS S S 5        O! , (       d  f       O= fWb  U R                  SU5         S nAg U R                  SSSU 305         S nAg S nAff = f! , (       d  f       GN= f)$N/z/proxy/wxdatar6   r   rN  Content-Typer  i  s   {}z/api/outagesz[OUTAGES] Aggregator error: Tr   F)successrt   countiesz/api/alerts/api/forecastzNWALScannerWeatherV3/1.0r   r   r  r   daypartr   daypartNamez1Weather.com response did not contain daypart dataz%[FORECAST] Weather.com fetch failed: rt   z"Weather.com forecast unavailable: z/api/lightningz/api/lightning/status
/api/callszCache-Controlzno-store/api/traffic)+r   r   rstripr  r  r  CUMULUS_URLr  rO  send_headerrP  rQ  wfilewriter   _json_responser   r
  r/   r  rI   _v3_forecast_lock_v3_forecast_cache_v3_forecast_cache_timeV3_FORECAST_CACHE_Sr  V3_WEATHERCOM_FORECAST_URLr   r  r  rH   rC   rD   
ValueErrorr   r   r   dumpsencoder1  r2  superdo_GET)rS  parsedr   r   rT   r  r   r  r  r  r  rawr\  ru   r   body	__class__s                   r   ro  Handler.do_GETM  s   $))$##C(?"(^^++K+Cq668D D""3'  1CD

  "

  & >!##C):)<= = %6%89?" ))+C"+5 # !cK&7:M&M##C0! ..00."4&@ 1  ^^++GR+@H"--/C::cjj&9:D A !HHY/9D61=C!'++m"<dCC$%XYY&)-&.2iik+ ' ##C.  ##%;%=>**%:%<=<LEJJu%,,.Ds#^-?@_j9JJLJJT">!O, ::d#**,Ds#^-?@_j9JJLJJT"] DC  (""3'

  "

  '(  4SE:$G##C$ X "*  
 " #"  A@ '&
  =cUC4P&.E '&& $''U33 	 '' $Fse"LM  H s   "Q P0,A&Q R 8S!1AT 46S3*A$T T&T 4V0
P?:Q ARR
S(,SS!
S03
T=T 
TT 
V!V4U;	V
U	V/VV
V"c                    [        U R                  5      nUR                  R                  S5      nUR                  S5      R	                  S5      n[        U5      S:X  Ga  US   S:X  a  US   S:X  a  US   S:X  a  US	   n[        U R                  R                  S
S5      5      nU R                  R                  U5      n [        R                  " U5      n[        5       nSn	U H@  n
U
R                  S5      U:X  d  M  UR                  SU
R                  S5      5      U
S'   Sn	  O   U	(       a   [!        U5        U R                  SSS05        g U R                  SSS05        g US:X  Ga	  [        U R                  R                  S
S5      5      nU R                  R                  U5      n [        R                  " U5      nSU;  a!  [#        [$        R&                  " 5       5      US'   SU;  a  [(        R(                  " 5       US'   [(        R*                  " S[(        R,                  " 5       5      US'   [        5       nUR/                  SU5        [!        U5        U R                  SSUS   S.5        g U R                  SSS05        g ! [         a    U R                  SSS05         g f = f! [         a    U R                  SSS05         g f = f)NrW     r   apir7   r   r6   extendr8   zContent-Lengthi  rt   zInvalid JSONFr?   r   TrN  ok  zCall not foundr^  rO   r   r.  )ry  r?   	Not found)r   r   r`  r3  splitrF   r-   r   rH   rfiler  r   r  r   re  r   r   r/   uuiduuid4rI   r   r4  r5  )rS  rp  r   partscall_idlengthrq  rr  r   updatedr   rH  s               r   do_POSTHandler.do_POST  s|   $))$##C(C&&s+ u:?uQx50U1X5HUSTXYaMaAhG$,,**+;Q?@Fjjoof-Czz# LEG55;')%)XXk155;M%NAkN"G	 
 5!##C$6  ##C'3C)DE<))*:A>?FZZ__V,Czz# 4 .T
4!YY[T
#}}-A4;;=QDLELLD!uDT
%CDC';!78I  ##C'>)BC,  ##C'>)BCs$   J .J/ J,+J,/KKc                    [        U R                  5      nUR                  R                  S5      R                  S5      n[	        U5      S:X  a  US   S:X  ax  US   S:X  ao  US   n[        5       n[	        U5      nU Vs/ s H  ofR                  S5      U:w  d  M  UPM     nn[        U5        U R                  S	S
U[	        U5      -
  S.5        g U R                  SSS05        g s  snf )NrW  r6   r   rw  r7   r   r8   r?   rN  T)ry  removedrz  rt   r{  )	r   r   r3  r|  rF   r   rH   r   re  )rS  rp  r  r  r   beforer   s          r   	do_DELETEHandler.do_DELETE  s    $))$""3'--c2u:?uQx50U1X5HAhG lE%jF"'B%Q55;'+Aq%EBuDVc%j=P%QRC';!78	 Cs   ?C%C%c                 p    U R                  SS5        U R                  SS5        U R                  SS5        g )NzAccess-Control-Allow-Origin*zAccess-Control-Allow-MethodszGET, POST, DELETE, OPTIONSzAccess-Control-Allow-HeadersrX  )rb  rR  s    r   rP  Handler._cors  s6    6=79UV7Hr(   c                    [         R                  " U5      R                  5       nU R                  U5        U R	                  SS5        U R                  5         U R                  5         U R                  R                  U5        g )NrX  r  )	r   rl  rm  rO  rb  rP  rQ  rc  rd  )rS  codeobjrr  s       r   re  Handler._json_response  s^    zz#%%'4 );<



r(   c                     U(       a;  [        U5      S:  a+  S[        US   5      ;   a  [        SUS    SUS    35        g g g g )Nr8   z/api/r   z	[SERVER] u    → r7   )rF   r/   r
  )rS  fmtargss      r   log_messageHandler.log_message  sD    CINw#d1g,'>Id1gYeDG956 (?N4r(   rW   )__name__
__module____qualname____firstlineno__rT  ro  r  r  rP  re  r  __static_attributes____classcell__)rs  s   @r   rL  rL  F  s/    
tl09d9I
7 7r(   rL  c                       \ rS rSrSrSrSrg)NWALThreadingHTTPServeri  TrW   N)r  r  r  r  allow_reuse_addressdaemon_threadsr  rW   r(   r   r  r    s    Nr(   r  c                     [         R                  " [         R                  R                  [         R                  R	                  [
        5      5      5        [        R                  " [        SS9n U R                  5         [        S[         S3SS9  [        SSS9  [        SSS9  [        SSS9  [        S	[         S
3SS9  [        S[         S3SS9  [        S[         S3SS9  [        S[         S3SS9  [        S[         S3SS9  [        S[         S3SS9  [        S[         S3SS9  [        S[         S3SS9  [        SSS9  [        S[         S3SS9  [        SSS9   [        S[        4[        5      n[        S"[         3SS9  UR!                  5         g ! [         aB  n[        SSS9  [        S[         SU 3SS9  [        SSS9  [        S SS9  [        S!SS9  e S nAff = f)#NT)targetdaemonu6     [ALGO] AlgoTraffic poller started — polling every sr   rm   z  NWALScanner Broadcast Serverz+  -----------------------------------------z#  Broadcast map : http://localhost:z/stormtracker_outagewatch.htmlz#  Call entry    : http://localhost:z/nwals_combined_admin.htmlz#  Calls API     : http://localhost:r^  z#  Traffic API   : http://localhost:r_  z#  Outages API   : http://localhost:z$/api/outages  [Florence + Sheffield]z#  Forecast API  : http://localhost:r[  z#  Alerts API    : http://localhost:z"/api/alerts  [20-second NWS cache]z#  Lightning API : http://localhost:z./api/lightning  [Spark primary / GLM fallback]u}     ─────────────────────────────────────────z"  Tailscale: http://100.77.235.11:z0.0.0.0z  SERVER FAILED TO STARTz  Port z is already in use or blocked: zP  Fix: close the other python.exe using port 9000, or run this temporarily with:z       set NWALS_PORT=9010z       python serve.pyz  SERVER LISTENING ON 0.0.0.0:)r#   chdirr   dirnameabspath__file__	threadingThreadrJ  startr
  r7  PORTr  rL  OSErrorserve_forever)r=   serverrI  s      r   
run_serverr    s   HHRWW__RWW__X678 	 0>AGGI	B;-q
QY]^	"D	
*$7	
7tD	/v5S
T\`a	/v5O
PX\]	/vZ
@M	/v\
B$O	/v5Y
Zbfg	/v]
C4P	/v5W
X`de	/v5c
dlpq	  J  SW  X	.tf4N
OW[\	"D()T):GD 
*4&
1>
  (5v<QC@M`hlm*$7&d3s   %F 
G)'=G$$G)__main__)J__doc__r   r#   rI   r~  r  urllib.requestr  http.serverr   r   urllib.parser   r   r   outage_sourcesr   r-   environrH   r  r   r   r  r  r  r   ra  r/  r0  r7  BASE_DIRr   r   r   r   rJ   rn   rj  rg  rh  Lockrf  ri  r  r   r   r   r   r   r   r   r2  r1  r    r'   r4   rQ   rh   rq   rx   r   r   r   r   r   r   r   r  rJ  rL  r  r  r  rW   r(   r   <module>r     su  
 7 6 6 E ! ' ,"**..v67ggll277??277??8+DE|T
:?y)77??277??845 ww||H.DE  ggll85RS 
 GGLL/0GGLL45GGLL01!  GGLL67GGLL;<GGLL78#     /    NN$   i "I. 8$H%  .." .."
")XD	>*K\B0&f"
>3
nr lK7& K7^1 
"H zL r(   