コード例 #1
0
 def from_state(self, state):
     assert state
     self.app_id = state.get('app_id') or self.app_id
     self.scope = state.get('scope') or self.scope
     self.redirect = state.get('redirect') or self.redirect
     self.patient_id = state.get('patient_id') or self.patient_id
     self.launch_context = state.get(
         'launch_context') or self.launch_context
     self.server = FHIRServer(self, state=state.get('server'))
コード例 #2
0
ファイル: client.py プロジェクト: udplabs/zartan
    def __init__(self, settings=None, state=None, save_func=lambda x: x):
        self.app_id = None
        self.app_secret = None
        """ The app-id for the app this client is used in. """

        self.server = None
        self.scope = scope_default
        self.redirect = None
        """ The redirect-uri that will be used to redirect after authorization. """

        self.launch_token = None
        """ The token/id provided at launch, if any. """

        self.launch_context = None
        """ Context parameters supplied by the server during launch. """

        self.wants_patient = True
        """ If true and launched without patient, will add the correct scope
        to indicate that the server should prompt for a patient after login. """

        self.patient_id = None
        self._patient = None

        #Handle downscoping.
        self.authorized_scopes = None

        if save_func is None:
            raise Exception(
                "Must supply a save_func when initializing the SMART client")
        self._save_func = save_func

        # init from state
        if state is not None:
            self.from_state(state)

        # init from settings dict
        elif settings is not None:
            if not 'app_id' in settings:
                raise Exception("Must provide 'app_id' in settings dictionary")
            if not 'api_base' in settings:
                raise Exception(
                    "Must provide 'api_base' in settings dictionary")

            self.app_id = settings['app_id']
            self.app_secret = settings.get('app_secret')
            self.redirect = settings.get('redirect_uri')
            self.patient_id = settings.get('patient_id')
            self.scope = settings.get('scope', self.scope)
            self.launch_token = settings.get('launch_token')
            self.server = FHIRServer(self, base_uri=settings['api_base'])
        else:
            raise Exception(
                "Must either supply settings or a state upon client initialization"
            )
コード例 #3
0
ファイル: client.py プロジェクト: udplabs/zartan
 def from_state(self, state):
     assert state
     self.app_id = state.get('app_id') or self.app_id
     self.app_secret = state.get('app_secret') or self.app_secret
     self.scope = state.get('scope') or self.scope
     self.redirect = state.get('redirect') or self.redirect
     self.patient_id = state.get('patient_id') or self.patient_id
     self.launch_token = state.get('launch_token') or self.launch_token
     self.launch_context = state.get(
         'launch_context') or self.launch_context
     self.authorized_scopes = state.get(
         'authorized_scopes') or self.authorized_scopes
     self.server = FHIRServer(self, state=state.get('server'))
コード例 #4
0
ファイル: client.py プロジェクト: ohmtrivedi/client-py
 def __init__(self, settings=None, state=None, save_func=lambda x:x):
     self.app_id = None
     self.server = None
     self.auth = None
     self.launch_context = None
     self._patient = None
     
     # init from state
     if state is not None:
         self.from_state(state)
     
     # init from settings dict
     elif settings is not None:
         self.app_id = settings['app_id']
         self.server = FHIRServer(base_uri=settings['api_base'])
         
         scope = scope_default
         if 'launch_token' in settings:
             scope = ' launch:'.join([scope, settings['launch_token']])
         else:
             scope = ' '.join([scope_nolaunch, scope])
         
         auth_type = settings.get('auth_type')
         redirect = settings.get('redirect_uri')
         patient_id = settings.get('patient_id')
         if patient_id:
             self.auth = FHIRAuth.create(auth_type, app_id=self.app_id, patient_id=patient_id)
         else:
             self.auth = FHIRAuth.create(auth_type, app_id=self.app_id, scope=scope, redirect_uri=redirect)
     else:
         raise Exception("Must either supply settings or a state upon client initialization")
 
     self._save_func = save_func
     self.save_state()
