# -*- coding: utf-8 -*- from flask import current_app from flask.globals import _request_ctx_stack from collections import OrderedDict from datetime import datetime from urlparse import urlunsplit import pytz import json import sys from . import db def dict_to_geojson(d_in): """ Encode a dict representing a GeoJSON object into a JSON string. This is needed because spatialite's GeoJSON parser is not really JSON-compliant and it fails when keys are not in the right order. """ d = OrderedDict() d['type'] = d_in['type'] if 'crs' in d_in: d['crs'] = d_in['crs'] # our spatialite geo column is defined with EPSG SRID 4326 (WGS 84) d['crs'] = {'type': 'name', 'properties': {'name': 'urn:ogc:def:crs:EPSG:4326'}} if 'bbox' in d_in: d['bbox'] = d_in['bbox'] d['coordinates'] = d_in['coordinates'] return json.dumps(d) def check_geojson_spatialite(_gjson): """ Checks if a GeoJSON dict is understood by spatialite >>> check_geojson_spatialite({'type': 'NOPE', 'coordinates': []}) False >>> check_geojson_spatialite({'type': 'Polygon', 'coordinates': [ ... [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]] ... ]}) True """ gjson = dict_to_geojson(_gjson) return bool(db.session.query(db.func.GeomFromGeoJSON(gjson) != None).first()[0]) def utcnow(): """ Return the current UTC date and time as a datetime object with proper tzinfo. """ return datetime.utcnow().replace(tzinfo=pytz.utc) def tosystemtz(d): """ Convert the UTC datetime ``d`` to the system time zone defined in the settings """ if d is None: return 'None' return d.astimezone(pytz.timezone(current_app.config['SYSTEM_TIME_ZONE'])) def filesize_fmt(num): fmt = lambda num, unit: "%s %s" % (format(num, '.2f').rstrip('0').rstrip('.'), unit) for x in ['bytes', 'KiB', 'MiB', 'GiB']: if num < 1024.0: return fmt(num, x) num /= 1024.0 return fmt(num, 'TiB') def stream_with_ctx_and_exc(generator_or_function): """ taken from flask's code, added exception logging """ try: gen = iter(generator_or_function) except TypeError: def decorator(*args, **kwargs): gen = generator_or_function() return stream_with_context(gen) return update_wrapper(decorator, generator_or_function) def generator(): ctx = _request_ctx_stack.top if ctx is None: raise RuntimeError('Attempted to stream with context but ' 'there was no context in the first place to keep around.') with ctx: # Dummy sentinel. Has to be inside the context block or we're # not actually keeping the context around. yield None # The try/finally is here so that if someone passes a WSGI level # iterator in we're still running the cleanup logic. Generators # don't need that because they are closed on their destruction # automatically. try: for item in gen: yield item except Exception as e: exc_type, exc_value, tb = sys.exc_info() current_app.log_exception((exc_type, exc_value, tb)) finally: if hasattr(gen, 'close'): gen.close() # The trick is to start the generator. Then the code execution runs until # the first dummy None is yielded at which point the context was already # pushed. This item is discarded. Then when the iteration continues the # real generator is executed. wrapped_g = generator() next(wrapped_g) return wrapped_g def make_ispjson_url(split_url): """ Take a tuple as returned by urlsplit and return the isp.json url for that domain >>> from urlparse import urlsplit >>> make_ispjson_url(urlsplit('http://isp.com')) 'http://isp.com/isp.json' """ u = list(split_url) u[2] = '/isp.json' # new path return urlunsplit(u)