
    0,ik                    H   U d Z ddl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ZddlZddlZddlZddlmZmZ ddlmZm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Z e            Z ej        d          Z G d dej                  Z G d d          Zd Z e  G d de                      Z!ddZ"d Z#e$dk    rddl%Z% e"e$            e            5 Ze&                    e          '                    ej(        dk              )                                Z*de+d<   ddd           n# 1 swxY w Y    e,                    d          e*          Z-e-.                                 	 	  e%j/        d           # e0$ r e-1                                 Y dS w xY wdS ) u  
SmartStop Sentinel Connect – Module 2
Connector SDK & sample EHR adapter (Epic FHIR R4)
-------------------------------------------------
This module defines:
  • BaseConnector abstract class
  • ConnectorRegistry for hot-plug discovery
  • EpicFHIRAdapter demonstrating SMART-on-FHIR Bulk export
Any connector inheriting BaseConnector auto-registers in the registry and can be
launched by the Connector Manager side-car.
    )annotationsN)EpicAppConfig)SessionLocal)Depends)datetime	timedelta)AnyDict)r   )declarative_base)get_or_create_epic_token)Sessionzsentinel.connectorsc                  l    e Zd ZU dZdZded<   dZded<   dd
Zd Zd Z	e
j        d             ZddZdS )BaseConnectorz'Abstract base class for all connectors.unknownstrvendorNz
str | NoneversionconfigDict[str, Any]c                z    || _         |                    dd          | _        t          j                    | _        d S )Norg_iddemo_org)r   getr   	threadingEvent	stop_flag)selfr   s     [/var/www/html/blood_donation_traceloop/python/sentinel-connect/connectors/connector_base.py__init__zBaseConnector.__init__/   s1    !::h
;;"**    c                    t                               d| j                   | j                                         t          j        | j        d                                           dS )z3Spawn background thread to listen/poll vendor feed.z[%s] starting connectorT)targetdaemonN)	LOGGERinfor   r   clearr   Thread_runstartr   s    r   r)   zBaseConnector.start8   sW    -t{;;;	$777==?????r    c                x    t                               d| j                   | j                                         d S )Nz[%s] stopping connector)r$   r%   r   r   setr*   s    r   stopzBaseConnector.stop>   s3    -t{;;;r    c                    dS )z?Blocking loop. Derived classes implement vendor-specific logic.N r*   s    r   r(   zBaseConnector._runB   s      r    eventc                .   ddl }ddl}t          j        dd          }t          j        d          }d| dd}	  |j        |||                    |          d	
           dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)z6Send event to Sentinel Connect ingest API (HTTP POST).r   NINGEST_ENDPOINTzhttp://sentinel-gw/eventsCONNECTOR_TOKENBearer zapplication/json)AuthorizationzContent-Type   )headersdatatimeoutzemit failed: %s)	jsonrequestsosgetenvpostdumps	Exceptionr$   error)r   r0   r:   r;   urltokenr7   excs           r   emitzBaseConnector.emitJ   s    i)+FGG	+,,.u...
 
	1HM#wTZZ5F5FPQRRRRRR 	1 	1 	1LL*C000000000	1s   'A$ $
B.BB)r   r   )r0   r   )__name__
__module____qualname____doc__r   __annotations__r   r   r)   r-   abcabstractmethodr(   rE   r/   r    r   r   r   '   s         11 FG+ + + +@ @ @   	N N N1 1 1 1 1 1r    r   c                  N    e Zd ZU i Zded<   ed
d            Zedd            Zd	S )ConnectorRegistryzDict[str, type[BaseConnector]]	_registryconnector_clstype[BaseConnector]c                H    || j         |j                                        <   |S N)rO   r   lower)clsrP   s     r   registerzConnectorRegistry.register_   s#    6Cm*00223r    vendor_namer   c                Z    | j                             |                                          S rS   )rO   r   rT   )rU   rW   s     r   r   zConnectorRegistry.getd   s$    }  !2!2!4!4555r    N)rP   rQ   )rW   r   )rF   rG   rH   rO   rJ   classmethodrV   r   r/   r    r   rN   rN   \   se         02I2222   [ 6 6 6 [6 6 6r    rN   c                6    t                               |           S rS   )rN   rV   )rU   s    r   	connectorr[   i   s    %%c***r    c                      e Zd ZdZdZd ZdS )EpicFHIRAdapterEpiczFHIR R4c           	        | j         d         }| j         d         }| j         d         }| j                             dd          }| j                             dd          }t          | j                             dd	                    }t          d
          }t	          j                    }|j                            dd| i           t          j	                    t          |          z
                                  dz   }	| j                                        s| d}