コード例 #5
0
ファイル: client.py プロジェクト: ohmtrivedi/client-py
 def from_state(self, state):
     assert state
     self.app_id = state.get('app_id') or self.app_id
     self.launch_context = state.get('launch_context') or self.launch_context
     self.server = FHIRServer(state=state.get('server'))
     self.auth = FHIRAuth.create(state.get('auth_type'), app_id=self.app_id, state=state.get('auth'))
     if self.auth is not None and self.auth.ready is not None:
         self.server.did_authorize(self.auth)
コード例 #6
0
ファイル: client.py プロジェクト: bchaballout/client-py
 def from_state(self, state):
     assert state
     self.app_id = state.get('app_id') or self.app_id
     self.scope = state.get('scope') or self.scope
     self.redirect = state.get('redirect') or self.redirect
     self.patient_id = state.get('patient_id') or self.patient_id
     self.launch_context = state.get('launch_context') or self.launch_context
     self.server = FHIRServer(self, state=state.get('server'))
コード例 #7
0
    def __init__(self, settings=None, state=None, save_func=lambda x: x):
        self.app_id = None
        self.server = None
        self.scope = None
        self.redirect = None

        self.launch_context = None
        """ Context parameters supplied by the server during launch. """

        self.wants_patient = True
        """ If true and launched without patient, will add the correct scope
        to indicate that the server should prompt for a patient after login. """

        self.patient_id = None
        self._patient = None

        if save_func is None:
            raise Exception(
                "Must supply a save_func when initializing the SMART client")
        self._save_func = save_func

        # init from state
        if state is not None:
            self.from_state(state)

        # init from settings dict
        elif settings is not None:
            self.app_id = settings['app_id']
            self.redirect = settings.get('redirect_uri')
            self.patient_id = settings.get('patient_id')
            scope = scope_default
            if 'launch_token' in settings:
                self.scope = ' launch:'.join([scope, settings['launch_token']])
            elif self.patient_id is None and self.wants_patient:
                self.scope = ' '.join([scope_nolaunch, scope])
            else:
                self.scope = scope
            self.server = FHIRServer(self, base_uri=settings['api_base'])
        else:
            raise Exception(
                "Must either supply settings or a state upon client initialization"
            )
コード例 #8
0
ファイル: client.py プロジェクト: smart-on-fhir/client-py
 def __init__(self, settings=None, state=None, save_func=lambda x:x):
     self.app_id = None
     self.app_secret = None
     """ The app-id for the app this client is used in. """
     
     self.server = None
     self.scope = scope_default
     self.redirect = None
     """ The redirect-uri that will be used to redirect after authorization. """
     
     self.launch_token = None
     """ The token/id provided at launch, if any. """
     
     self.launch_context = None
     """ Context parameters supplied by the server during launch. """
     
     self.wants_patient = True
     """ If true and launched without patient, will add the correct scope
     to indicate that the server should prompt for a patient after login. """
     
     self.patient_id = None
     self._patient = None
     
     if save_func is None:
         raise Exception("Must supply a save_func when initializing the SMART client")
     self._save_func = save_func
     
     # init from state
     if state is not None:
         self.from_state(state)
     
     # init from settings dict
     elif settings is not None:
         if not 'app_id' in settings:
             raise Exception("Must provide 'app_id' in settings dictionary")
         if not 'api_base' in settings:
             raise Exception("Must provide 'api_base' in settings dictionary")
         
         self.app_id = settings['app_id']
         self.app_secret = settings.get('app_secret')
         self.redirect = settings.get('redirect_uri')
         self.patient_id = settings.get('patient_id')
         self.scope = settings.get('scope', self.scope)
         self.launch_token = settings.get('launch_token')
         self.server = FHIRServer(self, base_uri=settings['api_base'])
     else:
         raise Exception("Must either supply settings or a state upon client initialization")
