def send(subject, recipients, template_base, **kwargs): ''' Send a given email to multiple recipients. User prefered language is taken in account. To translate the subject in the right language, you should ugettext_lazy ''' sender = kwargs.pop('sender', None) if not isinstance(recipients, (list, tuple)): recipients = [recipients] debug = current_app.config.get('DEBUG') connection = debug and dummyconnection or mail.connect with connection() as conn: for recipient in recipients: lang = i18n._default_lang(recipient) with i18n.language(lang): log.debug( 'Sending mail "%s" to recipient "%s"', subject, recipient) msg = Message(subject, sender=sender, recipients=[recipient.email]) msg.body = theme.render( 'mail/{0}.txt'.format(template_base), subject=subject, sender=sender, recipient=recipient, **kwargs) msg.html = theme.render( 'mail/{0}.html'.format(template_base), subject=subject, sender=sender, recipient=recipient, **kwargs) if debug: log.debug(msg.body) log.debug(msg.html) else: conn.send(msg)
def get(self): args = oembed_parser.parse_args() url = urlparse.urlparse(args['url']) try: API, VERSION, item_kind, item_id = url.path.split('/')[1:-1] except (ValueError, IndexError): return api.abort(400, 'Invalid URL.') if item_kind == 'datasets': try: item = Dataset.objects.get(id=item_id) except (db.ValidationError, Dataset.DoesNotExist): return api.abort(400, 'Incorrect ID.') template = 'embed-dataset.html' else: return api.abort(400, 'Invalid object type.') width = maxwidth = 1000 height = maxheight = 200 html = theme.render(template, **{ 'width': width, 'height': height, 'item': item, }) return output_json({ 'type': 'rich', 'version': '1.0', 'html': html, 'width': width, 'height': height, 'maxwidth': maxwidth, 'maxheight': maxheight, }, 200)
def openfield16(): datasets = (Dataset.objects(badges__kind=OPENFIELD16).visible() .order_by('-metrics.followers')) return theme.render('openfield16.html', datasets=datasets, badge=OPENFIELD16, nb_displayed_datasets=NB_DISPLAYED_DATASETS)
def nec_mergitur(): datasets = (Dataset.objects(badges__kind=NECMERGITUR).visible() .order_by('-metrics.followers')) return theme.render('nec_mergitur.html', datasets=datasets, badge=NECMERGITUR, nb_displayed_datasets=NB_DISPLAYED_DATASETS)
def render_search(): params = multi_to_dict(request.args) params['facets'] = True # We only fetch relevant data for the given filter. if 'tag' in params: search_queries = [ search.SearchQuery(Dataset, **params), search.SearchQuery(Reuse, **params) ] results_labels = ['datasets', 'reuses'] elif 'badge' in params: search_queries = [ search.SearchQuery(Dataset, **params), search.SearchQuery(Organization, **params) ] results_labels = ['datasets', 'organizations'] else: search_queries = [ search.SearchQuery(Dataset, **params), search.SearchQuery(Reuse, **params), search.SearchQuery(Organization, **params), search.SearchQuery(User, **params) ] results_labels = ['datasets', 'reuses', 'organizations', 'users'] results = search.multiquery(*search_queries) return theme.render('search.html', **dict(zip(results_labels, results)))
def get(self): """ The returned payload is a list of OEmbed formatted responses. See: http://oembed.com/ The `references` are composed by a keyword (`kind`) followed by the `id` each of those separated by commas. E.g: dataset-5369992aa3a729239d205183,territory-fr-town-75056-comptes Only datasets and territories are supported for now. """ args = oembeds_parser.parse_args() references = args['references'].split(',') result = [] for item_reference in references: try: item_kind, item_id = item_reference.split('-', 1) except ValueError: return api.abort(400, 'Invalid ID.') if item_kind == 'dataset': try: item = Dataset.objects.get(id=item_id) except (db.ValidationError, Dataset.DoesNotExist): return api.abort(400, 'Unknown dataset ID.') elif (item_kind == 'territory' and current_app.config.get('ACTIVATE_TERRITORIES')): from udata.models import TERRITORY_DATASETS try: country, town, code, kind = item_id.split('-') except ValueError: return api.abort(400, 'Invalid territory ID.') try: geozone = GeoZone.objects.get(code=code) except GeoZone.DoesNotExist: return api.abort(400, 'Unknown territory identifier.') if kind in TERRITORY_DATASETS: item = TERRITORY_DATASETS[kind](geozone) else: return api.abort(400, 'Unknown kind of territory.') else: return api.abort(400, 'Invalid object type.') width = maxwidth = 1000 height = maxheight = 200 html = theme.render('embed-dataset.html', **{ 'width': width, 'height': height, 'item': item, 'item_reference': item_reference, }) result.append({ 'type': 'rich', 'version': '1.0', 'html': html, 'width': width, 'height': height, 'maxwidth': maxwidth, 'maxheight': maxheight, }) return output_json(result, 200)
def show(post): others = Post.objects(id__ne=post.id).published() older = others(published__lt=post.published) newer = others(published__gt=post.published) return theme.render('post/display.html', post=post, previous_post=older.first(), next_post=newer.first())
def reuses(topic): kwargs = multi_to_dict(request.args) kwargs.update(topic=topic) return theme.render('topic/reuses.html', topic=topic, reuses=TopicSearchQuery(Reuse, facets=True, **kwargs).execute() )
def datasets(topic): kwargs = multi_to_dict(request.args) kwargs.update(topic=topic) return theme.render('topic/datasets.html', topic=topic, datasets=TopicSearchQuery(Dataset, facets=True, **kwargs).execute() )
def home(): context = { 'recent_datasets': Dataset.objects.visible(), 'recent_reuses': Reuse.objects(featured=True).visible(), 'last_post': Post.objects(private=False).first(), } processor = theme.current.get_processor('home') context = processor(context) return theme.render('home.html', **context)
def climate_change_challenge(): partners = Organization.objects(slug__in=C3_PARTNERS) datasets = (Dataset.objects(badges__kind=C3).visible() .order_by('-metrics.followers')) return theme.render('c3.html', partners=partners, datasets=datasets, badge=C3, nb_displayed_datasets=NB_DISPLAYED_DATASETS)
def dataconnexions5(): reuses = Reuse.objects(badges__kind=DATACONNEXIONS_5_CANDIDATE).visible() categories = [{ 'tag': tag, 'label': label, 'description': description, 'reuses': reuses(tags=tag), } for tag, label, description in DATACONNEXIONS_5_CATEGORIES] return theme.render('dataconnexions-5.html', categories=categories)
def test_oembed_for_dataset_with_organization(self, api): '''It should fetch a dataset in the oembed format with org.''' organization = OrganizationFactory() dataset = DatasetFactory(organization=organization) url = url_for('api.oembed', url=dataset.external_url) response = api.get(url) assert200(response) card = theme.render('dataset/card.html', dataset=dataset) assert card in response.json['html']
def authorize(*args, **kwargs): if request.method == 'GET': client_id = kwargs.get('client_id') client = OAuth2Client.objects.get(id=ObjectId(client_id)) kwargs['client'] = client return theme.render('api/oauth_authorize.html', oauth=kwargs) elif request.method == 'POST': accept = 'accept' in request.form decline = 'decline' in request.form return accept and not decline else: abort(405)
def dataconnexions6(): # Use tags until we are sure all reuse are correctly labeled # reuses = Reuse.objects(badges__kind=DATACONNEXIONS_6_CANDIDATE) reuses = Reuse.objects(tags='dataconnexions-6').visible() categories = [{ 'tag': tag, 'label': label, 'description': description, 'reuses': reuses(tags=tag), } for tag, label, description in DATACONNEXIONS_6_CATEGORIES] return theme.render('dataconnexions-6.html', categories=categories)
def display(topic): specs = { 'recent_datasets': TopicSearchQuery(Dataset, sort='-created', page_size=9, topic=topic), 'featured_reuses': TopicSearchQuery(Reuse, featured=True, page_size=6, topic=topic), } keys, queries = zip(*specs.items()) results = search.multiquery(*queries) return theme.render('topic/display.html', topic=topic, **dict(zip(keys, results)) )
def reuses(topic): kwargs = multi_to_dict(request.args) kwargs.pop('topic', None) topic_search = topic_search_for(topic, ReuseSearch, facets=True, **kwargs) return theme.render( 'topic/reuses.html', topic=topic, reuses=search.query(topic_search) )
def datasets(topic): kwargs = multi_to_dict(request.args) kwargs.pop('topic', None) topic_search = topic_search_for(topic, DatasetSearch, facets=True, **kwargs) return theme.render( 'topic/datasets.html', topic=topic, datasets=search.query(topic_search) )
def get(self): """ An OEmbed compliant API endpoint See: http://oembed.com/ Support datasets and reuses URLs """ args = oembed_parser.parse_args() if args['format'] != 'json': api.abort(501, 'Only JSON format is supported') url = args['url'] # Fix flask not detecting URL with https://domain:443/ if 'https:' in url and ':443/' in url: url = url.replace(':443/', '/') with current_app.test_request_context(url) as ctx: if not ctx.request.endpoint: api.abort(404, 'Unknown URL') endpoint = ctx.request.endpoint.replace('_redirect', '') view_args = ctx.request.view_args if endpoint not in self.ROUTES: api.abort(404, 'Unknown URL') param = self.ROUTES[endpoint] item = view_args[param] if isinstance(item, Exception): raise item width = maxwidth = 1000 height = maxheight = 200 params = { 'width': width, 'height': height, 'item': item, 'type': param } params[param] = item html = theme.render('oembed.html', **params) return { 'type': 'rich', 'version': '1.0', 'html': html, 'width': width, 'height': height, 'maxwidth': maxwidth, 'maxheight': maxheight, }
def home(): context = { 'recent_datasets': Dataset.objects.visible(), 'recent_reuses': Reuse.objects(featured=True).visible(), 'last_post': Post.objects.published().first(), 'rdf_links': [ (RDF_MIME_TYPES[fmt], url_for('site.rdf_catalog_format', format=ext)) for (fmt, ext) in RDF_EXTENSIONS.items() ] } processor = theme.current.get_processor('home') context = processor(context) return theme.render('home.html', **context)
def display(topic): specs = { 'recent_datasets': topic_search_for(topic, DatasetSearch, sort='-created', page_size=9), 'featured_reuses': topic_search_for(topic, ReuseSearch, featured=True, page_size=6), } keys, queries = zip(*specs.items()) results = search.multisearch(*queries) return theme.render( 'topic/display.html', topic=topic, datasets=[d for d in topic.datasets if hasattr(d, 'pk')], **dict(zip(keys, results)) )
def swaggerui(): page_size = 10 params = {"datasets": "many"} organizations = search.iter(Organization) organizations = list(itertools.islice(organizations, page_size)) if len(organizations) < page_size: # Fill with dummy values needs = page_size - len(organizations) extra_orgs = OrganizationFactory.build_batch(needs) for org in extra_orgs: org.id = ObjectId() Organization.slug.generate() organizations.extend(extra_orgs) return theme.render('apidoc.html', specs_url=api.specs_url, organizations=organizations)
def render_search(): params = multi_to_dict(request.args) params['facets'] = True datasets, organizations, reuses, users = search.multiquery( search.SearchQuery(Dataset, **params), search.SearchQuery(Organization, **params), search.SearchQuery(Reuse, **params), search.SearchQuery(User, **params), ) return theme.render('search.html', datasets=datasets, organizations=organizations, reuses=reuses, users=users )
def test_oembed_for_dataset(self, api): '''It should fetch a dataset in the oembed format.''' dataset = DatasetFactory() url = url_for('api.oembed', url=dataset.external_url) response = api.get(url) assert200(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('dataset/card.html', dataset=dataset) assert card in response.json['html']
def test_oembed_for_reuse(self, api): '''It should fetch a reuse in the oembed format.''' reuse = ReuseFactory() url = url_for('api.oembed', url=reuse.external_url) response = api.get(url) assert200(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('reuse/card.html', reuse=reuse) assert card in response.json['html']
def test_oembed_for_dataset(self, api): '''It should fetch a dataset in the oembed format.''' dataset = DatasetFactory() url = url_for('api.oembed', url=dataset.external_url) response = api.get(url) assert200(response) assert_cors(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('dataset/card.html', dataset=dataset) assert card in response.json['html']
def test_oembed_for_reuse(self, api): '''It should fetch a reuse in the oembed format.''' reuse = ReuseFactory() url = url_for('api.oembed', url=reuse.external_url) response = api.get(url) assert200(response) assert_cors(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('reuse/card.html', reuse=reuse) assert card in response.json['html']
def test_oembed_for_org(self, api): '''It should fetch an organization in the oembed format.''' org = OrganizationFactory() url = url_for('api.oembed', url=org.external_url) response = api.get(url) assert200(response) assert_cors(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('organization/card.html', organization=org) assert card in response.json['html']
def authorize(*args, **kwargs): if request.method == 'GET': grant = oauth.validate_consent_request(end_user=current_user) # Bypass authorization screen for internal clients if grant.client.internal: return oauth.create_authorization_response(grant_user=current_user) return theme.render('api/oauth_authorize.html', grant=grant) elif request.method == 'POST': accept = 'accept' in request.form decline = 'decline' in request.form if accept and not decline: grant_user = current_user else: grant_user = None return oauth.create_authorization_response(grant_user=grant_user) else: abort(405)
def test_oembed_for_dataset_redirect_link(self, api): '''It should fetch an oembed dataset using the redirect link.''' dataset = DatasetFactory() redirect_url = url_for('datasets.show_redirect', dataset=dataset, _external=True) url = url_for('api.oembed', url=redirect_url) response = api.get(url) assert200(response) assert 'html' in response.json assert 'width' in response.json assert 'maxwidth' in response.json assert 'height' in response.json assert 'maxheight' in response.json assert response.json['type'] == 'rich' assert response.json['version'] == '1.0' card = theme.render('dataset/card.html', dataset=dataset) assert card in response.json['html']
def authorize(*args, **kwargs): if request.method == 'GET': try: grant = oauth.validate_consent_request(end_user=current_user) except OAuth2Error as error: return error.error # Bypass authorization screen for internal clients if grant.client.internal: return oauth.create_authorization_response(grant_user=current_user) return theme.render('api/oauth_authorize.html', grant=grant) elif request.method == 'POST': accept = 'accept' in request.form decline = 'decline' in request.form if accept and not decline: grant_user = current_user else: grant_user = None return oauth.create_authorization_response(grant_user=grant_user)
def render_territory(territory): if not current_app.config.get('ACTIVATE_TERRITORIES'): return abort(404) from udata.models import TERRITORY_DATASETS territory_dataset_classes = sorted( TERRITORY_DATASETS.values(), key=lambda a: a.order) territory_datasets = [ territory_dataset_class(territory) for territory_dataset_class in territory_dataset_classes ] datasets = list(Dataset.objects.visible().filter(spatial__zones=territory)) context = { 'territory': territory, 'territory_datasets': territory_datasets, 'datasets': datasets, } return theme.render('territories/territory.html', **context)
def render_home(): if not current_app.config.get('ACTIVATE_TERRITORIES'): return abort(404) highest_level = current_app.config['HANDLED_LEVELS'][-1] regions = GeoZone.objects(level=highest_level).valid_at(date.today()) regions = sorted(regions, key=lambda zone: unicodedata.normalize('NFD', zone.name). encode('ascii', 'ignore')) return theme.render( 'territories/home.html', **{ 'geojson': { 'type': 'FeatureCollection', 'features': [region.toGeoJSON() for region in regions] }, 'regions': regions })
def render_search(): # We only fetch relevant data for the given filter. # To do so, we parse query for each type # and we only keep types supporting all parameters adapters = {t: search.adapter_for(m) for t, m in MAPPING.items()} type_args = { t: not_none_dict(a.as_request_parser().parse_args()) for t, a in adapters.items() } all_args = set.union(*[set(args.keys()) for args in type_args.values()]) types = [ typ for typ, args in type_args.items() if set(args.keys()) == all_args ] params = type_args[types[0]] params['facets'] = True models = [MAPPING[typ] for typ in types] results = search.multisearch(*models, **params) context = dict(zip(types, results)) territories = check_for_territories(params.get('q')) context['territories'] = territories return theme.render('search.html', **context)
def render_territory(territory): if not current_app.config.get('ACTIVATE_TERRITORIES'): return abort(404) territory_dataset_classes = sorted(TERRITORY_DATASETS.values(), key=lambda a: a.order) territory_datasets = [ territory_dataset_class(territory) for territory_dataset_class in territory_dataset_classes ] # Retrieve all datasets then split between those optionaly owned # by an org for that zone and others. We need to know if the current # user has datasets for that zone in order to display a custom # message to ease the conversion. datasets = Dataset.objects.visible().filter(spatial__zones=territory) town_datasets = [] other_datasets = [] editable_datasets = [] if datasets: for dataset in datasets: if (dataset.organization and territory.id == dataset.organization.zone): town_datasets.append(dataset) else: other_datasets.append(dataset) editable_datasets.append(current_user.is_authenticated and DatasetEditPermission(dataset).can()) context = { 'territory': territory, 'territory_datasets': territory_datasets, 'other_datasets': other_datasets, 'has_pertinent_datasets': any(editable_datasets), 'town_datasets': town_datasets } return theme.render('territories/territory.html', **context)
def render_search(): # We only fetch relevant data for the given filter. # To do so, we parse query for each type # and we only keep types supporting all parameters adapters = {t: search.adapter_for(m) for t, m in MAPPING.items()} type_args = { t: not_none_dict(a.as_request_parser().parse_args()) for t, a in adapters.items() } all_args = set.union(*[ set(args.keys()) for args in type_args.values() ]) types = [ typ for typ, args in type_args.items() if set(args.keys()) == all_args ] params = type_args[types[0]] params['facets'] = True models = [MAPPING[typ] for typ in types] results = search.multisearch(*models, **params) context = dict(zip(types, results)) territories = check_for_territories(params.get('q')) context['territories'] = territories return theme.render('search.html', **context)
def suivi(): try: return theme.render('suivi.html') except TemplateNotFound: abort(404)
def oauth_error(): return theme.render('api/oauth_error.html')
def home(): context = {} processor = theme.current.get_processor('home', default_home_context_processor) return theme.render('home.html', **processor(context))
def validation_error(error): return theme.render('errors/400.html', error=error), 400
def get(self): """ The returned payload is a list of OEmbed formatted responses. See: http://oembed.com/ The `references` are composed by a keyword (`kind`) followed by the `id` each of those separated by commas. E.g: dataset-5369992aa3a729239d205183, territory-fr:departement:33@1860-07-01:emploi_dep Only datasets and territories are supported for now. """ args = oembeds_parser.parse_args() references = args['references'].split(',') result = [] for item_reference in references: try: item_kind, item_id = item_reference.split('-', 1) except ValueError: return api.abort(400, 'Invalid ID.') if item_kind == 'dataset': try: item = Dataset.objects.get(id=item_id) except (db.ValidationError, Dataset.DoesNotExist): return api.abort(400, 'Unknown dataset ID.') elif (item_kind == 'territory' and current_app.config.get('ACTIVATE_TERRITORIES')): try: country, level, code, kind = item_id.split(':') except ValueError: return api.abort(400, 'Invalid territory ID.') geoid = ':'.join((country, level, code)) zone = GeoZone.objects.resolve(geoid) if not zone: return api.abort(400, 'Unknown territory identifier.') if level in TERRITORY_DATASETS: if kind in TERRITORY_DATASETS[level]: item = TERRITORY_DATASETS[level][kind](zone) else: return api.abort(400, 'Unknown territory dataset id.') else: return api.abort(400, 'Unknown kind of territory.') else: return api.abort(400, 'Invalid object type.') width = maxwidth = 1000 height = maxheight = 200 html = theme.render( 'embed-dataset.html', **{ 'width': width, 'height': height, 'item': item, 'item_reference': item_reference, }) result.append({ 'type': 'rich', 'version': '1.0', 'html': html, 'width': width, 'height': height, 'maxwidth': maxwidth, 'maxheight': maxheight, }) return result
def render(self, context=None, **kwargs): context = context or self.get_context() context.update(kwargs) return theme.render(self.get_template_name(), **context)
def faq(section): return theme.render('faq/{0}.html'.format(section), page_name=section)
def terms(): return theme.render('terms.html')
def swaggerui(): return theme.render('apidoc.html', specs_url=api.specs_url)
def dataset_apis(ctx): dataset = ctx['dataset'] return theme.render('dataset-apis.html', apis=dataset.extras.get(APIGOUVFR_EXTRAS_KEY))
def spd(): datasets = Dataset.objects(badges__kind=SPD).order_by('title') return theme.render('spd.html', datasets=datasets, badge=SPD)
def credits(): return theme.render('credits.html')
def map(): return theme.render('site/map.html')
def internal_error(error): return theme.render('errors/500.html', error=error), 500
def apidoc_index(): return theme.render('apidoc.html')
def licences(): try: return theme.render('licences.html') except TemplateNotFound: abort(404)
def faq(section): try: return theme.render('faq/{0}.html'.format(section), page_name=section) except TemplateNotFound: abort(404)
def forbidden(error): return theme.render('errors/403.html', error=error), 403
def redevances(): return theme.render('redevances.html')
def page_not_found(error): return theme.render('errors/404.html', error=error), 404
def terms(): content = get_terms_content() return theme.render('terms.html', terms=content)
def page_deleted(error): return theme.render('errors/410.html', error=error), 410