|	|d}|                    |
|d          }|j        dv r\t                              d| j                   t          ||          }d| |j        d<   | j                            |dz             |j        dk    r|j                            d          }|r| j                                        s|                    |d          }|j        dk    r|                                }|                    dg           D ]X}|                    |d                   j                                        }|D ]!}|                     dd|| j        d           "Yn|r| j                                        t          j	                                                    dz   }	| j                            |dz             | j                                        d S d S ) N	client_id	fhir_base	token_urlr   r   bulk_patientsPatientpoll_minutes      r5   r4   )minutesZz/$export)_since_type<   )paramsr9   )i  i  z#[%s] Epic token expired, refreshing)r`   rb      zContent-Location
   )r9      outputrB   	epic_fhirObservation)source
event_typerawr   )r   r   intr   r;   r   r7   updater   utcnowr   	isoformatr   is_setstatus_coder$   warningr   waitr:   text
splitlinesrE   r   )r   r`   ra   rb   r   rc   polling_intervalaccess_tokensession
last_sincebulk_urlrm   respjob_urljob_respmanifestrq   ndjsonlines                      r   r(   zEpicFHIRAdapter._runt   s"   K,	K,	K,	:66CCt{~rBBCC/22"$$1I<1I1IJKKKo'')<L*M*M*MMXXZZ]``
.'')) "	7#---H *]CCF;;x;CCD :--DdkRRR7''      4L\3K3K0##$4r$9:::3&&,**+=>> dn&;&;&=&= &{{7B{??H+s22#+==??&.ll8R&@&@ # #F%,[[%?%?%D%O%O%Q%QF(. # # $		.92?+/.2k	+" +" !# !# !# !##   dn&;&;&=&=  &_..88::S@
N 02 5666E .'')) "	7 "	7 "	7 "	7 "	7r    N)rF   rG   rH   r   r   r(   r/   r    r   r]   r]   o   s-        FG17 17 17 17 17r    r]   pkg_namer   c                   t          j        |           }t          |dd          }|st                              d|            dS t          j        ||j        dz             D ]\  }}}|st          j        |           dS )zAImport all modules in a package to trigger @connector decorators.__path__Nz+autodiscover: %s is not a package, skipping.)	importlibimport_modulegetattrr$   r}   pkgutilwalk_packagesrF   )r   packagepkg_path_modnameispkgs         r   autodiscoverr      s    %h//Gw
D11H DhOOO$28W=MPS=STT - -7E 	-#G,,,- -r    c               #     K   t                      } 	 | V  |                                  d S # |                                  w xY wrS   )r   close)dbs    r   get_dbr      s@      	B









s	   , A__main__rg   zEpicAppConfig | NonecfgepicT)r   r   )2rI   
__future__r   rK   r   
epic_tokenr   r   r   fastapir   loggingr<   r   r   r   r   typingr	   r
   sqlalchemy.ormr   token_servicer   r   r;   Base	getLoggerr$   ABCr   rN   r[   r]   r   r   rF   timequeryfilteridfirstr   rJ   r   r   r)   sleepKeyboardInterruptr-   r/   r    r   <module>r      sR  
 
 
 # " " " " " 



     $ $ $ $ $ $              				      ( ( ( ( ( ( ( (                + + + + + + 2 2 2 2 2 2 " " " " " " 		0	1	1
/1 /1 /1 /1 /1CG /1 /1 /1j
6 
6 
6 
6 
6 
6 
6 
6+ + + 57 57 57 57 57m 57 57 57v	- 	- 	- 	-   zKKK L 
 
2HH]##vm&!+,,uww 	 	
 	
 	
 	

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 )  ((--DJJLLL	DJqMMM	   		% s%   A	D22D69D60F FF