コード例 #9
0
ファイル: client.py プロジェクト: bchaballout/client-py
 def __init__(self, settings=None, state=None, save_func=lambda x:x):
     self.app_id = None
     self.server = None
     self.scope = None
     self.redirect = None
     
     self.launch_context = None
     """ Context parameters supplied by the server during launch. """
     
     self.wants_patient = True
     """ If true and launched without patient, will add the correct scope
     to indicate that the server should prompt for a patient after login. """
     
     self.patient_id = None
     self._patient = None
     
     if save_func is None:
         raise Exception("Must supply a save_func when initializing the SMART client")
     self._save_func = save_func
     
     # init from state
     if state is not None:
         self.from_state(state)
     
     # init from settings dict
     elif settings is not None:
         self.app_id = settings['app_id']
         self.redirect = settings.get('redirect_uri')
         self.patient_id = settings.get('patient_id')
         scope = scope_default
         if 'launch_token' in settings:
             self.scope = ' launch:'.join([scope, settings['launch_token']])
         elif self.patient_id is None and self.wants_patient:
             self.scope = ' '.join([scope_nolaunch, scope])
         else:
             self.scope = scope
         self.server = FHIRServer(self, base_uri=settings['api_base'])
     else:
         raise Exception("Must either supply settings or a state upon client initialization")
