View
@@ -29,7 +29,7 @@ module.exports.createResourceFromSource = function(urlOrFile) {
var resource = {
name: resourceName,
title: utils.convertToTitle(resourceName),
title: resourceName,
source: source,
data: {
headers: data.headers,
@@ -42,17 +42,14 @@ module.exports.createResourceFromSource = function(urlOrFile) {
lineTerminator: data.dialect.linebreak
},
fields: _.map(data.schema.fields, function(field, index) {
field = _.clone(field);
field.concept = (field.concept || '') + '';
field.inferredType = field.type;
field.title = utils.convertToTitle(field.name);
field.allowedTypes = utils.availableDataTypes;
field.allowedConcepts = utils.getAllowedConcepts(
field.allowedTypes);
return field;
var _field = {};
_field.type = '';
_field.name = field.name;
_field.title = field.name;
_field.data = _.map(_.first(data.rows, 3), function(row) { return row[index]; })
return _field;
})
};
resolve(resource);
return data;
})
@@ -61,55 +58,7 @@ module.exports.createResourceFromSource = function(urlOrFile) {
};
module.exports.getFiscalDataPackageSchema = function(useProxy) {
return module.exports.getDataPackageSchema('fiscal', useProxy);
};
module.exports.getDataPackageSchema = function(schemaId, useProxy) {
return new Promise(function(resolve, reject) {
var options = {
backend: dataPackageRegistryUrl
};
if (_.isUndefined(useProxy) || !!useProxy) {
options = {
backend: 'proxy?url=' + encodeURIComponent(dataPackageRegistryUrl)
};
}
registry.get(options).then(function(result) {
var profile = _.findWhere(result, {id: schemaId});
if (!profile) {
reject('No profile found with id ' + schemaId);
return null;
}
var options = {
method: 'GET'
};
var url = profile.schema;
if (_.isUndefined(useProxy) || !!useProxy) {
url = 'proxy?url=' + encodeURIComponent(profile.schema);
}
fetch(url, options)
.then(function(res) {
if (res.status != 200) {
reject('Failed loading schema from ' + profile.schema);
}
return res.text();
})
.then(function(data) {
try {
var schema = JSON.parse(data);
resolve(schema);
} catch (e) {
reject('Failed parsing schema json from ' + profile.schema);
}
})
.catch(reject);
}, function() {
reject('Registry request failed');
});
});
return 'fiscal';
};
module.exports.validateDataPackage = function(dataPackage, schema) {
@@ -119,13 +68,21 @@ module.exports.validateDataPackage = function(dataPackage, schema) {
};
module.exports.createFiscalDataPackage = function(attributes, resources) {
var result = {};
// Use OSTypes to generate FDP
var fields = resources[0].fields; //TODO: Add support for more than one resource once OSTypes supports it
_.forEach(fields, function(field) {
delete field.errors;
delete field.additionalOptions;
delete field.slug;
});
var fdp = new OSTypes().fieldsToModel(fields);
// Package metadata
_.extend(result, utils.removeEmptyAttributes(attributes));
_.extend(fdp, utils.removeEmptyAttributes(attributes));
// Resources
result.resources = _.map(resources, function(resource) {
fdp.resources = _.map(resources, function(resource) {
var result = {};
result.name = resource.name;
result.format = 'csv';
@@ -143,108 +100,16 @@ module.exports.createFiscalDataPackage = function(attributes, resources) {
if (resource.dialect) {
result.dialect = _.clone(resource.dialect);
}
result.schema = {
fields: _.map(resource.fields, function(field) {
field = _.clone(field);
delete field.concept;
delete field.inferredType;
delete field.allowedTypes;
delete field.allowedConcepts;
delete field.options;
delete field.additionalOptions;
return field;
})
};
return result;
});
// Model
result.model = {
measures: {},
dimensions: {}
};
var groups = {};
_.each(resources, function(resource) {
_.each(resource.fields, function(field) {
if (field.concept) {
var key = [field.concept];
if (_.isObject(field.options) && field.options.classificationType) {
key.push(field.options.classificationType);
result.schema = fdp.schema;
result.schema.fields = _.map(
_.values(result.schema.fields),
function(field) {
return _.omit(field, 'options');
}
key = JSON.stringify(key);
groups[key] = groups[key] || [];
groups[key].push(_.extend({
resource: resource.name
}, field));
}
});
});
var createMappingFromField = function(field) {
var options = _.isObject(field.options) ? field.options : {};
options = _.clone(options);
options.classificationType = '';
return _.extend(utils.removeEmptyAttributes(options), {
source: field.name,
resource: field.resource
});
};
var allConcepts = function() {
return _.keys(result.model.dimensions)
.concat(_.keys(result.model.measures));
};
var mappingName = null;
_.each(groups, function(fields, concept) {
var optionalAttributes = {};
concept = JSON.parse(concept);
if (concept.length > 1) {
optionalAttributes.classificationType = concept[1];
}
concept = _.first(concept);
concept = _.find(utils.availableConcepts, function(item) {
return item.id == concept;
});
if (!concept || !concept.dimensionType) {
return;
}
var conceptName = concept.dimensionType + ' ' +
(optionalAttributes.classificationType || '');
switch (concept.group) {
case 'measure': {
_.each(fields, function(field) {
mappingName = utils.createUniqueName(
utils.convertToSlug(field.title || field.name),
allConcepts());
result.model.measures[mappingName] = createMappingFromField(field);
});
break;
}
case 'dimension': {
mappingName = utils.createUniqueName(
utils.convertToSlug(conceptName), allConcepts());
var attributes = [];
_.each(fields, function(field) {
attributes.push([
utils.createUniqueName(
utils.convertToSlug(field.title || field.name),
_.map(attributes, _.first)
),
createMappingFromField(field)
]);
});
result.model.dimensions[mappingName] = _.extend(optionalAttributes, {
dimensionType: concept.dimensionType,
primaryKey: _.map(attributes, _.first),
attributes: _.object(attributes)
});
break;
}
}
);
delete fdp.schema;
return result;
});
return result;
return fdp;
};
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,5 @@
<div>
<label class="control-label">DataType</label>
<textarea type="text" class="form-control typeahead" rows="2" readonly/>
<a href="#" class="clear" ng-show="ctrl.sugg != ''"><span class="glyphicon glyphicon-remove-circle"></span>&nbsp;Clear {{ctrl.sugg}}</a>
</div>
View
@@ -13,27 +13,31 @@
<div class="panel-heading">
<a ng-click="fieldState.editDescription = !fieldState.editDescription"
href="javascript:void(0)" class="pull-right" title="Add/Edit Description"><i class="fa fa-sticky-note-o"></i></a>
<h3 class="panel-title">{{ resource.data.headers[fieldState.index] }}</h3>
<h3 class="panel-title">{{ field.name }}</h3>
</div>
<div class="panel-body">
<div class="x-data-sample">
<div ng-repeat="row in resource.data.rows | limitTo:3 track by $index">{{ row[fieldState.index] }}</div>
<div ng-repeat="datum in field.data track by $index">{{ datum }}</div>
</div>
<div class="x-field-info">
<div>
<label class="control-label">Title</label>
<input ng-model="field.title" type="text" class="form-control">
</div>
<os-datatype data-field="field" data-on-changed="onConceptChanged(field)"></os-datatype>
<p ng-repeat="error in field.errors" class="error">
<i class="glyphicon glyphicon-warning-sign"></i>&nbsp;{{ error }}
</p>
<!--<div>-->
<!--<label class="control-label">Data type</label>-->
<!--<select ng-model="field.type" class="form-control"-->
<!--ng-options="item.id as item.name for item in (field | fieldTypes)"></select>-->
<!--</div>-->
<div>
<label class="control-label">Data type</label>
<select ng-model="field.type" class="form-control"
ng-options="item.id as item.name for item in (field | fieldTypes)"></select>
</div>
<div>
<label class="control-label">Concept</label>
<select ng-model="field.concept" class="form-control x-field-info-concept"
ng-change="onConceptChanged(field)"
ng-options="item.id as item.name for item in field.allowedConcepts"></select>
<!--<label class="control-label">Concept</label>-->
<!--<select ng-model="field.concept" class="form-control x-field-info-concept"-->
<!--ng-change="onConceptChanged(field)"-->
<!--ng-options="item.id as item.name for item in field.allowedConcepts"></select>-->
<div ng-repeat="option in field.additionalOptions" class="margin-top-8">
<div>
@@ -44,7 +48,7 @@ <h3 class="panel-title">{{ resource.data.headers[fieldState.index] }}</h3>
ng-options="item.value as item.name for item in (option.values | orderBy:'name')"></select>
</div>
<div ng-if="!option.values">
<input type="text" ng-model="field.options[option.name]" class="form-control">
<input type="{{option.type || text}}" ng-model="field.options[option.name]" class="form-control">
</div>
</div>
</div>
View
@@ -4,9 +4,15 @@
<div ng-init="forms.metadata = metadata"></div>
<div class="col-xs-12 col-md-6">
<div class="form-group" ng-class="{'has-error': metadata.title.$dirty && !metadata.title.$valid}">
<label class="control-label required" for="step3-package-name">Name your Data Package</label>
<label class="control-label required" for="step3-package-title">Provide a Human readable name for your Data Package</label>
<input ng-model="attributes.title" required="required" name="title"
type="text" class="form-control" id="step3-package-name">
type="text" class="form-control" id="step3-package-title">
</div>
<div class="form-group" ng-class="{'has-error': metadata.name.$dirty && !metadata.name.$valid}">
<label class="control-label required" for="step3-package-name">And a short English name for your Data Package</label>
<input ng-model="attributes.name" required="required" name="name"
type="text" class="form-control" id="step3-package-name">
</div>
<div class="form-group">
View
@@ -13,6 +13,7 @@ var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var resolve = require('resolve');
var less = require('gulp-less');
var watch = require('gulp-watch');
var _ = require('underscore');
var frontSrcDir = path.join(__dirname, '/app/front');
@@ -26,6 +27,9 @@ var publicStylesDir = path.join(publicDir, '/styles');
var publicFontsDir = path.join(publicDir, '/fonts');
var publicAssetsDir = path.join(publicDir, '/assets');
var viewsDir = path.join(__dirname, '/app/views');
var servicesDir = path.join(__dirname, '/app/services');
var nodeModulesDir = path.join(__dirname, '/node_modules');
var modules = [
@@ -37,7 +41,9 @@ var modules = [
'treo',
'treo/plugins/treo-promise',
'treo/plugins/treo-websql',
'isomorphic-fetch/fetch-npm-browserify'
'isomorphic-fetch/fetch-npm-browserify',
'os-types',
'lodash'
];
var appModules = {
@@ -55,6 +61,18 @@ gulp.task('default', [
'app.favicon'
]);
gulp.task('watch', ['default'], function () {
var files = [
path.join(frontScriptsDir, '/**/*.js'),
path.join(servicesDir, '/**/*.js'),
path.join(frontStylesDir, '/**/*.less'),
path.join(viewsDir, '/**/*.html')
];
watch(files, {usePolling: true}, function (events) {
gulp.start('default');
});
});
gulp.task('app.scripts', function() {
var files = [
path.join(frontScriptsDir, '/application.js'),
@@ -106,7 +124,8 @@ gulp.task('vendor.scripts', function() {
path.join(nodeModulesDir, '/os-bootstrap/dist/js/bootstrap.min.js'),
path.join(nodeModulesDir, '/angular/angular.min.js'),
path.join(nodeModulesDir, '/angular-animate/angular-animate.min.js'),
path.join(nodeModulesDir, '/angular-route/angular-route.min.js')
path.join(nodeModulesDir, '/angular-route/angular-route.min.js'),
path.join(nodeModulesDir, '/typeahead.js/dist/typeahead.jquery.js')
];
return gulp.src(files)
.pipe(concat('vendor.js'))
@@ -118,7 +137,8 @@ gulp.task('vendor.styles', function() {
path.join(nodeModulesDir, '/font-awesome/css/font-awesome.min.css'),
path.join(nodeModulesDir, '/os-bootstrap/dist/css/bootstrap.min.css'),
path.join(nodeModulesDir, '/angular/angular-csp.css'),
path.join(nodeModulesDir, '/c3/c3.min.css')
path.join(nodeModulesDir, '/c3/c3.min.css'),
path.join(nodeModulesDir, '/typeahead.js-bootstrap-css/typeaheadjs.css')
];
return gulp.src(files)
.pipe(concat('vendor.css'))
View
@@ -50,12 +50,16 @@
"js-md5": "git+https://github.com/akariv/js-md5.git",
"js-polyfills": "^0.1.12",
"json-table-schema": "git+https://github.com/okfn/jsontableschema-js.git",
"lodash": "^4.11.1",
"marked": "^0.3.5",
"nconf": "^0.8.2",
"nunjucks": "^2.1.0",
"os-types": "^1.1.0",
"papaparse": "^4.1.2",
"request": "^2.65.0",
"treo": "^0.5.1",
"typeahead.js": "^0.11.1",
"typeahead.js-bootstrap-css": "git+https://github.com/bassjobsen/typeahead.js-bootstrap-css",
"underscore": "^1.8.3",
"validator": "^4.2.1"
},
@@ -73,6 +77,7 @@
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.4.2",
"gulp-watch": "^4.3.5",
"jquery": "^2.1.4",
"jscs": "^2.5.0",
"mocha": "^2.3.3",