def key_of_coord(self, lon, lat, only_one=True): """ For a given coordinate, return the corresponding Navitia key Raise RegionNotFound if nothing found if only_one param is true return only one key, else return the list of possible keys """ p = geometry.Point(lon, lat) valid_instances = [] # a valid instance is an instance containing the coord and accessible by the user found_one = False for key, instance in self.instances.iteritems(): if instance.geom and instance.geom.contains(p): found_one = True jormun_instance = models.Instance.get_by_name(key) if not jormun_instance: raise RegionNotFound(custom_msg="technical problem, impossible " "to find region {r} in jormungandr database" .format(r=key)) if authentification.has_access(jormun_instance, abort=False): #TODO, pb how to check the api ? valid_instances.append(key) if valid_instances: if only_one: #If we have only one instance we return the 'best one' return choose_best_instance(valid_instances) else: return valid_instances elif found_one: raise RegionNotFound(custom_msg="coord {lon};{lat} are covered, " "but not in regions available for user" .format(lon=lon, lat=lat)) raise RegionNotFound(lon=lon, lat=lat)
def mock_key_by_id(object_id, only_one=True): if object_id == 'paris': if not paris_region: raise RegionNotFound('paris') return [self.regions[r] for r in paris_region] if object_id == 'lima': if not lima_region: raise RegionNotFound('lima') return [self.regions[r] for r in lima_region]
def mock_get_instances(region_str=None, lon=None, lat=None, object_id=None, api='ALL', only_one=True): if object_id == 'paris': if not paris_region: raise RegionNotFound('paris') return [self.regions[r] for r in paris_region] if object_id == 'lima': if not lima_region: raise RegionNotFound('lima') return [self.regions[r] for r in lima_region]
def _all_keys_of_coord(self, lon, lat): p = geometry.Point(lon, lat) instances = [i.name for i in self.instances.values() if i.has_point(p)] logging.getLogger(__name__).debug("all_keys_of_coord(self, {}, {}) returns {}".format(lon, lat, instances)) if not instances: raise RegionNotFound(lon=lon, lat=lat) return instances
def instances_comparator(instance1, instance2): """ compare the instances for journey computation we want first the non free instances then the free ones """ jormun_bdd_instance1 = models.Instance.get_by_name(instance1) jormun_bdd_instance2 = models.Instance.get_by_name(instance2) #TODO the is_free should be in the instances, no need to fetch the bdd for this if not jormun_bdd_instance1 and not jormun_bdd_instance2: raise RegionNotFound( custom_msg="technical problem, impossible " "to find region {i} and region{j} in jormungandr database".format( i=jormun_bdd_instance1, j=jormun_bdd_instance2)) if not jormun_bdd_instance1: return -1 if not jormun_bdd_instance2: return 1 if jormun_bdd_instance1.is_free != jormun_bdd_instance2.is_free: return jormun_bdd_instance1.is_free - jormun_bdd_instance2.is_free # TODO choose the smallest region ? # take the origin/destination coords into account and choose the region with the center nearest to those coords ? return 1
def set_request_timezone(region): from jormungandr import i_manager instance = i_manager.instances.get(region, None) if not instance: raise RegionNotFound(region) set_request_instance_timezone(instance)
def has_access(region, api, abort, user): """ Check the Authorization of the current user for this region and this API. If abort is True, the request is aborted with the appropriate HTTP code. Warning: Please this function is cached therefore it should not be dependent of the request context, so keep it as a pure function. """ if current_app.config.get('PUBLIC', False): #if jormungandr is on public mode we skip the authentification process return True if not user: #no user --> no need to continue, we can abort, a user is mandatory even for free region abort_request(user=user) model_instance = Instance.get_by_name(region) if not model_instance: if abort: raise RegionNotFound(region) return False if (model_instance.is_free and user.have_access_to_free_instances) or user.has_access( model_instance.id, api): return True else: if abort: abort_request(user=user) else: return False
def set_request_instance_timezone(instance): logger = logging.getLogger(__name__) if not instance: raise RegionNotFound() if not instance.timezone: logger.warning("region {} has no timezone".format(instance.name)) try: g.timezone = None except RuntimeError: pass # working outside application context... return tz = pytz.timezone(instance.timezone) if not tz: logger.warning( "impossible to find timezone: '{}' for region {}".format( instance.timezone, instance.name)) try: g.timezone = tz except RuntimeError: pass # working outside of an application context...
def get_regions(self, region_str=None, lon=None, lat=None, object_id=None, api='ALL', only_one=False): available_regions = [] if region_str and self.region_exists(region_str): available_regions = [region_str] elif lon and lat: available_regions = self._all_keys_of_coord(lon, lat) elif object_id: available_regions = self._all_keys_of_id(object_id) else: available_regions = self.instances.keys() valid_regions = self._filter_authorized_instances( available_regions, api) if valid_regions: return choose_best_instance( valid_regions) if only_one else valid_regions elif available_regions: authentication.abort_request(user=authentication.get_user()) raise RegionNotFound(region=region_str, lon=lon, lat=lat, object_id=object_id)
def get_regions(self, region_str=None, lon=None, lat=None, object_id=None, api='ALL', only_one=False): valid_instances = self.get_instances(region_str, lon, lat, object_id, api) if not valid_instances: raise RegionNotFound(region=region_str, lon=lon, lat=lat, object_id=object_id) if only_one: return choose_best_instance(valid_instances).name else: return [i.name for i in valid_instances]
def get_region(self, region_str=None, lon=None, lat=None, object_id=None): if region_str and self.region_exists(region_str): return region_str elif lon and lat: return self.key_of_coord(lon, lat) elif object_id: return self.key_of_id(object_id) else: raise RegionNotFound(region=region_str, lon=lon, lat=lat, object_id=object_id)
def _all_keys_of_id(self, object_id): instances = [] futures = {} for name, instance in self.instances.items(): futures[name] = gevent.spawn(instance.has_id, object_id) for name, future in futures.items(): if future.get(): instances.append(name) if not instances: raise RegionNotFound(object_id=object_id) return instances
def key_of_coord(self, lon, lat): """ Étant donné une coordonnée, retourne à quelle clef NAViTiA cela correspond Retourne None si on a rien trouvé """ p = geometry.Point(lon, lat) for key, instance in self.instances.iteritems(): if instance.geom and instance.geom.contains(p): return key raise RegionNotFound(lon=lon, lat=lat)
def key_of_id(self, object_id, only_one=True): """ Retrieves the key of the region of a given id if it's a coord calls key_of_coord Return one region key if only_one param is true, or None if it doesn't exists and the list of possible regions if only_one is set to False """ if object_id.count(";") == 1 or object_id[:6] == "coord:": if object_id.count(";") == 1: lon, lat = object_id.split(";") else: lon, lat = object_id[6:].split(":") try: flon = float(lon) flat = float(lat) except: raise RegionNotFound(object_id=object_id) return self.key_of_coord(flon, flat, only_one) ptobject = models.PtObject.get_from_uri(object_id) if ptobject: instances = ptobject.instances() if len(instances) > 0: available_instances = [] #tmp debug, replace with list generator for r in instances: if authentification.has_access(r, abort=False): logging.warn("instance {i} available for user".format(i=r)) available_instances.append(r.name) else: logging.warn("instance {i} not available for user".format(i=r)) if not available_instances: raise RegionNotFound(custom_msg="id {i} exists but not in regions available for user" .format(i=object_id)) if only_one: return choose_best_instance(available_instances) return available_instances #return [i.name for i in instances if authentification.has_access(i, abort=False)] raise RegionNotFound(object_id=object_id)
def dispatch(self, arguments, api, instance_obj=None, instance_name=None, request=None): if instance_obj: instance_name = instance_obj.name if instance_name in self.instances: instance = self.instances[instance_name] if api in instance.script.apis: api_func = getattr(instance.script, api) return api_func(arguments, instance) else: raise ApiNotFound(api) else: raise RegionNotFound(instance_name)
def tz(self): if not self._tz: instance = i_manager.instances.get(self.region, None) if not instance: raise RegionNotFound(self.region) tz_name = instance.timezone # TODO store directly the tz? if not tz_name: logging.Logger(__name__).warning("unknown timezone for region {}".format(self.region)) return None self._tz = (pytz.timezone(tz_name),) return self._tz[0]
def key_of_id(self, object_id): """ Retrieves the key of the region of a given id if it's a coord calls key_of_coord Return the region key, or None if it doesn't exists """ # Il s'agit d'une coordonnée if object_id.count(";") == 1 or object_id[:6] == "coord:": if object_id.count(";") == 1: lon, lat = object_id.split(";") else: lon, lat = object_id[6:].split(":") try: flon = float(lon) flat = float(lat) except: raise RegionNotFound(object_id=object_id) return self.key_of_coord(flon, flat) else: ptobject = models.PtObject.get_from_uri(object_id) if ptobject: instances = ptobject.instances() if len(instances) > 0: return instances[0].name raise RegionNotFound(object_id=object_id)
def dispatch(self, arguments, api, instance_name=None): if instance_name not in self.instances: raise RegionNotFound(instance_name) instance = self.instances[instance_name] scenario = instance.scenario(arguments.get('_override_scenario')) if not hasattr(scenario, api) or not callable(getattr(scenario, api)): raise ApiNotFound(api) publication_date = instance.publication_date api_func = getattr(scenario, api) resp = api_func(arguments, instance) if instance.publication_date != publication_date: self._clear_cache() return resp
def _all_keys_of_id(self, object_id): if object_id.count(";") == 1 or object_id[:6] == "coord:": if object_id.count(";") == 1: lon, lat = object_id.split(";") else: lon, lat = object_id[6:].split(":") try: flon = float(lon) flat = float(lat) except: raise InvalidArguments(object_id) return self._all_keys_of_coord(flon, flat) instances = [i.name for i in self.instances.values() if i.has_id(object_id)] if not instances: raise RegionNotFound(object_id=object_id) return instances
def compute_regions(args): """ method computing the region the journey has to be computed on The complexity comes from the fact that the regions in jormungandr can overlap. return the kraken instance keys rules are easy: we fetch the different regions the user can use for 'origin' and 'destination' we do the intersection and sort the list """ from_regions = set() to_regions = set() if args['origin']: from_regions = set(i_manager.get_instances(object_id=args['origin'])) # Note: if get_regions does not find any region, it raises a RegionNotFoundException if args['destination']: to_regions = set(i_manager.get_instances(object_id=args['destination'])) if not from_regions: # we didn't get any origin, the region is in the destination's list possible_regions = to_regions elif not to_regions: # we didn't get any origin, the region is in the destination's list possible_regions = from_regions else: # we need the intersection set possible_regions = from_regions.intersection(to_regions) logging.getLogger(__name__).debug( "orig region = {o}, dest region = {d} => set = {p}".format( o=from_regions, d=to_regions, p=possible_regions ) ) if not possible_regions: raise RegionNotFound( custom_msg="cannot find a region with {o} and {d} in the same time".format( o=args['origin'], d=args['destination'] ) ) sorted_regions = list(possible_regions) regions = sorted(sorted_regions, key=cmp_to_key(instances_comparator)) return [r.name for r in regions]
def compute_regions(args): """ Method computing the possible regions on which the journey can be queried The complexity comes from the fact that the regions in Jormungandr can overlap. Rules are : - Fetch the different regions that can be used for 'origin' and 'destination' - Intersect both lists and sort it :return: Kraken instance keys """ from_regions = set() to_regions = set() if args['origin']: from_regions = set(i_manager.get_instances(object_id=args['origin'])) # Note: if get_regions does not find any region, it raises a RegionNotFoundException if args['destination']: to_regions = set(i_manager.get_instances(object_id=args['destination'])) if not from_regions: # we didn't get any origin, the region is in the destination's list possible_regions = to_regions elif not to_regions: # we didn't get any origin, the region is in the destination's list possible_regions = from_regions else: # we need the intersection set possible_regions = from_regions.intersection(to_regions) logging.getLogger(__name__).debug( "orig region = {o}, dest region = {d} => set = {p}".format( o=from_regions, d=to_regions, p=possible_regions ) ) if not possible_regions: raise RegionNotFound( custom_msg="cannot find a region with {o} and {d} in the same time".format( o=args['origin'], d=args['destination'] ) ) sorted_regions = list(possible_regions) regions = sort_regions(sorted_regions) return [r.name for r in regions]
def compute_regions(args): """ method computing the region the journey has to be computed on The complexity comes from the fact that the regions in jormungandr can overlap. return the kraken instance key rules are easy: we fetch the different regions the user can use for 'origin' and 'destination' we do the intersection and sort the list """ _region = None possible_regions = set() from_regions = set() to_regions = set() if args['origin']: from_regions = set(i_manager.key_of_id(args['origin'], only_one=False)) #Note: if the key_of_id does not find any region, it raises a RegionNotFoundException if args['destination']: to_regions = set( i_manager.key_of_id(args['destination'], only_one=False)) if not from_regions: #we didn't get any origin, the region is in the destination's list possible_regions = to_regions elif not to_regions: #we didn't get any origin, the region is in the destination's list possible_regions = from_regions else: #we need the intersection set possible_regions = from_regions.intersection(to_regions) logging.debug("orig region = {o}, dest region = {d} => set = {p}".format( o=from_regions, d=to_regions, p=possible_regions)) if not possible_regions: raise RegionNotFound( custom_msg="cannot find a region with {o} and {d} in the same time" .format(o=args['origin'], d=args['destination'])) sorted_regions = list(possible_regions) _region = choose_best_instance(sorted_regions) return _region
def has_access(region, api, abort, user): """ Check the Authorization of the current user for this region and this API. If abort is True, the request is aborted with the appropriate HTTP code. Warning: Please this function is cached therefore it should not be dependent of the request context, so keep it as a pure function. """ # if jormungandr is on public mode or database is not accessible, we skip the authentication process logging.getLogger(__name__).debug('User "has_access" to region/api not cached') if current_app.config.get('PUBLIC', False) or (not can_connect_to_database()): return True if not user: # no user --> no need to continue, we can abort, a user is mandatory even for free region # To manage database error of the following type we should fetch one more time from database # Can connect to database but at least one table/attribute is not accessible due to transaction problem if can_read_user(): context = 'User is undefined, but table users is accessible in database' abort_request(user=user, context=context) else: return True try: model_instance = Instance.get_by_name(region) except Exception as e: logging.getLogger(__name__).error('No access to table Instance (error: {})'.format(e)) g.can_connect_to_database = False return True if not model_instance: if abort: raise RegionNotFound(region) return False if (model_instance.is_free and user.have_access_to_free_instances) or user.has_access( model_instance.id, api ): return True else: if abort: context = 'User has no permission to access this api {} or instance {}'.format( api, model_instance.id ) abort_request(user=user, context=context) else: return False
def choose_best_instance(instances): """ chose the best instance we want first the non free instances then the free ones """ best = None for i in instances: jormun_bdd_instance = models.Instance.get_by_name(i) #TODO the is_free should be in the instances, no need to fetch the bdd for this if not jormun_bdd_instance: raise RegionNotFound(custom_msg="technical problem, impossible " "to find region {i} in jormungandr database".format(i=i)) if not best or not jormun_bdd_instance.is_free: #TODO what to do if we have 2 free instances or 2 private instances ? best = jormun_bdd_instance return best.name
def set_request_timezone(region): logger = logging.getLogger(__name__) instance = i_manager.instances.get(region, None) if not instance: raise RegionNotFound(region) if not instance.timezone: logger.warn("region {} has no timezone".format(region)) g.timezone = None return tz = pytz.timezone(instance.timezone) if not tz: logger.warn("impossible to find timezone: '{}' for region {}".format( instance.timezone, region)) g.timezone = tz
def dispatch(self, arguments, api, instance_obj=None, instance_name=None, request=None): if instance_obj: instance_name = instance_obj.name if instance_name not in self.instances: raise RegionNotFound(instance_name) instance = self.instances[instance_name] if not hasattr(instance.scenario, api) or not callable( getattr(instance.scenario, api)): raise ApiNotFound(api) api_func = getattr(instance.scenario, api) resp = api_func(arguments, instance) if resp.HasField("publication_date") and\ instance.publication_date != resp.publication_date: self._clear_cache() instance.publication_date = resp.publication_date return resp
def _all_keys_of_id(self, object_id): if object_id.count(";") == 1 or object_id[:6] == "coord:": if object_id.count(";") == 1: lon, lat = object_id.split(";") else: lon, lat = object_id[6:].split(":") try: flon = float(lon) flat = float(lat) except: raise InvalidArguments(object_id) return self._all_keys_of_coord(flon, flat) instances = [] futures = {} for name, instance in self.instances.items(): futures[name] = gevent.spawn(instance.has_id, object_id) for name, future in futures.items(): if future.get(): instances.append(name) if not instances: raise RegionNotFound(object_id=object_id) return instances
def region_exists(self, region_str): if region_str in self.instances.keys(): return True else: raise RegionNotFound(region=region_str)