コード例 #10
0
class FHIRClient(object):
    """ Instances of this class handle authorizing and talking to SMART on FHIR
    servers.
    
    The settings dictionary supports:
    
        - `app_id`*: Your app/client-id, e.g. 'my_web_app'
        - `app_secret`*: Your app/client-secret
        - `api_base`*: The FHIR service to connect to, e.g. 'https://fhir-api-dstu2.smarthealthit.org'
        - `redirect_uri`: The callback/redirect URL for your app, e.g. 'http://localhost:8000/fhir-app/' when testing locally
        - `patient_id`: The patient id against which to operate, if already known
        - `scope`: Space-separated list of scopes to request, if other than default
        - `launch_token`: The launch token
    """
    def __init__(self, settings=None, state=None, save_func=lambda x: x):
        self.app_id = None
        self.app_secret = None
        """ The app-id for the app this client is used in. """

        self.server = None
        self.scope = scope_default
        self.redirect = None
        """ The redirect-uri that will be used to redirect after authorization. """

        self.launch_token = None
        """ The token/id provided at launch, if any. """

        self.launch_context = None
        """ Context parameters supplied by the server during launch. """

        self.wants_patient = True
        """ If true and launched without patient, will add the correct scope
        to indicate that the server should prompt for a patient after login. """

        self.patient_id = None
        self._patient = None

        if save_func is None:
            raise Exception(
                "Must supply a save_func when initializing the SMART client")
        self._save_func = save_func

        # init from state
        if state is not None:
            self.from_state(state)

        # init from settings dict
        elif settings is not None:
            if not 'app_id' in settings:
                raise Exception("Must provide 'app_id' in settings dictionary")
            if not 'api_base' in settings:
                raise Exception(
                    "Must provide 'api_base' in settings dictionary")

            self.app_id = settings['app_id']
            self.app_secret = settings.get('app_secret')
            self.redirect = settings.get('redirect_uri')
            self.patient_id = settings.get('patient_id')
            self.scope = settings.get('scope', self.scope)
            self.launch_token = settings.get('launch_token')
            self.server = FHIRServer(self, base_uri=settings['api_base'])
        else:
            raise Exception(
                "Must either supply settings or a state upon client initialization"
            )

    # MARK: Authorization

    @property
    def desired_scope(self):
        """ Ensures `self.scope` is completed with launch scopes, according to
        current client settings.
        """
        scope = self.scope
        if self.launch_token is not None:
            scope = ' '.join([scope_haslaunch, scope])
        elif self.patient_id is None and self.wants_patient:
            scope = ' '.join([scope_patientlaunch, scope])
        return scope

    @property
    def ready(self):
        """ Returns True if the client is ready to make API calls (e.g. there
        is an access token or this is an open server).
        
        :returns: True if the server can make authenticated calls
        """
        return self.server.ready if self.server is not None else False

    def prepare(self):
        """ Returns True if the client is ready to make API calls (e.g. there
        is an access token or this is an open server). In contrast to the
        `ready` property, this method will fetch the server's capability
        statement if it hasn't yet been fetched.
        
        :returns: True if the server can make authenticated calls
        """
        if self.server:
            if self.server.ready:
                return True
            return self.server.prepare()
        return False

    @property
    def authorize_url(self):
        """ The URL to use to receive an authorization token.
        """
        return self.server.authorize_uri if self.server is not None else None

    def handle_callback(self, url):
        """ You can call this to have the client automatically handle the
        auth callback after the user has logged in.
        
        :param str url: The complete callback URL
        """
        ctx = self.server.handle_callback(
            url) if self.server is not None else None
        self._handle_launch_context(ctx)

    def reauthorize(self):
        """ Try to reauthorize with the server.
        
        :returns: A bool indicating reauthorization success
        """
        ctx = self.server.reauthorize() if self.server is not None else None
        self._handle_launch_context(ctx)
        return self.launch_context is not None

    def _handle_launch_context(self, ctx):
        logger.debug("SMART: Handling launch context: {0}".format(ctx))
        if 'patient' in ctx:
            #print('Patient id was {0}, row context is {1}'.format(self.patient_id, ctx))
            self.patient_id = ctx['patient']  # TODO: TEST THIS!
        if 'id_token' in ctx:
            logger.warning("SMART: Received an id_token, ignoring")
        self.launch_context = ctx
        self.save_state()

    # MARK: Current Patient

    @property
    def patient(self):
        if self._patient is None and self.patient_id is not None and self.ready:
            import models.patient
            try:
                logger.debug("SMART: Attempting to read Patient {0}".format(
                    self.patient_id))
                self._patient = models.patient.Patient.read(
                    self.patient_id, self.server)
            except FHIRUnauthorizedException as e:
                if self.reauthorize():
                    logger.debug(
                        "SMART: Attempting to read Patient {0} after reauthorizing"
                        .format(self.patient_id))
                    self._patient = models.patient.Patient.read(
                        self.patient_id, self.server)
            except FHIRNotFoundException as e:
                logger.warning("SMART: Patient with id {0} not found".format(
                    self.patient_id))
                self.patient_id = None
            self.save_state()

        return self._patient

    def human_name(self, human_name_instance):
        """ Formats a `HumanName` instance into a string.
        """
        if human_name_instance is None:
            return 'Unknown'

        parts = []
        for n in [human_name_instance.prefix, human_name_instance.given]:
            if n is not None:
                parts.extend(n)
        if human_name_instance.family:
            parts.append(human_name_instance.family)
        if human_name_instance.suffix and len(human_name_instance.suffix) > 0:
            if len(parts) > 0:
                parts[len(parts) - 1] = parts[len(parts) - 1] + ','
            parts.extend(human_name_instance.suffix)

        return ' '.join(parts) if len(parts) > 0 else 'Unnamed'

    # MARK: State

    def reset_patient(self):
        self.launch_token = None
        self.launch_context = None
        self.patient_id = None
        self._patient = None
        self.save_state()

    @property
    def state(self):
        return {
            'app_id': self.app_id,
            'app_secret': self.app_secret,
            'scope': self.scope,
            'redirect': self.redirect,
            'patient_id': self.patient_id,
            'server': self.server.state,
            'launch_token': self.launch_token,
            'launch_context': self.launch_context,
        }

    def from_state(self, state):
        assert state
        self.app_id = state.get('app_id') or self.app_id
        self.app_secret = state.get('app_secret') or self.app_secret
        self.scope = state.get('scope') or self.scope
        self.redirect = state.get('redirect') or self.redirect
        self.patient_id = state.get('patient_id') or self.patient_id
        self.launch_token = state.get('launch_token') or self.launch_token
        self.launch_context = state.get(
            'launch_context') or self.launch_context
        self.server = FHIRServer(self, state=state.get('server'))

    def save_state(self):
        self._save_func(self.state)
