import logging from flask import Blueprint, request from flask.ext.login import current_user from flask.ext.babel import gettext as _ from colander import SchemaNode, String, Invalid from sqlalchemy.orm import aliased from apikit import jsonify, Pager, request_data from fiscalmodel import COUNTRIES, LANGUAGES from spendb.core import db from spendb.model import Dataset, DatasetLanguage, DatasetTerritory, Account from spendb.auth import require from spendb.lib.helpers import get_dataset from spendb.views.context import etag_cache_keygen from spendb.validation.dataset import validate_dataset, validate_managers from spendb.validation.model import validate_model log = logging.getLogger(__name__) blueprint = Blueprint('datasets_api', __name__) def query_index(): q = Dataset.all_by_account(current_user, order=False) q = q.order_by(Dataset.updated_at.desc()) # Filter by languages if they have been provided for language in request.args.getlist('languages'): l = aliased(DatasetLanguage) q = q.join(l, Dataset._languages) q = q.filter(l.code == language) # Filter by territories if they have been provided for territory in request.args.getlist('territories'): t = aliased(DatasetTerritory) q = q.join(t, Dataset._territories) q = q.filter(t.code == territory) # Filter by account if one has been provided for account in request.args.getlist('account'): a = aliased(Account) q = q.join(a, Dataset.managers) q = q.filter(a.name == account) # Return a list of languages as dicts with code, count, url and label languages = [{'code': code, 'count': count, 'label': LANGUAGES.get(code)} for (code, count) in DatasetLanguage.dataset_counts(q)] territories = [{'code': code, 'count': count, 'label': COUNTRIES.get(code)} for (code, count) in DatasetTerritory.dataset_counts(q)] pager = Pager(q, limit=15) return pager, languages, territories @blueprint.route('/datasets') def index(): pager, languages, territories = query_index() data = pager.to_dict() data['languages'] = languages data['territories'] = territories return jsonify(data) @blueprint.route('/datasets/<name>') def view(name): dataset = get_dataset(name) etag_cache_keygen(dataset, private=dataset.private) return jsonify(dataset) @blueprint.route('/datasets', methods=['POST', 'PUT']) def create(): require.dataset.create() dataset = request_data() data = validate_dataset(dataset) if Dataset.by_name(data['name']) is not None: raise Invalid(SchemaNode(String(), name='name'), _("A dataset with this identifer already exists!")) dataset = Dataset({'dataset': data, 'model': {}}) dataset.managers.append(current_user) db.session.add(dataset) db.session.commit() return view(dataset.name) @blueprint.route('/datasets/<name>', methods=['POST', 'PUT']) def update(name): dataset = get_dataset(name) require.dataset.update(dataset) dataset.update(validate_dataset(request_data())) dataset.touch() db.session.commit() return view(name) @blueprint.route('/datasets/<name>/structure') def structure(name): dataset = get_dataset(name) etag_cache_keygen(dataset, private=dataset.private) return jsonify({ 'fields': dataset.fields }) @blueprint.route('/datasets/<name>/model') def model(name): dataset = get_dataset(name) etag_cache_keygen(dataset, private=dataset.private) return jsonify(dataset.model or {}) @blueprint.route('/datasets/<name>/model', methods=['POST', 'PUT']) def update_model(name): dataset = get_dataset(name) require.dataset.update(dataset) data = request_data() if isinstance(data, dict): data['fact_table'] = dataset.fact_table.table_name dataset.model = validate_model(data) db.session.commit() return model(name) @blueprint.route('/datasets/<name>/managers') def managers(name): dataset = get_dataset(name) etag_cache_keygen(dataset, private=dataset.private) return jsonify({'managers': dataset.managers}) @blueprint.route('/datasets/<name>/managers', methods=['POST', 'PUT']) def update_managers(name): dataset = get_dataset(name) require.dataset.update(dataset) data = validate_managers(request_data()) if current_user not in data['managers']: data['managers'].append(current_user) dataset.managers = data['managers'] dataset.touch() db.session.commit() return managers(name) @blueprint.route('/datasets/<name>', methods=['DELETE']) def delete(name): dataset = get_dataset(name) require.dataset.update(dataset) dataset.fact_table.drop() db.session.delete(dataset) db.session.commit() return jsonify({'status': 'deleted'}, status=410)