コード例 #11
0
ファイル: client.py プロジェクト: sync-for-science/client-py
class FHIRClient(object):
    """ Instances of this class handle authorizing and talking to SMART on FHIR
    servers.
    
    The settings dictionary supports:
    
        - `app_id`*: Your app/client-id, e.g. 'my_web_app'
        - `api_base`*: The FHIR service to connect to, e.g. 'https://fhir-api-dstu2.smarthealthit.org'
        - `redirect_uri`: The callback/redirect URL for your app, e.g. 'http://localhost:8000/fhir-app/' when testing locally
        - `patient_id`: The patient id against which to operate, if already known
        - `scope`: Space-separated list of scopes to request, if other than default
        - `launch_token`: The launch token
    """
    
    def __init__(self, settings=None, state=None, save_func=lambda x:x):
        self.app_id = None
        """ The app-id for the app this client is used in. """
        
        self.server = None
        self.scope = scope_default
        self.redirect = None
        """ The redirect-uri that will be used to redirect after authorization. """
        
        self.launch_token = None
        """ The token/id provided at launch, if any. """
        
        self.launch_context = None
        """ Context parameters supplied by the server during launch. """
        
        self.wants_patient = True
        """ If true and launched without patient, will add the correct scope
        to indicate that the server should prompt for a patient after login. """
        
        self.patient_id = None
        self._patient = None
        
        if save_func is None:
            raise Exception("Must supply a save_func when initializing the SMART client")
        self._save_func = save_func
        
        # init from state
        if state is not None:
            self.from_state(state)
        
        # init from settings dict
        elif settings is not None:
            if not 'app_id' in settings:
                raise Exception("Must provide 'app_id' in settings dictionary")
            if not 'api_base' in settings:
                raise Exception("Must provide 'api_base' in settings dictionary")
            
            self.app_id = settings['app_id']
            self.redirect = settings.get('redirect_uri')
            self.patient_id = settings.get('patient_id')
            self.scope = settings.get('scope', self.scope)
            self.launch_token = settings.get('launch_token')
            self.server = FHIRServer(self, base_uri=settings['api_base'])
        else:
            raise Exception("Must either supply settings or a state upon client initialization")
    
    
    # MARK: Authorization
    
    @property
    def desired_scope(self):
        """ Ensures `self.scope` is completed with launch scopes, according to
        current client settings.
        """
        scope = self.scope
        if self.launch_token is not None:
            scope = ' '.join([scope_haslaunch, scope])
        elif self.patient_id is None and self.wants_patient:
            scope = ' '.join([scope_patientlaunch, scope])
        return scope
    
    @property
    def ready(self):
        """ Returns True if the client is ready to make API calls (e.g. there
        is an access token or this is an open server).
        
        :returns: True if the server can make authenticated calls
        """
        return self.server.ready if self.server is not None else False
    
    def prepare(self):
        """ Returns True if the client is ready to make API calls (e.g. there
        is an access token or this is an open server). In contrast to the
        `ready` property, this method will fetch the server's Conformance
        statement if it hasn't yet been fetched.
        
        :returns: True if the server can make authenticated calls
        """
        if self.server:
            if self.server.ready:
                return True
            return self.server.prepare()
        return False
    
    @property
    def authorize_url(self):
        """ The URL to use to receive an authorization token.
        """
        return self.server.authorize_uri if self.server is not None else None
    
    def handle_callback(self, url):
        """ You can call this to have the client automatically handle the
        auth callback after the user has logged in.
        
        :param str url: The complete callback URL
        """
        ctx = self.server.handle_callback(url) if self.server is not None else None
        self._handle_launch_context(ctx)
    
    def reauthorize(self):
        """ Try to reauthorize with the server.
        
        :returns: A bool indicating reauthorization success
        """
        ctx = self.server.reauthorize(self.server) if self.server is not None else None
        self._handle_launch_context(ctx)
        return self.launch_context is not None
    
    def _handle_launch_context(self, ctx):
        logging.debug("SMART: Handling launch context: {0}".format(ctx))
        if 'patient' in ctx:
            #print('Patient id was {0}, row context is {1}'.format(self.patient_id, ctx))
            self.patient_id = ctx['patient']        # TODO: TEST THIS!
        if 'id_token' in ctx:
            logging.warning("SMART: Received an id_token, ignoring")
        self.launch_context = ctx
        self.save_state()
    
    
    # MARK: Current Patient
    
    @property
    def patient(self):
        if self._patient is None and self.patient_id is not None and self.ready:
            import models.patient
            try:
                logging.debug("SMART: Attempting to read Patient {0}".format(self.patient_id))
                self._patient = models.patient.Patient.read(self.patient_id, self.server)
            except FHIRUnauthorizedException as e:
                if self.reauthorize():
                    logging.debug("SMART: Attempting to read Patient {0} after reauthorizing"
                        .format(self.patient_id))
                    self._patient = models.patient.Patient.read(self.patient_id, self.server)
            except FHIRNotFoundException as e:
                logging.warning("SMART: Patient with id {0} not found".format(self.patient_id))
                self.patient_id = None
            self.save_state()
        
        return self._patient
    
    def human_name(self, human_name_instance):
        """ Formats a `HumanName` instance into a string.
        """
        if human_name_instance is None:
            return 'Unknown'
        
        parts = []
        for n in [human_name_instance.prefix, human_name_instance.given, human_name_instance.family, human_name_instance.suffix]:
            if n is not None:
                parts.extend(n)
        
        return ' '.join(parts) if len(parts) > 0 else 'Unnamed'
    
    
    # MARK: State
    
    def reset_patient(self):
        self.launch_token = None
        self.launch_context = None
        self.patient_id = None
        self._patient = None
        self.save_state()
    
    @property
    def state(self):
        return {
            'app_id': self.app_id,
            'scope': self.scope,
            'redirect': self.redirect,
            'patient_id': self.patient_id,
            'server': self.server.state,
            'launch_token': self.launch_token,
            'launch_context': self.launch_context,
        }
    
    def from_state(self, state):
        assert state
        self.app_id = state.get('app_id') or self.app_id
        self.scope = state.get('scope') or self.scope
        self.redirect = state.get('redirect') or self.redirect
        self.patient_id = state.get('patient_id') or self.patient_id
        self.launch_token = state.get('launch_token') or self.launch_token
        self.launch_context = state.get('launch_context') or self.launch_context
        self.server = FHIRServer(self, state=state.get('server'))
    
    def save_state (self):
        self._save_func(self.state)
コード例 #12
0
ファイル: client.py プロジェクト: ohmtrivedi/client-py
class FHIRClient(object):
    """ Instances of this class handle authorizing and talking to SMART on FHIR
    servers.
    
    The settings dictionary supports:
    
        - `app_id`: Your app/client-id, e.g. 'my_web_app'
        - `api_base`: The SMART service to connect to, e.g. 'https://fhir-api.smarthealthit.org'
        - `auth_type`: The authorization type, supports "oauth2" and "none". Defaults to "oauth2" if omitted
        - `redirect_uri`: The callback/redirect URL for your app, e.g. 'http://localhost:8000/fhir-app/' when testing locally
    """
    
    def __init__(self, settings=None, state=None, save_func=lambda x:x):
        self.app_id = None
        self.server = None
        self.auth = None
        self.launch_context = None
        self._patient = None
        
        # init from state
        if state is not None:
            self.from_state(state)
        
        # init from settings dict
        elif settings is not None:
            self.app_id = settings['app_id']
            self.server = FHIRServer(base_uri=settings['api_base'])
            
            scope = scope_default
            if 'launch_token' in settings:
                scope = ' launch:'.join([scope, settings['launch_token']])
            else:
                scope = ' '.join([scope_nolaunch, scope])
            
            auth_type = settings.get('auth_type')
            redirect = settings.get('redirect_uri')
            patient_id = settings.get('patient_id')
            if patient_id:
                self.auth = FHIRAuth.create(auth_type, app_id=self.app_id, patient_id=patient_id)
            else:
                self.auth = FHIRAuth.create(auth_type, app_id=self.app_id, scope=scope, redirect_uri=redirect)
        else:
            raise Exception("Must either supply settings or a state upon client initialization")
    
        self._save_func = save_func
        self.save_state()
    
    # MARK: Authorization
    
    @property
    def auth_type(self):
        return self.auth.auth_type if self.auth else None
    
    @property
    def ready(self):
        """ Returns True if the client is ready to make API calls (e.g. there
        is an access token).
        """
        return self.auth.ready if self.auth is not None else False
    
    @property
    def authorize_url(self):
        """ The URL to use to receive an authorization token.
        """
        auth_url = self.auth.authorize_url(self.server) if self.auth is not None else None
        self.save_state()
        return auth_url
    
    def handle_callback(self, url):
        """ You can call this to have the client automatically handle the
        auth callback after the user has logged in.
        
        :param str url: The complete callback URL
        """
        self.launch_context = self.auth.handle_callback(url, self.server)
        self._set_authorized(True)
        self.save_state()
 
    def reauthorize(self):
        """ Try to reauthorize with the server; handled by our `auth` instance.
        
        :returns: A bool indicating reauthorization success
        """
        ctx = self.auth.reauthorize(self.server) if self.auth is not None else None
        if ctx is not None:
            self.launch_context = ctx
            return True
        return False
    
    
    def _set_authorized(self, flag):
        """ Internal method used to sync server and auth. """
        if flag:
            self.server.did_authorize(self.auth)
        else:
            self.server.did_authorize(None)
            self.auth.reset()
    
    
    # MARK: Current Patient
    
    @property
    def patient_id(self):
        return self.auth.patient_id
    
    @property
    def patient(self):
        if self._patient is None and self.ready:
            try:
                self._patient = patient.Patient.read(self.patient_id, self.server)
            except FHIRUnauthorizedException as e:
                if self.reauthorize():
                    self._patient = patient.Patient.read(self.patient_id, self.server)
                else:
                    self._set_authorized(False)
         
        return self._patient
    
    def human_name(self, human_name_instance):
        """ Formats a `HumanName` instance into a string.
        """
        if human_name_instance is None:
            return 'Unknown'
        
        parts = []
        for n in [human_name_instance.prefix, human_name_instance.given, human_name_instance.family, human_name_instance.suffix]:
            if n is not None:
                parts.extend(n)
        
        return ' '.join(parts) if len(parts) > 0 else 'Unnamed'
    
    def string_gender(self, gender_concept):
        """ Takes a `CodeableConcept` instance and returns either 'male',
        'female' or None.
        
        TODO: inspect coding system of the concepts and decide more thoroughly
        """
        if gender_concept is not None \
            and gender_concept.coding is not None \
            and len(gender_concept.coding) > 0:
            
            if gender_concept.coding[0].code: # and 'http://hl7.org/fhir/v3/AdministrativeGender' == gender_concept.coding[0].system:
                return 'male' if 'M' == gender_concept.coding[0].code else 'female'
        return None
    
    
    # MARK: State
    
    @property
    def state(self):
        return {
            'app_id': self.app_id,
            'server': self.server.state,
            'auth_type': self.auth_type,
            'auth': self.auth.state,
            'launch_context': self.launch_context,
        }
    
    def from_state(self, state):
        assert state
        self.app_id = state.get('app_id') or self.app_id
        self.launch_context = state.get('launch_context') or self.launch_context
        self.server = FHIRServer(state=state.get('server'))
        self.auth = FHIRAuth.create(state.get('auth_type'), app_id=self.app_id, state=state.get('auth'))
        if self.auth is not None and self.auth.ready is not None:
            self.server.did_authorize(self.auth)
    
    def save_state (self):
        self._save_func(self.state)